การปรับแต่ง Django Admin 2026: Actions, Filters และคำถามสัมภาษณ์งาน
เรียนรู้การปรับแต่ง Django admin ด้วย custom actions, list filters, fieldsets และ inlines สำหรับ Django 6 พร้อมคำถามสัมภาษณ์งานที่พบบ่อย

การปรับแต่ง Django admin เปลี่ยนอินเทอร์เฟซ admin ที่มีมาให้จากเครื่องมือ scaffolding ธรรมดาให้กลายเป็น back office ที่ออกแบบมาเฉพาะสำหรับแอปพลิเคชัน production Django 6 มาพร้อมฟีเจอร์ admin ที่ได้รับการปรับปรุง รวมถึง facet counts สำหรับ filters, form เปลี่ยนรหัสผ่านที่ปรับแต่งได้ และไอคอน Font Awesome ทำให้ admin ที่กำหนดค่าอย่างดีเป็นเครื่องมือที่ใช้งานได้อย่างสะดวกสบาย
ModelAdmin คือคลาสหลักสำหรับการปรับแต่ง admin ทั้งหมด ควรเชี่ยวชาญ list_display, list_filter, search_fields, fieldsets และ custom actions เพื่อจัดการกับ 90% ของความต้องการในการกำหนดค่า admin
พื้นฐาน ModelAdmin: list_display และ search_fields
list_display ควบคุมว่าคอลัมน์ใดจะแสดงบนหน้า change list นอกจากชื่อ field แบบธรรมดาแล้ว ยังรับ callable ที่ถูก decorate ด้วย @admin.display() สำหรับค่าที่คำนวณ search_fields เปิดใช้งานกล่องค้นหาที่กรอง record โดยใช้ icontains เป็นค่าเริ่มต้น
# admin.py
from django.contrib import admin
from django.utils.html import format_html
from .models import Article
class ArticleAdmin(admin.ModelAdmin):
list_display = ["title", "author_name", "status", "publish_date", "is_recent"]
search_fields = ["title", "author__email", "content"]
list_per_page = 50
list_select_related = ["author"] # Avoid N+1 queries
@admin.display(description="Author", ordering="author__last_name")
def author_name(self, obj):
return f"{obj.author.first_name} {obj.author.last_name}"
@admin.display(description="Recent?", boolean=True)
def is_recent(self, obj):
from django.utils import timezone
return obj.publish_date >= timezone.now() - timezone.timedelta(days=7)
admin.site.register(Article, ArticleAdmin)Decorator @admin.display() แทนที่รูปแบบเก่าของการตั้ง short_description เป็น attribute ของฟังก์ชัน พารามิเตอร์ boolean=True แสดงผลเป็นไอคอนเครื่องหมายถูกหรือ X แทนข้อความ True/False การใช้ list_select_related ป้องกัน query N+1 เมื่อเข้าถึง field ของ foreign key ใน list_display
Custom list_filter ด้วย SimpleListFilter
list_filter เปิดใช้งาน filter ที่ sidebar บนหน้า change list ชื่อ field ทำงานได้สำหรับการกรองพื้นฐาน แต่ SimpleListFilter จัดการกับ logic การกรองที่ซับซ้อนซึ่งครอบคลุมหลาย field หรือต้องการ query เฉพาะ
# admin.py
from datetime import date, timedelta
from django.contrib import admin
from django.utils.translation import gettext_lazy as _
from .models import Article
class PublishDateFilter(admin.SimpleListFilter):
title = _("publish period")
parameter_name = "published" # URL parameter: ?published=this_week
def lookups(self, request, model_admin):
# Returns (value, label) tuples for the filter sidebar
return [
("this_week", _("This week")),
("this_month", _("This month")),
("this_year", _("This year")),
("unpublished", _("Not published")),
]
def queryset(self, request, queryset):
# Filter the queryset based on self.value()
today = date.today()
if self.value() == "this_week":
return queryset.filter(publish_date__gte=today - timedelta(days=7))
if self.value() == "this_month":
return queryset.filter(
publish_date__year=today.year, publish_date__month=today.month
)
if self.value() == "this_year":
return queryset.filter(publish_date__year=today.year)
if self.value() == "unpublished":
return queryset.filter(publish_date__isnull=True)
return queryset
class ArticleAdmin(admin.ModelAdmin):
list_filter = [
"status",
PublishDateFilter,
("author", admin.RelatedOnlyFieldListFilter), # Show only authors with articles
]RelatedOnlyFieldListFilter จำกัด dropdown ของ author ให้แสดงเฉพาะ author ที่มีบทความจริงๆ แทนที่จะแสดงรายการ user ทั้งหมดใน database สิ่งนี้ปรับปรุงการใช้งานเมื่อ model ที่เกี่ยวข้องมี record จำนวนมาก
Django 6 Facets: จำนวน Filter ใน Sidebar
Django 6 แนะนำ facet counts ที่แสดงจำนวน object ที่ตรงกันข้างตัวเลือก filter แต่ละตัว Attribute show_facets ควบคุมพฤติกรรมนี้
# admin.py
from django.contrib import admin
from django.contrib.admin import ShowFacets
from .models import Article
class ArticleAdmin(admin.ModelAdmin):
list_display = ["title", "status", "category"]
list_filter = ["status", "category", "author"]
show_facets = ShowFacets.ALWAYS # Always display counts
# Alternative values:
# ShowFacets.ALLOW - Show counts when ?_facets=1 in URL
# ShowFacets.NEVER - Never show counts (for large datasets)Facet counts รัน query COUNT เพิ่มเติมสำหรับตัวเลือก filter แต่ละตัว บนตารางที่มีหลายล้านแถว ให้ตั้ง show_facets = ShowFacets.NEVER เพื่อหลีกเลี่ยงการลดประสิทธิภาพ จำนวนจะอัปเดตแบบไดนามิกเมื่อมีการใช้ filter แสดงว่ามี record กี่รายการที่ตรงกับชุด filter ปัจจุบัน
Custom Admin Actions สำหรับการดำเนินการแบบกลุ่ม
Admin actions ดำเนินการแบบกลุ่มบน object ที่เลือก Decorator @admin.action กำหนด permissions และคำอธิบายที่แสดงใน dropdown
# admin.py
from django.contrib import admin
from django.contrib import messages
from django.utils.translation import ngettext
from .models import Article
@admin.action(description="Publish selected articles", permissions=["change"])
def publish_articles(modeladmin, request, queryset):
# Exclude already published articles
unpublished = queryset.exclude(status="published")
updated = unpublished.update(status="published")
modeladmin.message_user(
request,
ngettext(
"%d article was published.",
"%d articles were published.",
updated,
)
% updated,
messages.SUCCESS,
)
@admin.action(description="Export selected as JSON")
def export_as_json(modeladmin, request, queryset):
from django.http import HttpResponse
from django.core import serializers
response = HttpResponse(content_type="application/json")
response["Content-Disposition"] = 'attachment; filename="articles.json"'
serializers.serialize("json", queryset, stream=response)
return response # Returning HttpResponse triggers file download
class ArticleAdmin(admin.ModelAdmin):
actions = [publish_articles, export_as_json]
def get_actions(self, request):
# Conditionally remove actions based on user permissions
actions = super().get_actions(request)
if not request.user.has_perm("articles.delete_article"):
del actions["delete_selected"]
return actionsการ return HttpResponse จาก action จะเริ่มการดาวน์โหลดไฟล์หรือ redirect ไปยังหน้ายืนยัน พารามิเตอร์ permissions จำกัด action เฉพาะผู้ใช้ที่มี permissions ของ model ที่กำหนด Override get_actions() เพื่อแสดงหรือซ่อน actions แบบไดนามิกตาม request ปัจจุบัน
พร้อมที่จะพิชิตการสัมภาษณ์ Django แล้วหรือยังครับ?
ฝึกฝนด้วยตัวจำลองแบบโต้ตอบ, flashcards และแบบทดสอบเทคนิคครับ
Fieldsets และ Inline Models สำหรับฟอร์มที่มีระเบียบ
fieldsets จัดระเบียบฟอร์ม add และ change เป็น section ที่ย่อได้ TabularInline และ StackedInline ฝัง model ที่เกี่ยวข้องโดยตรงในฟอร์มหลัก ลดการนำทางระหว่างหน้า
# admin.py
from django.contrib import admin
from .models import Article, ArticleImage, ArticleComment
class ArticleImageInline(admin.TabularInline):
model = ArticleImage
extra = 1 # Number of empty forms to display
max_num = 5 # Maximum images per article
fields = ["image", "caption", "order"]
class ArticleCommentInline(admin.StackedInline):
model = ArticleComment
extra = 0
readonly_fields = ["created_at", "author"]
can_delete = True
class ArticleAdmin(admin.ModelAdmin):
fieldsets = [
(
None,
{
"fields": ["title", "slug", "author"],
},
),
(
"Content",
{
"fields": ["content", "excerpt"],
"classes": ["wide"], # Full-width fields
},
),
(
"Publishing",
{
"fields": ["status", "publish_date", "category", "tags"],
"classes": ["collapse"], # Collapsed by default
},
),
(
"SEO",
{
"fields": ["meta_title", "meta_description"],
"classes": ["collapse"],
"description": "Search engine optimization fields",
},
),
]
inlines = [ArticleImageInline, ArticleCommentInline]
prepopulated_fields = {"slug": ["title"]} # Auto-generate slug from title
autocomplete_fields = ["author", "category"] # AJAX-powered select
filter_horizontal = ["tags"] # Widget for M2M selection
admin.site.register(Article, ArticleAdmin)TabularInline แสดง object ที่เกี่ยวข้องในรูปแบบตารางกะทัดรัด เหมาะสำหรับ model ง่ายๆ ที่มี field น้อย StackedInline แสดง object ที่เกี่ยวข้องแต่ละตัวเป็นบล็อกฟอร์มแยก ดีกว่าสำหรับ model ที่มีหลาย field ตัวเลือก autocomplete_fields แทนที่ dropdown เริ่มต้นด้วย widget ค้นหา AJAX จำเป็นเมื่อ model ที่เกี่ยวข้องมี record หลายพันรายการ
การปรับแต่ง Admin Site
การสร้าง subclass ของ AdminSite อนุญาตให้มีอินเทอร์เฟซ admin หลายตัวที่มี branding, permissions หรือการลงทะเบียน model ที่แตกต่างกัน Django 6 เพิ่มการปรับแต่ง password_change_form สำหรับนโยบายรหัสผ่านที่เข้มงวดขึ้น
# admin.py
from django.contrib import admin
from django.contrib.admin import AdminSite
from .models import Article, Category
class ContentAdminSite(AdminSite):
site_header = "Content Management"
site_title = "CMS"
index_title = "Dashboard"
def has_permission(self, request):
# Restrict to staff in the 'editors' group
return (
request.user.is_active
and request.user.is_staff
and request.user.groups.filter(name="editors").exists()
)
content_admin = ContentAdminSite(name="content_admin")
content_admin.register(Article, ArticleAdmin)
content_admin.register(Category)
# In urls.py:
# path('content-admin/', content_admin.urls),แต่ละ instance ของ AdminSite มี namespace URL ของตัวเอง หน้าเข้าสู่ระบบ และชุดของ model ที่ลงทะเบียน รูปแบบนี้เหมาะสำหรับแอปพลิเคชันที่มี role ผู้ใช้ที่แตกต่างกัน ซึ่ง editor ไม่ควรเห็นการจัดการผู้ใช้หรือการตั้งค่าระบบ
คำถามสัมภาษณ์: การปรับแต่ง Django Admin
คำถามเหล่านี้มักปรากฏในการสัมภาษณ์เชิงเทคนิคของ Django การเข้าใจกลไกพื้นฐาน ไม่ใช่แค่ syntax แยกความแตกต่างระหว่างผู้สมัครระดับกลางกับ engineer อาวุโส
ถาม: จะเพิ่มคอลัมน์ที่คำนวณให้ list_display ที่ไม่สามารถเรียงลำดับได้อย่างไร?
Decorator @admin.display() รับพารามิเตอร์ ordering ที่ระบุว่า field database ใดที่ใช้สำหรับการเรียงลำดับ หากไม่มี header ของคอลัมน์จะไม่สามารถคลิกได้ ค่าที่คำนวณซึ่งรวมหลาย record หรือเรียกบริการภายนอกไม่สามารถเรียงลำดับได้เพราะไม่มีคอลัมน์ database พื้นฐานสำหรับ order
ถาม: ผลกระทบต่อประสิทธิภาพของ facet counts คืออะไร และเมื่อใดควรปิดใช้งาน?
Facets รัน query COUNT หนึ่งตัวต่อตัวเลือก filter คูณด้วยจำนวน filter ที่ใช้งานอยู่ บนตารางที่มี 10 ล้านแถวและ 5 ตัวเลือก filter แต่ละตัวบน 3 field นั่นคือ 15 query COUNT เพิ่มเติมต่อการโหลดหน้าหนึ่งครั้ง ตั้ง show_facets = ShowFacets.NEVER เมื่อ filter กำหนดเป้าหมายตารางที่มีมากกว่า 100,000 แถวโดยไม่มี index ที่เหมาะสม
ถาม: admin actions แตกต่างจาก custom admin views อย่างไร?
Actions ทำงานบน queryset ของ object ที่เลือกและรวมเข้ากับ checkbox การเลือกของ change list Custom admin views เป็นหน้าแบบ standalone ที่เข้าถึงผ่าน URL เฉพาะ เหมาะสำหรับ reports, imports หรือ dashboards ที่ไม่ทำงานบน record ที่เลือก Actions ไม่ควรดำเนินการที่ใช้เวลานานเพราะจะบล็อก request
ถาม: ทำไมต้องใช้ RelatedOnlyFieldListFilter แทน filter เริ่มต้น?
RelatedFieldListFilter เริ่มต้น query object ทั้งหมดในตารางที่เกี่ยวข้อง เมื่อ ForeignKey ชี้ไปยัง model User ที่มี 100,000 record dropdown ของ filter โหลดตัวเลือกทั้ง 100,000 ทำให้เบราว์เซอร์ค้าง RelatedOnlyFieldListFilter query เฉพาะค่าที่มีอยู่ใน record ของ model ปัจจุบัน ซึ่งโดยปกติเป็นชุดที่เล็กกว่ามาก
ข้อผิดพลาดทั่วไปที่ควรหลีกเลี่ยงใน Production
หลายรูปแบบที่ทำงานได้ใน development ทำให้เกิดปัญหาเมื่อ scale
# admin.py
from django.contrib import admin
from .models import Order
class OrderAdmin(admin.ModelAdmin):
# Mistake: Accessing related fields without list_select_related
# This causes N+1 queries, one per row in the list view
list_display = ["id", "customer_email", "total"]
# Fix: Prefetch related objects
list_select_related = ["customer"]
def customer_email(self, obj):
return obj.customer.email
# Mistake: search_fields on unindexed large text columns
# search_fields = ["notes"] # Slow on large tables
# Fix: Limit search to indexed fields
search_fields = ["id", "customer__email"]
# Mistake: Expensive computation in list_display
# def order_total(self, obj):
# return sum(item.price * item.qty for item in obj.items.all()) # N+1
# Fix: Use annotation in get_queryset
def get_queryset(self, request):
from django.db.models import Sum, F
qs = super().get_queryset(request)
return qs.annotate(computed_total=Sum(F("items__price") * F("items__qty")))
@admin.display(description="Total", ordering="computed_total")
def total(self, obj):
return obj.computed_totalการ override get_queryset() เพื่อเพิ่ม annotations รักษาค่าที่คำนวณไว้ใน query เดียวแทนที่จะเข้าถึง database หนึ่งครั้งต่อแถว ใช้ Django Debug Toolbar เพื่อ profile admin และจับ query N+1 ก่อนที่จะถึง production
ธีม Admin จากบุคคลที่สาม: Django Unfold
Django Unfold เป็นธีม admin ที่ได้รับการดูแลอย่างแข็งขันที่สุดสำหรับ Django 6 สร้างด้วย Tailwind CSS มันขยายแทนที่จะแทนที่ admin มาตรฐาน ดังนั้นการกำหนดค่า ModelAdmin ที่มีอยู่จะยังคงทำงานต่อไป
# settings.py
INSTALLED_APPS = [
"unfold", # Must come before django.contrib.admin
"unfold.contrib.filters", # Advanced filter widgets
"unfold.contrib.forms", # Enhanced form widgets
"django.contrib.admin",
# ...
]Unfold เพิ่ม dark mode, layouts ที่ตอบสนอง, การนำทาง sidebar และประเภท filter ขั้นสูงเช่น RangeNumericFilter และ RangeDateFilter การรวม HTMX และ Alpine.js ให้การโต้ตอบโดยไม่มี overhead ของ framework SPA เต็มรูปแบบ สำหรับทีมที่ต้องการ admin ที่ขัดเกลาโดยไม่ต้องสร้าง dashboard เอง Unfold ประหยัดเวลาหลายสัปดาห์ของงาน frontend
เริ่มฝึกซ้อมเลย!
ทดสอบความรู้ของคุณด้วยตัวจำลองสัมภาษณ์และแบบทดสอบเทคนิคครับ
สิ่งที่ควรจำเกี่ยวกับการปรับแต่ง Django Admin
list_displayด้วย@admin.display()ควบคุมคอลัมน์; เพิ่มorderingสำหรับค่าที่คำนวณที่สามารถเรียงลำดับได้SimpleListFilterจัดการ logic filter เฉพาะที่ครอบคลุมหลาย field หรือต้องการช่วงวันที่- Django 6 facets แสดงจำนวน filter; ปิดด้วย
ShowFacets.NEVERบนตารางขนาดใหญ่เพื่อหลีกเลี่ยง overhead ของ query COUNT - Admin actions ด้วย
@admin.action(permissions=[...])จัดการการดำเนินการแบบกลุ่ม; returnHttpResponseสำหรับการส่งออกไฟล์ - ใช้
list_select_relatedและ queryset ที่มี annotations ในget_queryset()เพื่อป้องกัน query N+1 ใน list views RelatedOnlyFieldListFilterป้องกันเบราว์เซอร์ค้างเมื่อกรองตาม ForeignKey ไปยังตารางขนาดใหญ่- ฝึกฝนรูปแบบเหล่านี้ด้วย คำถามสัมภาษณ์ Django ที่กำหนดเป้าหมายการกำหนดค่า admin โดยเฉพาะ
คุณหาบั๊กใน Django เจอไหม
โค้ดจริงหนึ่งชิ้น บั๊กที่ซ่อนอยู่หนึ่งจุด วันละหนึ่งครั้ง ลองได้โดยไม่ต้องมีบัญชี

เขียนโดย
Anthony Fillion-Mailletผู้ก่อตั้ง SharpSkill
เป็นนักพัฒนาฟูลสแตกมากว่า 10 ปี ดูแล SharpSkill และรับผิดชอบทุกสิ่งที่เผยแพร่ที่นี่
อัปเดตเมื่อ 13 กันยายน 2569
แชร์
บทความที่เกี่ยวข้อง

Django Middleware ขั้นสูง 2026: Custom Middleware, Logging และคำถามสัมภาษณ์งาน
คู่มือฉบับสมบูรณ์เกี่ยวกับ Django middleware ในปี 2026 ครอบคลุมการสร้าง custom middleware การใช้งาน logging อย่างมีประสิทธิภาพ และคำถามสัมภาษณ์ที่พบบ่อยสำหรับนักพัฒนา Django ระดับ senior

Django Signals vs Celery Tasks 2026: เมื่อไหร่ควรใช้อะไร
เรียนรู้ว่าเมื่อไหร่ควรใช้ Django signals เทียบกับ Celery tasks ครอบคลุมการจัดการ event แบบ synchronous vs asynchronous ผลกระทบด้านประสิทธิภาพ และคำถามสัมภาษณ์สำหรับนักพัฒนา Django

Django Channels 2026: WebSockets, Real-Time และคำถามสัมภาษณ์
เชี่ยวชาญ Django Channels 4.x กับ WebSocket consumers, Redis channel layers และคำถามสัมภาษณ์ที่พบบ่อย ตัวอย่างโค้ดพร้อมใช้งาน production พร้อมการ deploy ASGI