pythondjangoslugify

How to add index to my slug if it already exists in database?


When i create new post i need to do next:

1. Generate slug from self.title with slugify
2. Check if this slug does not exists we save post with self.slug
3. If this slug already exists we save post with self.slug + '-' + count index

I found worked solution but i'm new in django so i want to ask you is this optimal solution?

#models.py

from django.db import models
from django.shortcuts import reverse
from django.utils.text import slugify
from django.db.models.signals import post_save
from django.dispatch import receiver

class Post(models.Model):
    title = models.CharField(max_length=150, db_index=True)
    slug = models.SlugField(max_length=150, blank=True, unique=True)

    def get_absolute_url(self):
        return reverse('post_detail_url', kwargs={'slug': self.slug})

@receiver(post_save, sender=Post)
def set_slug(sender, instance, *args, **kwargs):
    if not instance.slug:
        instance.slug = slugify(instance.title)
        while Post.objects.filter(slug__startswith=instance.slug).exists():
            instance.slug += '-' + str(Post.objects.filter(slug__startswith=instance.slug).count())
        instance.save()

Solution

  • A pre-save signal is the best way to deal with this. Everytime a instance is about to get save, the signal will be triggered and run some logic. In this case, it will populate the slug field before saving.

    from django.db.models.signals import pre_save
    from django.dispatch import receiver
    
    # you other stuff goes here
    
    @receiver(pre_save, sender=MyModel)
    def set_slug(sender, instance, *args, **kwargs):
        instance.slug = slugify(instance.title)
    

    And that is it!

    If your signals is not connected around apps, you can place it on your models.py. But if you are using it to connect different apps, or a common signal to multiple apps, you can should have a separated file to place.

    Just a note: see that sender=MyModel piece? That is tying the signal to a specific model. If you have a lot of models that would use slugs, you can just remove it to make the pre save hook available to multiple models.