Bagaimana cara membuat siput di Django?

218

Saya mencoba membuat SlugFielddi Django.

Saya membuat model sederhana ini:

from django.db import models

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

Saya kemudian melakukan ini:

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

Saya mengharapkan b-b-b-b.

Johnd
sumber

Jawaban:

413

Anda harus menggunakan fungsi slugify.

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

Anda dapat menelepon slugifysecara otomatis dengan mengganti savemetode:

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)

Perlu diketahui bahwa di atas akan menyebabkan URL Anda berubah ketika qbidang diedit, yang dapat menyebabkan tautan rusak . Mungkin lebih baik untuk menghasilkan siput hanya sekali ketika Anda membuat objek baru:

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)
Sobat
sumber
4
malu memiliki tipe model khusus? mengapa tidak hanya membasmi CharFields?
Johnd
23
SlugFields set db_index = Benar secara default, dan juga menggunakan bidang formulir secara default yang memiliki regex validasi untuk meminta siput yang valid (jika diwakili dalam ModelForm atau di admin). Anda dapat melakukan hal-hal itu secara manual dengan CharField jika Anda mau, itu hanya membuat maksud kode Anda kurang jelas. Juga, jangan lupa pengaturan ModelAdmin prepopulate_fields, jika Anda ingin auto-prepopulate berbasis JS di admin.
Carl Meyer
4
Seperti yang dikatakan Dingle di bawah dalam jawabannya, Anda harus menggantinya def save(self):dengan def save(self, *args, **kwargs):untuk menghindari kesalahan yang terjadi saat menulis sesuatu seperti test.objects.create(q="blah blah blah").
Liam
6
Berhati-hatilah bahwa kode ini akan memperbarui siput yang disimpan masing-masing. url Anda akan berubah, dan "Cool URIs tidak berubah" w3.org/Provider/Style/URI.html
dzen
18
slugify()juga dapat ditemukan di django.utils.text.slugify, tidak jelas kapan ini ditambahkan.
mrmagooey
112

Ada kasus sudut dengan beberapa karakter utf-8

Contoh:

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

Ini dapat diatasi dengan Unidecode

>>> from unidecode import unidecode
>>> from django.template.defaultfilters import slugify
>>> slugify(unidecode(u"test ąęśćółń"))
u'test-aescoln'
DooBLER
sumber
7
utf-8 sekarang ditangani dengan benar oleh slugify (dalam django 1.8.5)
Rick Westera
Seperti @RickWestera mengatakan ini sekarang ditangani oleh slugify, walaupun jika karena alasan tertentu Anda tidak ingin menggunakan slugify, periksa iri_to_uri dari django.utils.encoding: docs.djangoproject.com/en/2.0/ref/unicode/…
Erwol
64

Koreksi kecil untuk jawaban Thepeer: Untuk menggantikan save() fungsi di kelas model, lebih baik tambahkan argumen ke dalamnya:

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)

Jika tidak, test.objects.create(q="blah blah blah")akan menghasilkan force_insertkesalahan (argumen yang tidak terduga).

Lembah dalam
sumber
2
Satu hal lagi yang sangat kecil untuk ditambahkan ke jawaban thepeer: Saya akan membuat baris terakhir return super(test, self).save(*args, **kwargs). Saya pikir metode ini kembali None, dan saya tidak tahu ada rencana untuk mengubahnya, tetapi tidak ada salahnya untuk mengembalikan apa yang dilakukan metode superclass kalau-kalau itu berubah di masa depan.
Duncan Parkes
Silakan tambahkan bahwa dari django.utils.text, impor slugify diperlukan untuk solusi ini.
Routhinator
1
@Routhinator melakukannya
Jonas Gröger
Keluarkan beberapa perasa untuk bertanya apakah ini masih merupakan metode yang disukai untuk melakukan ini.
sytech
29

Jika Anda menggunakan antarmuka admin untuk menambahkan item baru dari model Anda, Anda dapat mengatur ModelAdmindi Anda admin.pydan menggunakannya prepopulated_fieldsuntuk secara otomatis memasukkan siput:

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

admin.site.register(Client, ClientAdmin)

Di sini, ketika pengguna memasukkan nilai dalam formulir admin untuk namebidang, maka slugakan secara otomatis diisi dengan slugified yang benar name.

henrym
sumber
Bidang saya slugdan namememiliki terjemahan. Bagaimana saya bisa melakukannya dengan terjemahan? Karena saya sudah mencoba menambahkan 'slug_en':('name_en',)dan mendapatkan kesalahan bahwa atribut tidak ada dalam model saya.
patricia
22

Dalam kebanyakan kasus siput tidak boleh berubah, jadi Anda benar-benar hanya ingin menghitungnya pada penyimpanan pertama:

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()
theer
sumber
6

Gunakan prepopulated_fieldsdi kelas admin Anda:

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

admin.site.register(Article, ArticleAdmin)
sergey
sumber
1
Bisakah Anda jelaskan? Bagaimana admin mempengaruhi proyek?
Bryce
5

Jika Anda tidak ingin mengatur slugfield menjadi Tidak dapat diedit, maka saya yakin Anda ingin mengatur properti Null dan Blank menjadi False. Kalau tidak, Anda akan mendapatkan kesalahan saat mencoba menyimpan di Admin.

Jadi modifikasi pada contoh di atas akan menjadi ::

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
sumber
Documents on diedit
stephen
4

Saya menggunakan Django 1.7

Buat SlugField dalam model Anda seperti ini:

slug = models.SlugField()

Kemudian di admin.pydefinisikan prepopulated_fields;

class ArticleAdmin(admin.ModelAdmin):
    prepopulated_fields = {"slug": ("title",)}
min2bro
sumber
Apa yang saya inginkan
Nick