4

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

unlockme
  • 3,897
  • 3
  • 28
  • 42

1 Answers1

5

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
xyres
  • 20,487
  • 3
  • 56
  • 85
  • I think to accept this answer conditionally. The slug has to be unique and therefore collisions have to be handled properly. Otherwise thanks for the pointer – unlockme Mar 18 '18 at 05:22
  • I have tested this, it does not handle take care of uniqueness. I can create and save the same slug for two different model objects which I don't want. – unlockme Mar 18 '18 at 05:53
  • edit the answer for to handle collisions using multiple fields or to indicate the collision e.g. `slug_text = model_object.var1 + .... try: model=Model.objects.get(slug=slug_text) except DoesNotExist: modelobject.slug=slug_text modelobject.save() else look_for_anotjer_slug' – unlockme Mar 18 '18 at 06:01
  • @unlockme I've updated the answer based on your suggestions. Thank you. I've also added another function to generate a new slug if the old one is not unique. – xyres Mar 18 '18 at 07:39