Admin là một trong những thứ "miễn phí" giá trị nhất của Django — vừa migrate xong, non-dev đã có chỗ nhập liệu và audit.
Customize bằng cách viết ModelAdmin class rồi register cho mỗi model.
from django.contrib import admin
class OrderItemInline(admin.TabularInline): # child rows inline
model = OrderItem
extra = 0
@admin.register(Order)
class OrderAdmin(admin.ModelAdmin):
list_display = ('id', 'customer', 'total', 'status', 'created_at')
list_filter = ('status', 'created_at') # sidebar filter
search_fields = ('customer__email', 'id')
date_hierarchy = 'created_at' # drilldown theo ngày
readonly_fields = ('created_at', 'total')
inlines = [OrderItemInline]
actions = ['mark_as_shipped']
@admin.action(description='Mark selected as shipped')
def mark_as_shipped(self, request, queryset):
updated = queryset.update(status='shipped')
self.message_user(request, f'{updated} orders shipped')
def get_queryset(self, request):
# N+1 fix cho list page
return super().get_queryset(request).select_related('customer')Mấy mảnh hay dùng: list_display chọn cột hiện trên list page (method def total_display(self, obj): ... cũng đặt được); list_filter cho sidebar filter (status, date); search_fields tạo search bar, traverse FK bằng __; readonly_fields cho field view-only; inlines để edit child model cùng parent; actions cho bulk action trên row đã chọn.
Admin rất dễ chậm với bảng to: list page query toàn bộ kèm count(*) cho pagination. Phải override get_queryset() để select_related(...) và set list_per_page giới hạn. Đừng dựa vào admin cho workflow non-dev quan trọng — admin thiếu approval flow và audit chi tiết. Nghiệp vụ phức tạp nên build app admin riêng (workspace này có luyenphongvan-admin chính vì lý do đó).
Admin is one of Django's most valuable "free" features — non-devs can enter data and audit right after migrate.
Customize via a ModelAdmin class registered per model.
from django.contrib import admin
class OrderItemInline(admin.TabularInline): # inline child rows
model = OrderItem
extra = 0
@admin.register(Order)
class OrderAdmin(admin.ModelAdmin):
list_display = ('id', 'customer', 'total', 'status', 'created_at')
list_filter = ('status', 'created_at') # sidebar filter
search_fields = ('customer__email', 'id')
date_hierarchy = 'created_at' # drill down by date
readonly_fields = ('created_at', 'total')
inlines = [OrderItemInline]
actions = ['mark_as_shipped']
@admin.action(description='Mark selected as shipped')
def mark_as_shipped(self, request, queryset):
updated = queryset.update(status='shipped')
self.message_user(request, f'{updated} orders shipped')
def get_queryset(self, request):
# N+1 fix for the list page
return super().get_queryset(request).select_related('customer')Common pieces:
- list_display — columns on the list page; def total_display(self, obj): ... methods also work.
- list_filter — sidebar filters (status, date).
- search_fields — search bar; use __ to traverse FKs.
- readonly_fields — view-only fields.
- inlines — edit child models alongside the parent (Order + OrderItem).
- actions — bulk actions on selected rows.
Pitfall: Admin easily gets slow on big tables: the list page queries everything plus count(*) for pagination. Always get_queryset().select_related(...) and set list_per_page. And do not lean on admin for serious non-dev workflows — admin lacks approval flow / detailed audit; build a dedicated admin app for complex business (this workspace has a separate luyenphongvan-admin for that reason).