Comment créer un slug dans Django?

218

J'essaie de créer un SlugFielddans Django.

J'ai créé ce modèle simple:

from django.db import models

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

Je fais ensuite ceci:

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

J'attendais b-b-b-b.

Johnd
la source

Réponses:

413

Vous devrez utiliser la fonction slugify.

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

Vous pouvez appeler slugifyautomatiquement en remplaçant la saveméthode:

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)

N'oubliez pas que ce qui précède entraînera la modification de votre URL lorsque le qchamp est modifié, ce qui peut provoquer des liens rompus . Il peut être préférable de générer le slug une seule fois lorsque vous créez un nouvel objet:

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)
Copain
la source
4
timide avez un type de modèle spécial? pourquoi ne pas simplement slugify CharFields?
Johnd
23
SlugFields définit db_index = True par défaut, et utilise également un champ de formulaire par défaut qui a une expression régulière de validation pour exiger des slugs valides (s'ils sont représentés dans un ModelForm ou dans l'administrateur). Vous pouvez faire ces choses manuellement avec un CharField si vous préférez, cela rend simplement l'intention de votre code moins claire. N'oubliez pas non plus le paramètre prepopulate_fields ModelAdmin, si vous souhaitez un pré-remplissage automatique basé sur JS dans l'administrateur.
Carl Meyer,
4
Comme Dingle l'a dit ci-dessous dans sa réponse, vous devrez remplacer def save(self):par def save(self, *args, **kwargs):afin d'éviter que des erreurs ne soient lancées lors de l'écriture de quelque chose comme test.objects.create(q="blah blah blah").
Liam
6
Attention, ce code mettra à jour le slug à chaque sauvegarde. votre URL va changer, et "Les URI cool ne changent pas" w3.org/Provider/Style/URI.html
dzen
18
slugify()peut également être trouvé dans django.utils.text.slugify, pas clair quand cela a été ajouté.
mrmagooey
112

Il y a un cas d'angle avec quelques caractères utf-8

Exemple:

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

Cela peut être résolu avec Unidecode

>>> from unidecode import unidecode
>>> from django.template.defaultfilters import slugify
>>> slugify(unidecode(u"test ąęśćółń"))
u'test-aescoln'
DooBLER
la source
7
utf-8 est désormais géré correctement par slugify (dans django 1.8.5)
Rick Westera
Comme @RickWestera l'a dit, cela est désormais géré par slugify, bien que si pour une raison quelconque vous ne souhaitez pas utiliser slugify, vérifiez iri_to_uri depuis django.utils.encoding: docs.djangoproject.com/en/2.0/ref/unicode/…
Erwol
64

Une petite correction à la réponse de Thepeer: Pour remplacer la save()fonction dans les classes de modèle, il vaut mieux y ajouter des arguments:

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)

Sinon, test.objects.create(q="blah blah blah")cela entraînera une force_inserterreur (argument inattendu).

Dingle
la source
2
Une autre chose très mineure à ajouter à la réponse du peintre: je ferais cette dernière ligne return super(test, self).save(*args, **kwargs). Je pense que cette méthode revient None, et je ne connais aucun plan pour changer cela, mais cela ne fait pas de mal de retourner ce que fait la méthode de la superclasse au cas où elle changerait dans le futur.
Duncan Parkes
Veuillez ajouter qu'à partir de django.utils.text, l'importation slugify est requise pour cette solution.
Routhinator
1
@Routhinator l'a fait
Jonas Gröger
Éteindre certains palpeurs pour demander si c'est toujours une méthode préférée pour le faire.
sytech
29

Si vous utilisez l'interface d'administration pour ajouter de nouveaux éléments de votre modèle, vous pouvez configurer un ModelAdmindans votre admin.pyet utiliser prepopulated_fieldspour automatiser la saisie d'un slug:

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

admin.site.register(Client, ClientAdmin)

Ici, lorsque l'utilisateur entre une valeur dans le formulaire d'administration pour le namechamp, le slugsera automatiquement rempli avec le bon slugified name.

henrym
la source
Mes champs sluget nameont des traductions. Comment puis-je faire cela avec des traductions? Parce que j'ai essayé d'ajouter 'slug_en':('name_en',)et obtenu l'erreur que l'attribut n'existe pas dans mon modèle.
patricia
22

Dans la plupart des cas, le slug ne devrait pas changer, donc vous ne voulez vraiment le calculer qu'au premier enregistrement:

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
la source
6

Utilisez prepopulated_fieldsdans votre classe d'administration:

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

admin.site.register(Article, ArticleAdmin)
sergey
la source
1
Pourriez-vous expliquer? Comment l'administrateur affecte-t-il le projet?
Bryce
5

Si vous ne souhaitez pas définir le champ slug sur Non modifiable, je pense que vous souhaiterez définir les propriétés Null et Blank sur False. Sinon, vous obtiendrez une erreur lors de la tentative d'enregistrement dans Admin.

Une modification de l'exemple ci-dessus serait donc:

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
la source
Documents sur modifiables
Stephen
4

J'utilise Django 1.7

Créez un SlugField dans votre modèle comme ceci:

slug = models.SlugField()

Ensuite, admin.pydéfinissez prepopulated_fields;

class ArticleAdmin(admin.ModelAdmin):
    prepopulated_fields = {"slug": ("title",)}
min2bro
la source
Exactement ce que je voulais
Nick