djangodjango-extensions

Populate existing model objects with AutoSlugField


I need to populate an AutoslugField for already existing model objects. My bad realized that the slug field was so handy and better that using pk for security purposes.

I already have model objects (rows) in the database. I want to add the AutoSlugField to them.

Anybody know how I can achieve this.

Thanks


Solution

  • Assuming that the model looks like this:

    class MyModel(...):
        title = <Charfield>
        slug = <AutoSlugField>
    

    You can write a for loop to read all the objects in MyModel and use django.utils.text.slugify to convert title into a slug. You can run this in a shell:

    from django.utils.text import slugify
    
    from myapp.models import MyModel
    
    
    # The for loop to create new slugs and update db records
    
    for obj in MyModel.objects.all():
        if not obj.slug: # only create slug if empty
    
            slug = slugify(obj.title)
    
            cycle = 1 # the current loop cycle
    
            while True:
                # this loop will run until the slug is unique
                try:
                    model = MyModel.objects.get(slug=slug_text)
                except MyModel.DoesNotExist:
                    obj.slug = slug
                    obj.save()
                    break
                else:
                    slug = generate_another_slug(slug, cycle)
    
                cycle += 1 # update cycle number
    

    The generate_another_slug function can look like this:

    def generate_another_slug(slug, cycle):
        """A function that takes a slug and 
        appends a number to the slug
    
        Examle: 
            slug = 'hello-word', cycle = 1
            will return 'hello-word-1'
        """
        if cycle == 1:
            # this means that the loop is running 
            # first time and the slug is "fresh"
            # so append a number in the slug
            new_slug = "%s-%s" % (slug, cycle)
        else:
            # the loop is running more than 1 time
            # so the slug isn't fresh as it already 
            # has a number appended to it
            # so, replace that number with the 
            # current cycle number
            original_slug = "-".join(slug.split("-")[:-1])
            new_slug = "%s-%s" % (original_slug, cycle)
    
        return new_slug