Gerando slugs usando o Django Signals

É muito comum o uso de slugs na construção de urls elegantes, o Django já vem com uma função chamada slugify que transforma um string no formato de slug.

Utilizando o signals, podemos criar uma função que faz o processamento do slug após o registro ser criado no banco, ficando transparente para o usuário final.

Eu vou criar um model chamado category com name e slug

# models.py
# -*- coding: utf-8 -*-
from django.db import models

class Category(models.Model):
    # category name
    name = models.CharField(u'nome', max_length=100)
    # category slug
    slug = models.SlugField(max_length=150, editable=False)

    def __unicode__(self):
        return self.name

    class Meta:
        verbose_name = u'categoria'
        verbose_name_plural = u'categorias'

Note que é interessante usar mais caracteres no max_length do slug, aqui temos o name com max_length=100 e o slug com max_length=150.

Agora para um melhor controle, vamos adicionar outros dois atributos:
slug_field_name: informa qual é o atributo slug
slug_from: informa o atributo de onde o slug vai ser gerado

# models.py
# -*- coding: utf-8 -*-
from django.db import models

class Category(models.Model):
    # category name
    name = models.CharField(u'nome', max_length=100)
    # category slug
    slug = models.SlugField(max_length=150, editable=False)
    # Field to slug
    slug_field_name = 'slug'
    slug_from = 'name'

    def __unicode__(self):
        return self.name

    class Meta:
        verbose_name = u'categoria'
        verbose_name_plural = u'categorias'

Agora vamos escrever a função que vai tratar o signal

# signals.py
# -*- coding: utf-8 -*-
from django.template.defaultfilters import slugify

def create_slug(sender, instance, signal, *args, **kwargs):
    # check for id and attributes
    if instance.id and hasattr(instance, 'slug_field_name') and hasattr(instance, 'slug_from'):
        # get slug information
        slug_name = instance.slug_field_name
        slug_from = instance.slug_from
        # save slug if empty
        if not getattr(instance, slug_name, None):
            # create slug
            slug = '%s-' % instance.id + slugify(getattr(instance, slug_from))
            # set slug
            setattr(instance, slug_name, slug)
            # save instance
            instance.save()

A função create_slug checa se a instância em questão possui id, slug_field_name e slug_from, caso o slug não esteja preenchido um novo slug é gerado e gravado.

Para geração de slugs eu utilizei a concatenação do id do objeto e o slug, por isso que é sempre interessante deixar o max_length do slug maior do que a origem dele. Dessa forma nunca vai existir slugs em duplicidade, porque o id da instância sempre é diferente.

Para finalizar, basta adicionar o signal no models.py

# models.py
# -*- coding: utf-8 -*-
from django.db import models
from django.db.models import signals

from signals import create_slug

class Category(models.Model):
    # category name
    name = models.CharField(u'nome', max_length=100)
    # category slug
    slug = models.SlugField(max_length=150, editable=False)
    # Field to slug
    slug_field_name = 'slug'
    slug_from = 'name'

    def __unicode__(self):
        return self.name

    class Meta:
        verbose_name = u'categoria'
        verbose_name_plural = u'categorias'

# Signals
signals.post_save.connect(create_slug, sender=Category)

Como essa função é genérica, você pode usar ela em qualquer model, basta informar o slug_field_name e slug_from.

Deixe um comentário

Preencha os seus dados abaixo ou clique em um ícone para log in:

Logotipo do WordPress.com

Você está comentando utilizando sua conta WordPress.com. Sair / Alterar )

Imagem do Twitter

Você está comentando utilizando sua conta Twitter. Sair / Alterar )

Foto do Facebook

Você está comentando utilizando sua conta Facebook. Sair / Alterar )

Foto do Google+

Você está comentando utilizando sua conta Google+. Sair / Alterar )

Conectando a %s