Como crio uma lesma no Django?

218

Eu estou tentando criar um SlugFieldno Django.

Eu criei este modelo simples:

from django.db import models

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

Eu então faço isso:

>>> 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'

Eu estava esperando b-b-b-b.

Johnd
fonte

Respostas:

413

Você precisará usar a função slugify.

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

Você pode chamar slugifyautomaticamente, substituindo o 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)

Esteja ciente de que o descrito acima fará com que seu URL seja alterado quando o qcampo for editado, o que pode causar links quebrados . Pode ser preferível gerar a lesma apenas uma vez quando você cria um novo 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)
camarada
fonte
4
tímido tem um tipo de modelo especial? por que não slugify CharFields?
JohnD
23
SlugFields define db_index = True por padrão e também usa um campo de formulário por padrão que possui uma regex de validação para exigir lesmas válidas (se representadas em um ModelForm ou no administrador). Você pode fazer essas coisas manualmente com um CharField, se preferir, apenas torna menos clara a intenção do seu código. Além disso, não se esqueça da configuração ModelAdmin prepopulate_fields, se você desejar um pré-preenchimento automático baseado em JS no administrador.
Carl Meyer #
4
Como Dingle disse abaixo em sua resposta, você precisará substituí-lo def save(self):por def save(self, *args, **kwargs):para evitar que erros ocorram ao escrever algo parecido test.objects.create(q="blah blah blah").
Liam
6
Cuidado para que esse código atualize a lesma que cada uma salva. seu URL mudará e "URIs legais não mudam" w3.org/Provider/Style/URI.html
dzen
18
slugify()também pode ser encontrado em django.utils.text.slugify, não está claro quando isso foi adicionado.
mrmagooey
112

Há uma caixa de canto com alguns caracteres utf-8

Exemplo:

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

Isso pode ser resolvido com o Unidecode

>>> from unidecode import unidecode
>>> from django.template.defaultfilters import slugify
>>> slugify(unidecode(u"test ąęśćółń"))
u'test-aescoln'
DooBLER
fonte
7
utf-8 agora é tratada corretamente pelo slugify (em django 1.8.5)
Rick Westera
Como o @RickWestera disse que isso agora é tratado pelo slugify, embora, por algum motivo, você não queira usar o slugify, verifique iri_to_uri em django.utils.encoding: docs.djangoproject.com/en/2.0/ref/unicode/…
Erwol
64

Uma pequena correção na resposta de Thepeer: Para substituir a save()função nas classes de modelo, adicione melhor argumentos a ela:

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)

Caso contrário, test.objects.create(q="blah blah blah")resultará em um force_inserterro (argumento inesperado).

Dingle
fonte
2
Mais uma coisa muito menor a acrescentar à resposta do sujeito: eu faria essa última linha return super(test, self).save(*args, **kwargs). Acho que esse método retorna None, e não conheço nenhum plano para mudar isso, mas não faz mal retornar o que o método da superclasse faz caso ele mude em algum momento no futuro.
Duncan Parkes
Por favor, adicione que a partir do django.utils.text import slugify é necessário para esta solução.
Routhinator
1
@Routhinator fez isso
Jonas Gröger
Coloque alguns sensores para perguntar se esse ainda é o método preferido para fazer isso.
Sytech
29

Se você estiver usando a interface administrativa para adicionar novos itens ao seu modelo, poderá configurar um ModelAdminno seu admin.pye utilizar prepopulated_fieldspara automatizar a entrada de uma lesma:

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

admin.site.register(Client, ClientAdmin)

Aqui, quando o usuário digitar um valor no formulário de administrador para o namecampo, slugele será preenchido automaticamente com o slugified correto name.

henrym
fonte
Meus campos sluge nametêm traduções. Como posso fazer isso com traduções? Porque eu tentei adicionar 'slug_en':('name_en',)e obtive o erro de que esse atributo não existe no meu modelo.
Patricia
22

Na maioria dos casos, a lesma não deve mudar; portanto, você realmente deseja calculá-la apenas no primeiro salvamento:

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()
thepeer
fonte
6

Use prepopulated_fieldsna sua classe de administrador:

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

admin.site.register(Article, ArticleAdmin)
sergey
fonte
1
Você poderia explicar? Como o administrador afeta o projeto?
Bryce
5

Se você não deseja definir o campo slug como Não pode ser editável, acredito que deseje definir as propriedades Null e Blank como False. Caso contrário, você receberá um erro ao tentar salvar no Admin.

Portanto, uma modificação no exemplo acima seria:

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
fonte
Documentos no editable
stephen
4

Estou usando o Django 1.7

Crie um SlugField no seu modelo como este:

slug = models.SlugField()

Então em admin.pydefine prepopulated_fields;

class ArticleAdmin(admin.ModelAdmin):
    prepopulated_fields = {"slug": ("title",)}
min2bro
fonte
Exatamente o que eu queria
Nick