Khi model có 10–15 field, trang sửa trong Django admin thành một cột dài vô tận: tiêu đề, slug, nội dung, danh mục, ngày xuất bản và cờ trạng thái nằm lẫn lộn nhau. Giải pháp là fieldsets để gom field thành từng nhóm có tiêu đề và thu gọn được, kết hợp prepopulated_fields để slug tự sinh từ tiêu đề mà không cần gõ tay. Hai thuộc tính này chỉ cần khai báo trong ModelAdmin.

Vấn đề

Mặc định, Django admin hiển thị mọi field của model theo đúng thứ tự khai báo, xếp thành một cột duy nhất. Với model bài viết có nhiều field, trang sửa dài lê thê: field quan trọng như tiêu đề nằm chung với field ít dùng như ngày xuất bản. Tệ hơn, field slug thường phải gõ tay — gõ sai định dạng, trùng slug cũ hoặc quên điền đều gây lỗi khi lưu.

# admin.py — mặc định: mọi field xếp thành một cột dài
from django.contrib import admin
from .models import Article
@admin.register(Article)
class ArticleAdmin(admin.ModelAdmin):
list_display = ("title", "category", "is_published")

Giải pháp

Khai báo fieldsets để chia form thành từng nhóm, và prepopulated_fields để slug tự điền từ tiêu đề ngay khi gõ:

# admin.py
from django.contrib import admin
from .models import Article
@admin.register(Article)
class ArticleAdmin(admin.ModelAdmin):
list_display = ("title", "category", "is_published")
fieldsets = (
(None, {
"fields": ("title", "slug"),
}),
("Nội dung", {
"fields": ("category", "content"),
}),
("Xuất bản", {
"classes": ("collapse",),
"fields": ("is_published", "published_at"),
}),
)
prepopulated_fields = {"slug": ("title",)}

Mỗi tuple trong fieldsets gồm tiêu đề nhóm và dict mô tả: nhóm đầu tiên để tiêu đề None sẽ không hiện tiêu đề riêng. Nhóm “Xuất bản” có "classes": ("collapse",) nên mặc định thu gọn lại — field ít dùng không còn chiếm chỗ. prepopulated_fields nhận dict dạng {"slug": ("title",)}: mỗi khi gõ tiêu đề, JavaScript của admin tự sinh slug theo chuẩn slugify, ví dụ “Bài viết mới” thành bai-viet-moi.

Ba lưu ý khi dùng. Thứ nhất, không khai báo cả fields và fieldsets trong cùng một ModelAdmin — Django sẽ báo lỗi cấu hình. Thứ hai, prepopulated_fields chỉ tự điền khi slug đang trống, nên slug đã lưu không bị ghi đè khi sửa tiêu đề sau này. Thứ ba, slugify mặc định bỏ dấu tiếng Việt, vì vậy slug sinh ra luôn sạch và an toàn cho URL.

Áp dụng vào dự án của bạn

Mở admin.py của app có model nhiều field nhất, nhóm các field theo quy trình nhập liệu thật: thông tin chính lên đầu, nội dung ở giữa, cấu hình và trạng thái cho vào nhóm thu gọn cuối cùng. Thêm prepopulated_fields cho mọi model có SlugField đi kèm trường tên hoặc tiêu đề. Sau khi lưu, mở lại trang sửa và kiểm tra: nhóm ít dùng phải thu gọn, slug phải tự sinh đúng khi tạo mới.

Bài viết liên quan

Câu hỏi thường gặp

fieldsets khác gì so với fields?

fields chỉ liệt kê thứ tự field trong một cột duy nhất, còn fieldsets chia form thành nhiều nhóm có tiêu đề riêng và hỗ trợ class CSS như collapse hay wide. Chỉ cần đổi thứ tự thì dùng fields, muốn gom nhóm và thu gọn thì dùng fieldsets.

prepopulated_fields có ghi đè slug đã lưu không?

Không. JavaScript của admin chỉ điền slug khi ô slug đang trống, thường là lúc tạo bản ghi mới. Khi sửa bản ghi cũ, slug giữ nguyên dù bạn đổi tiêu đề — muốn đổi slug thì sửa tay.

Slug tiếng Việt có dấu thì sao?

slugify mặc định loại bỏ dấu, nên “Bài viết mới” thành bai-viet-moi. Nếu muốn giữ nguyên dấu trong slug, truyền allow_unicode=True cho slugify trong save(), nhưng URL có dấu dễ gây lỗi khi chia sẻ nên mặc định bỏ dấu vẫn an toàn hơn.

Có dùng fieldsets chung với readonly_fields được không?

Được. Field chỉ đọc vẫn khai báo bình thường trong fieldsets, Django hiển thị giá trị của nó thay vì ô nhập. Cách này tiện khi muốn nhóm cả field khóa như ngày tạo vào nhóm thu gọn cuối trang.

💬 Bạn đã thử cách này trong dự án của mình chưa? Nếu gặp lỗi hoặc có cách làm gọn hơn, để lại bình luận bên dưới — mình đọc hết và sẽ bổ sung những ý hay vào bài.

Nguồn tham khảo