Home > Software design >  Slugs not appearing in URLS
Slugs not appearing in URLS

Time:01-03

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 >
      <div  id="gallerygrid1">
        <div >
          {% for photo in images %}
          <div  id="gallerygrid2">
            <a href="{% url 'viewimage' photo.id %}"
              ><img
                
                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 >
      <div >
        <a href="{% url 'gallery' %}"
          ><img  src="{{photo.image.url}}"
        /></a>
        <h2 >{{photo.image_title}}</h2>
        <p >
          Interested in purchasing this as a print? Contact me for more
          information regarding price and sizes.
        </p>
        <a href="{% url 'contact' %}"  type="button"
          >Contact</a
        >
        <a href="{% url 'gallery' %}"  type="button"
          >Gallery</a
        >
      </div>
    </div>
  </body>
</html>

CodePudding user response:

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})
  • Related