¿Cómo creo una babosa en Django?

218

Estoy tratando de crear un SlugFielden Django.

Creé este modelo simple:

from django.db import models

class Test(models.Model):
    q = models.CharField(max_length=30)
    s = models.SlugField()

Entonces hago esto:

>>> from mysite.books.models import Test
>>> t=Test(q="aa a a a", s="b b b b")
>>> t.s
'b b b b'
>>> t.save()
>>> t.s
'b b b b'

Yo estaba esperando b-b-b-b.

Johnd
fuente

Respuestas:

413

Deberá usar la función slugify.

>>> from django.template.defaultfilters import slugify
>>> slugify("b b b b")
u'b-b-b-b'
>>>

Puede llamar slugifyautomáticamente anulando el savemétodo:

class Test(models.Model):
    q = models.CharField(max_length=30)
    s = models.SlugField()

    def save(self, *args, **kwargs):
        self.s = slugify(self.q)
        super(Test, self).save(*args, **kwargs)

Tenga en cuenta que lo anterior hará que su URL cambie cuando qse edite el campo, lo que puede causar enlaces rotos . Puede ser preferible generar la babosa solo una vez cuando crea un nuevo objeto:

class Test(models.Model):
    q = models.CharField(max_length=30)
    s = models.SlugField()

    def save(self, *args, **kwargs):
        if not self.id:
            # Newly created object, so set slug
            self.s = slugify(self.q)

        super(Test, self).save(*args, **kwargs)
compañero
fuente
44
tímido tiene un tipo de modelo especial? ¿Por qué no simplemente slugify CharFields?
Johnd
23
SlugFields establece db_index = True de forma predeterminada, y también usa un campo de formulario de forma predeterminada que tiene una expresión regular de validación para requerir babosas válidas (si se representa en un ModelForm o en el administrador). Puede hacer esas cosas manualmente con un CharField si lo prefiere, solo hace que la intención de su código sea menos clara. Además, no olvide la configuración de prepopulate_fields ModelAdmin, si desea auto-prepoblar basado en JS en el administrador.
Carl Meyer
44
Como dijo Dingle continuación en su respuesta, tendrá que reemplazar def save(self):con def save(self, *args, **kwargs):el fin de errores Evita sea lanzado al escribir algo así test.objects.create(q="blah blah blah").
Liam
66
Tenga en cuenta que este código actualizará el slug que guarda cada uno. su URL cambiará y "Los URI geniales no cambian" w3.org/Provider/Style/URI.html
dzen
18
slugify()también se puede encontrar en django.utils.text.slugify, no está claro cuándo se agregó esto.
mrmagooey
112

Hay una esquina con algunos caracteres utf-8

Ejemplo:

>>> from django.template.defaultfilters import slugify
>>> slugify(u"test ąęśćółń")
u'test-aescon' # there is no "l"

Esto se puede resolver con Unidecode

>>> from unidecode import unidecode
>>> from django.template.defaultfilters import slugify
>>> slugify(unidecode(u"test ąęśćółń"))
u'test-aescoln'
DooBLER
fuente
77
utf-8 ahora es manejado correctamente por slugify (en django 1.8.5)
Rick Westera
Como dijo @RickWestera, ahora esto es manejado por slugify, aunque si por alguna razón no desea usar slugify, verifique iri_to_uri en django.utils.encoding: docs.djangoproject.com/en/2.0/ref/unicode/…
Erwol
64

Una pequeña corrección a la respuesta de Thepeer: para anular la save()función en las clases de modelo, es mejor agregarle argumentos:

from django.utils.text import slugify

def save(self, *args, **kwargs):
    if not self.id:
        self.s = slugify(self.q)

    super(test, self).save(*args, **kwargs)

De lo contrario, test.objects.create(q="blah blah blah")se producirá un force_inserterror (argumento inesperado).

Valle arbolado
fuente
2
Otra cosa muy menor para agregar a la respuesta del compañero: haría esa última línea return super(test, self).save(*args, **kwargs). Creo que este método regresa None, y no conozco ningún plan para cambiar eso, pero no hace daño devolver lo que hace el método de la superclase en caso de que cambie en algún momento en el futuro.
Duncan Parkes el
Agregue que desde django.utils.text import slugify se requiere para esta solución.
Routhinator
1
@Routhinator lo hizo
Jonas Gröger
Poniendo algunos sensores para preguntar si este sigue siendo un método preferido para hacerlo.
sytech
29

Si está utilizando la interfaz de administración para agregar nuevos elementos de su modelo, puede configurar un ModelAdminen su admin.pyy utilizar prepopulated_fieldspara automatizar la entrada de una babosa:

class ClientAdmin(admin.ModelAdmin):
    prepopulated_fields = {'slug': ('name',)}

admin.site.register(Client, ClientAdmin)

Aquí, cuando el usuario ingresa un valor en el formulario de administración para el namecampo, slugse completará automáticamente con el slugified correcto name.

henrym
fuente
Mi slugy los namecampos tienen traducciones. ¿Cómo puedo hacer eso con las traducciones? Como intenté agregar 'slug_en':('name_en',)y obtuve el error de que el atributo no existe en mi modelo.
patricia
22

En la mayoría de los casos, el slug no debería cambiar, por lo que realmente solo desea calcularlo en el primer guardado:

class Test(models.Model):
    q = models.CharField(max_length=30)
    s = models.SlugField(editable=False) # hide from admin

    def save(self):
        if not self.id:
            self.s = slugify(self.q)

        super(Test, self).save()
el par
fuente
6

Use prepopulated_fieldsen su clase de administrador:

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

admin.site.register(Article, ArticleAdmin)
sergey
fuente
1
¿Podrías explicar? ¿Cómo afecta el administrador al proyecto?
Bryce
5

Si no desea establecer el campo slugfield en No ser editable, entonces creo que querrá establecer las propiedades Nulo y En blanco en Falso. De lo contrario, recibirá un error al intentar guardar en Admin.

Entonces, una modificación al ejemplo anterior sería ::

class test(models.Model):
    q = models.CharField(max_length=30)
    s = models.SlugField(null=True, blank=True) # Allow blank submission in admin.

    def save(self):
        if not self.id:
            self.s = slugify(self.q)

        super(test, self).save()
Streamweaver
fuente
Docs en editable
stephen
4

Estoy usando Django 1.7

Cree un SlugField en su modelo como este:

slug = models.SlugField()

Luego en admin.pydefinir prepopulated_fields;

class ArticleAdmin(admin.ModelAdmin):
    prepopulated_fields = {"slug": ("title",)}
min2bro
fuente
Exactamente lo que quería
Nick