Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is there an easy way to populate SlugField from CharField?

class Foo(models.Model):
    title = models.CharField(max_length=20)
    slug = models.SlugField()

Is there a built-in way to get the slug field to autopopulate based on the title? Perhaps in the Admin and outside of the Admin.

like image 890
ashchristopher Avatar asked Sep 26 '08 19:09

ashchristopher


3 Answers

for Admin in Django 1.0 and up, you'd need to use

prepopulated_fields = {'slug': ('title',), }

in your admin.py

Your key in the prepopulated_fields dictionary is the field you want filled, and the value is a tuple of fields you want concatenated.

Outside of admin, you can use the slugify function in your views. In templates, you can use the |slugify filter.

There is also this package which will take care of this automatically: https://pypi.python.org/pypi/django-autoslug

like image 174
camflan Avatar answered Oct 17 '22 08:10

camflan


Thought I would add a complete and up-to-date answer with gotchas mentioned:

1. Auto-populate forms in Django Admin

If you are only concerned about adding and updating data in the admin, you could simply use the prepopulated_fields attribute

class ArticleAdmin(admin.ModelAdmin):
    prepopulated_fields = {"slug": ("title",)}

admin.site.register(Article, ArticleAdmin)

2. Auto-populate custom forms in templates

If you have built your own server-rendered interface with forms, you could auto-populate the fields by using either the |slugify tamplate filter or the slugify utility when saving the form (is_valid).

3. Auto-populating slugfields at model-level with django-autoslug

The above solutions will only auto-populate the slugfield (or any field) when data is manipulated through those interfaces (the admin or a custom form). If you have an API, management commands or anything else that also manipulates the data you need to drop down to model-level.

django-autoslug provides the AutoSlugField-fields which extends SlugField and allows you to set which field it should slugify neatly:

class Article(Model):
    title = CharField(max_length=200)
    slug = AutoSlugField(populate_from='title')

The field uses pre_save and post_save signals to achieve its functionality so please see the gotcha text at the bottom of this answer.

4. Auto-populating slugfields at model-level by overriding save()

The last option is to implement this yourself, which involves overriding the default save() method:

class Article(Model):
    title = CharField(max_length=200)
    slug = SlugField()

    def save(self, *args, **kwargs):
        self.slug = slugify(self.title)
        super().save(*args, **kwargs)

NOTE: Bulk-updates will bypass your code (including signals)

This is a common miss-understanding by beginners to Django. First you should know that the pre_save and post_save signals are directly related to the save()-method. Secondly the different ways to do bulk-updates in Django all circumvent the save()-method to achieve high performance, by operating directly on the SQL-layer. This means that for the example model used in solution 3 or 4 above:

  • Article.objects.all().update(title='New post') will NOT update the slug of any article
  • Using bulk_create or bulk_update on the Article model will NOT update the slug of any article.
  • Since the save()-method is not called, no pre_save or post_save signals will be emitted.

To do bulk updates and still utilize code-level constraints the only solution is to iterate objects one by one and call its save()-method, which has drastically less performance than SQL-level bulk operations. You could of course use triggers in your database, though that is a totally different topic.

like image 7
Andreas Bergström Avatar answered Oct 17 '22 08:10

Andreas Bergström


Outside the admin, see this django snippet. Put it in your .save(), and it'll work with objects created programmatically. Inside the admin, as the others have said, use prepopulated_fields.

like image 6
AdamKG Avatar answered Oct 17 '22 09:10

AdamKG