Login

AutoSlugField

Author:
callipeo
Posted:
November 28, 2007
Language:
Python
Version:
.96
Score:
2 (after 4 ratings)

This is a simple solution aimed at saving some time when you simply want to get a slug from a model field just before saving.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
from django.db.models import CharField

from django.utils.encoding import force_unicode

from django.template.defaultfilters import slugify

def _get_field(instance, name):
    try:
        return getattr(instance, name)
    except AttributeError:
        raise ValueError("Model %s has no field '%s'" % \
                             (instance.__class__.__name__, name))

class AutoSlugField(CharField):
    """ A SlugField that automatically populate itself using the value of another
    field.

    In addition to CharField's usual parameters you can specify:

    populate_from (mandatory): the name of the field to be used for the slug
                               creation. ValueError will be raised at the
                               object save() time if the field does not exist.

    slugify_func: the function to apply on the value of the field.
                  If unspecified django.template.defaultfilters.slugify will be
                  used.

    append_field: the name of a field that will be appended to the slug, or
                  None. ValueError will be raised at the object save() time if
                  the field does not exist.

    prepend_field: the name of a field that will be prepended to the slug, or
                   None. ValueError will be raised at the object save() time if
                   the field does not exist.

    field_separator: the separator between the slug and the {pre, ap}pended
                     fields. The default value is u'-'.

    Unless explicitly set otherwise, the field will be created with the
    'editable' and 'db_index' parameters set respectively to False and
    True. """
    
    def __init__(self, *args, **kwargs):
        # Set editable=False if not explicitly set
        if 'editable' not in kwargs:
            kwargs['editable'] = False
            
        # Set db_index=True if not explicitly set
        if 'db_index' not in kwargs:
            kwargs['db_index'] = True

        populate_from = kwargs.pop('populate_from', None)
        slugify_func = kwargs.pop('slugify_func', slugify)
        append_field = kwargs.pop('append_field', None)
        prepend_field = kwargs.pop('prepend_field', None)
        field_separator = kwargs.pop('field_separator', u'-')
            
        if populate_from is None:
            raise ValueError("missing 'populate_from' argument")
        else:
            self._populate_from = populate_from
        
        self._slugify_func = slugify_func

        self._prepend_field = prepend_field
        self._append_field = append_field
        self._field_separator = field_separator

        super(AutoSlugField, self).__init__(*args, **kwargs)
        
    def pre_save(self, model_instance, add):
        populate_from = _get_field(model_instance, self._populate_from)
        
        make_slug = self._slugify_func

        chunks = list()

        if self._prepend_field is not None:
            prepend_field = _get_field(model_instance, self._prepend_field)
            # Prepend the field's value only if it is not empty
            if prepend_field:
                chunks.append(force_unicode(prepend_field))
        
        chunks.append(make_slug(populate_from))
                
        if self._append_field is not None:
            append_field = _get_field(model_instance, self._append_field)
            # Append the field's value only if it is not empty
            if append_field:
                chunks.append(force_unicode(append_field))

        value = self._field_separator.join(chunks)
        
        setattr(model_instance, self.attname, value)

        return value

    def get_internal_type(self):
        return 'SlugField'

More like this

  1. Template tag - list punctuation for a list of items by shapiromatron 11 months, 2 weeks ago
  2. JSONRequestMiddleware adds a .json() method to your HttpRequests by cdcarter 11 months, 3 weeks ago
  3. Serializer factory with Django Rest Framework by julio 1 year, 6 months ago
  4. Image compression before saving the new model / work with JPG, PNG by Schleidens 1 year, 7 months ago
  5. Help text hyperlinks by sa2812 1 year, 8 months ago

Comments

callipeo (on November 28, 2007):

Some simple examples:

class Article1(models.Model):
    """
    >>> article = Article1(title=u"This is the title",
    ... text=u"This is the text")
    >>> article.save()
    >>> article.title
    u'This is the title'
    >>> article.text
    u'This is the text'
    >>> article.slug
    u'this-is-the-title'  
    """
    title = models.CharField(max_length=100)
    text = models.TextField()
    slug = AutoSlugField(max_length=100, populate_from='title')

class Article2(models.Model):
    """
    >>> article_a = Article2(title=u"The title",
    ... author=u"joe", text=u"The text")
    >>> article_a.save()
    >>> article_a.author
    u'joe'
    >>> article_a.slug
    u'joe_the-title'

    >>> article_b = Article2(title=u"Another title",
    ... text=u"Another text")
    >>> article_b.save()
    >>> article_b.author

    >>> article_b.slug
    u'another-title'
    """
    title = models.CharField(max_length=100)
    author = models.CharField(max_length=20, null=True)
    text = models.TextField()
    slug = AutoSlugField(max_length=120, populate_from='title',
                         prepend_field='author', field_separator=u'_')

#

mikko (on November 29, 2007):

I always just do something like this:

from django.template.defaultfilters import slugify

class Foo(models.Model):
    slug = models.SlugField(editable=False,max_length=100)
    title = models.CharField(max_length=100)
    ...
    def save(self):
        self.slug = slugify(self.title)
        ...

I dont really see the point of this field type..

#

callipeo (on November 30, 2007):

That's what i used to do too. I just thought that the need of an automatically-generated slug was a common enough situation to write some genereal-purpose code and forget about it for the rest of my time. Moreover, i felt more comfortable with a declarative approach than with the standard idiom of overriding save(). Not really a big deal, i know :), but i eventually came up with this solution.

#

Please login first before commenting.