ادمین جنگو؛ پنل اداری رایگان
ادمین جنگو برای کارمندان داخلی ساخته شده، نه مشتریان. با چند خط تنظیم، واحد فروش میتواند سفارشها را جستوجو، فیلتر و ویرایش کند و شما هفتهها زمان ساخت پنل را ذخیره میکنید. اما ادمین پیشفرض روی جدولهای بزرگ کند و بیاستفاده است؛ شخصیسازی درست تفاوت را میسازد.
# apps/orders/admin.py
from django.contrib import admin
from django.db.models import Count
from apps.core.templatetags.persian import jdate, toman
from .models import Order
@admin.register(Order)
class OrderAdmin(admin.ModelAdmin):
list_display = ("tracking_code", "customer", "status", "total_display", "items_count", "created_jalali")
list_display_links = ("tracking_code",)
list_filter = ("status", "customer__city", ("created_at", admin.DateFieldListFilter))
search_fields = ("=tracking_code", "customer__full_name", "^customer__user__mobile")
list_select_related = ("customer",)
list_per_page = 50
readonly_fields = ("tracking_code", "total", "created_at")
ordering = ("-created_at",)
show_facets = admin.ShowFacets.ALWAYS # تعداد کنار هر فیلتر (Django 5.0+)
fieldsets = (
("اطلاعات سفارش", {"fields": ("tracking_code", "customer", "status")}),
("مالی", {"fields": ("total", "discount")}),
("زمانها", {"fields": ("delivery_date", "created_at"), "classes": ("collapse",)}),
)
def get_queryset(self, request):
return super().get_queryset(request).annotate(_items=Count("items"))
@admin.display(description="مبلغ", ordering="total")
def total_display(self, obj):
return toman(obj.total)
@admin.display(description="تعداد اقلام", ordering="_items")
def items_count(self, obj):
return obj._items
@admin.display(description="تاریخ ثبت", ordering="created_at")
def created_jalali(self, obj):
return jdate(obj.created_at, "%Y/%m/%d %H:%M")
چه چیزی چه میکند
| گزینه | اثر |
|---|---|
| list_display | ستونهای فهرست؛ فیلد، متد یا تابع |
| list_filter | فیلترهای کناری؛ روی رابطهها هم با __ |
| search_fields | کادر جستوجو؛ پیشوند = تطابق دقیق، ^ شروعشدن |
| list_select_related | جلوگیری از N+1 برای ستونهای ForeignKey |
| readonly_fields | نمایش بدون امکان ویرایش؛ متدها را هم میپذیرد |
| fieldsets | گروهبندی فرم ویرایش؛ collapse برای بخش جمعشونده |
ستون محاسبهشده بدون N+1
اگر در items_count بنویسید obj.items.count()، برای هر ردیف یک کوئری میزند: پنجاه ردیف یعنی پنجاه کوئری. با annotate در get_queryset همه در یک کوئری حساب میشود و ordering="_items" ستون را قابل مرتبسازی هم میکند.
نکتههایی که کمتر کسی میداند
search_fieldsبدون پیشوند ازicontainsروی همهی فیلدها با OR استفاده میکند؛ روی جدول میلیونی کند است. برای کد و موبایل=یا^بگذارید تا ایندکس استفاده شود.- برای جدولهای خیلی بزرگ،
show_full_result_count = Falseکوئری COUNT کامل «از ۳٬۲۰۰٬۰۰۰» را حذف میکند و صفحهی جستوجو چند برابر سریعتر میشود. date_hierarchyادمین بر اساس ماههای میلادی کار میکند؛ برای کاربر ایرانی یکSimpleListFilterسفارشی با بازههای «امروز، این هفته، این ماه شمسی» مفیدتر است.list_editableویرایش درجا در فهرست را ممکن میکند، اما هر فیلد list_editable باید در list_display باشد و نمیتواند اولین ستون (لینک) باشد.- عنوانهای ادمین را با
admin.site.site_header،site_titleوindex_titleفارسی کنید؛ یک خط در admin.py هر اپ یا در urls.