Làm thế nào để tôi tạo một con sên trong Django?


218

Tôi đang cố gắng tạo ra một SlugFieldDjango.

Tôi đã tạo ra mô hình đơn giản này:

from django.db import models

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

Sau đó tôi làm điều này:

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

Tôi đã mong đợi b-b-b-b.

Câu trả lời:


413

Bạn sẽ cần sử dụng hàm slugify.

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

Bạn có thể gọi slugifytự động bằng cách ghi đè savephương thức:

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)

Xin lưu ý rằng những điều trên sẽ khiến URL của bạn thay đổi khi qtrường được chỉnh sửa, điều này có thể gây ra các liên kết bị hỏng . Có thể tốt hơn là chỉ tạo sên một lần khi bạn tạo một đối tượng mới:

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)

4
nhút nhát có một mô hình đặc biệt? Tại sao không chỉ slugify CharFields?
Johnd

23
SlugFields đặt db_index = True theo mặc định và cũng sử dụng trường biểu mẫu theo mặc định có biểu thức xác thực để yêu cầu sên hợp lệ (nếu được trình bày trong ModelForm hoặc trong quản trị viên). Bạn có thể thực hiện những điều đó một cách thủ công với CharField nếu bạn thích, nó chỉ làm cho ý định mã của bạn không rõ ràng. Ngoài ra, đừng quên cài đặt Preopulation_fields ModelAdmin, nếu bạn muốn tự động chuẩn bị dựa trên JS trong quản trị viên.
Carl Meyer

4
Như Dingle nói dưới đây trong câu trả lời của mình, bạn sẽ cần phải thay thế def save(self):bằng def save(self, *args, **kwargs):để tránh sai sót khỏi bị ném khi viết một cái gì đó giống như test.objects.create(q="blah blah blah").
Liam

6
Coi chừng mã này sẽ cập nhật sên mỗi lần lưu. url của bạn sẽ thay đổi và "URI tuyệt vời không thay đổi" w3.org/Provider/Style/URI.html
dzen

18
slugify()cũng có thể được tìm thấy trong django.utils.text.slugify, không rõ ràng khi điều này đã được thêm vào.
mrmagooey

112

Có trường hợp góc với một số ký tự utf-8

Thí dụ:

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

Điều này có thể được giải quyết với Unidecode

>>> from unidecode import unidecode
>>> from django.template.defaultfilters import slugify
>>> slugify(unidecode(u"test ąęśćółń"))
u'test-aescoln'

7
utf-8 hiện được xử lý chính xác bằng slugify (trong django 1.8.5)
Rick Westera

Như @RickWestera cho biết hiện tại việc này được xử lý bằng slugify, mặc dù nếu vì lý do nào đó bạn không muốn sử dụng slugify, hãy kiểm tra iri_to_uri từ django.utils.encoding: docs.djangoproject.com/en/2.0/ref/unicode/
Erwol

64

Một sửa chữa nhỏ cho câu trả lời của Theerer's: Để ghi đè save()hàm trong các lớp mô hình, tốt hơn nên thêm đối số vào nó:

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)

Nếu không, test.objects.create(q="blah blah blah")sẽ dẫn đến một force_insertlỗi (đối số không mong muốn).


2
Một điều nữa rất nhỏ để thêm vào câu trả lời của người viết: Tôi sẽ thực hiện dòng cuối cùng đó return super(test, self).save(*args, **kwargs). Tôi nghĩ phương pháp này trả về Nonevà tôi không biết có kế hoạch nào để thay đổi điều đó, nhưng sẽ không có hại gì khi trả lại phương thức của siêu lớp trong trường hợp nó thay đổi vào lúc nào đó trong tương lai.
Duncan Parkes

Vui lòng thêm rằng từ django.utils.text nhập slugify là cần thiết cho giải pháp này.
Routhinator

1
@Routhinator đã làm điều đó
Jonas Gröger

Đưa ra một số người cảm nhận để hỏi nếu đây vẫn là một phương pháp ưa thích để làm điều này.
sytech

29

Nếu bạn đang sử dụng giao diện quản trị để thêm các mục mới trong mô hình của mình, bạn có thể thiết lập một ModelAdmintrong admin.pyvà sử dụng prepopulated_fieldsđể tự động nhập vào một con sên:

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

admin.site.register(Client, ClientAdmin)

Ở đây, khi người dùng nhập một giá trị trong biểu mẫu quản trị viên cho nametrường, slugnó sẽ được tự động điền với slugified chính xác name.


Tôi slugnamecác lĩnh vực có bản dịch. Làm thế nào tôi có thể làm điều đó với bản dịch? Bởi vì tôi đã cố thêm 'slug_en':('name_en',)và gặp lỗi mà thuộc tính không tồn tại trong mô hình của tôi.
patricia

22

Trong hầu hết các trường hợp, sên không nên thay đổi, vì vậy bạn thực sự chỉ muốn tính toán nó trong lần lưu đầu tiên:

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()

6

Sử dụng prepopulated_fieldstrong lớp quản trị của bạn:

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

admin.site.register(Article, ArticleAdmin)

1
Bạn có thể giải thích? Làm thế nào để quản trị viên ảnh hưởng đến dự án?
Bryce

5

Nếu bạn không muốn đặt slugfield thành Không thể chỉnh sửa, thì tôi tin rằng bạn sẽ muốn đặt các thuộc tính Null và Blank thành Sai. Nếu không, bạn sẽ gặp lỗi khi cố lưu trong Quản trị viên.

Vì vậy, một sửa đổi cho ví dụ trên sẽ là ::

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()


4

Tôi đang sử dụng Django 1.7

Tạo một SlugField trong mô hình của bạn như thế này:

slug = models.SlugField()

Sau đó, trong admin.pyđịnh nghĩa prepopulated_fields;

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

Chính xác những gì tôi muốn
Nick

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.