Search code examples
djangoslug

Slugs not appearing in URLS


I want my slugs to show up in my URLS instead of the number ID of the image that is being viewed. I can't seem to get it working. Can anyone spot what I'm missing/doing wrong? TYIA

models.py:

class PostImage(models.Model):
    image = models.ImageField(null=False, blank=False, upload_to="images", default="default.png")
    image_title = models.CharField(max_length=100, null=False, blank=False, default="")
    slug = models.SlugField(null=True)

    def save(self, *args, **kwargs):
        self.slug = self.slug or slugify(self.title)
        super().save(*args, **kwargs)
    
    def __str__(self):
        return self.image_title
    
    class Meta:
            verbose_name_plural = 'PostImage'

views.py:

def galleryPage(request):
    images = PostImage.objects.all()
    context = {'images':images}

    return render(request, 'gallery.html', context)


def viewImage(request, slug):
    photo = PostImage.objects.get(id=slug)
    return render(request, 'viewimage.html', {'photo': photo, 'slug': slug})

urls.py:

path('viewimage/<slug:slug>/', views.viewImage, name='viewimage'),

admin.py:

@admin.register(PostImage)
class PostAdmin(admin.ModelAdmin):
    prepopulated_fields = {'slug': ('image_title',)}

These are the html pages, the first is where the image is clicked, the second is where the image is shown and also where I would want the slug showing in the URL on browser rather than the id/number.

html for page that image is selected:

  </head>
  <body>
    <header>{% include 'navbardesktop.html' %}</header>
    <div class="container-gallery">
      <div class="col-md-12" id="gallerygrid1">
        <div class="row">
          {% for photo in images %}
          <div class="col-md-4" id="gallerygrid2">
            <a href="{% url 'viewimage' photo.id %}"
              ><img
                class="gallery-thumbnail"
                src="{{photo.image.url}}"
                style=""
            /></a>
          </div>
          {% empty %}
          <h3>No Projects...</h3>
          {% endfor %}
        </div>
      </div>
    </div>
  </body>
</html>

html for page where image is shown:

  </head>
  <body>
    <header>{% include 'navbardesktop.html' %}</header>
    <div class="image-container">
      <div class="image-post">
        <a href="{% url 'gallery' %}"
          ><img class="photo-img" src="{{photo.image.url}}"
        /></a>
        <h2 class="photo-title">{{photo.image_title}}</h2>
        <p class="contact">
          Interested in purchasing this as a print? Contact me for more
          information regarding price and sizes.
        </p>
        <a href="{% url 'contact' %}" class="btn btn-secondary" type="button"
          >Contact</a
        >
        <a href="{% url 'gallery' %}" class="btn btn-secondary" type="button"
          >Gallery</a
        >
      </div>
    </div>
  </body>
</html>

Solution

  • Short answer
    The url that is opening the page with the image is <a href="{% url 'viewimage' photo.id %}">, and since you are feeding in photo.id, that's what you're getting. You could change it to <a href="{% url 'viewimage' photo.slug %}"> and that should solve the problem.

    But
    But this will create problems since you are allowing null=True for the slug field, so what happens when you have a PostImage instance with no slug? An error, since your url is expecting a parameter, <slug:slug>:

    path('viewimage/<slug:slug>/', views.viewImage, name='viewimage'),
    

    I don't see why you need to make the slug field null, since you have a save() function that will fill it in automatically, though it does have a typo, I believe, and that is slugify(self.title). What is self.title? Perhaps you meant self.image_title?:

    class PostImage(models.Model):
        image = models.ImageField(null=False, blank=False, upload_to="images", default="default.png")
        image_title = models.CharField(max_length=100, null=False, blank=False, default="")
        slug = models.SlugField()
    
        def save(self, *args, **kwargs):
            self.slug = self.slug or slugify(self.image_title)
            super().save(*args, **kwargs)
        ...
    

    Unfortunately
    Unfortunately, there is still something that will go wrong in your views, since you are trying to get a photo = PostImage.objects.get(slug=slug), but now slug will be an actual slug, not an id. Easy to fix:

    def viewImage(request, slug):
        photo = PostImage.objects.get(slug=slug)
        return render(request, 'viewimage.html', {'photo': photo, 'slug': slug})
    

    And since you should never try to get an object without checking if it can be done first:

    def viewImage(request, slug):
        try:
            photo = PostImage.objects.get(slug=slug)
        except PostImage.DoesNotExist:
            print("PostImage with this slug does not exist")
            photo = None
        return render(request, 'viewimage.html', {'photo': photo, 'slug': slug})