🛡️ فصل ۶: پنل ادمین
یکی از قدرتمندترین قابلیتهای جنگو - یه پنل مدیریت کامل که خودکار ساخته میشه!
یکی از قدرتمندترین قابلیتهای جنگو – یه پنل مدیریت کامل که خودکار ساخته میشه!
📑 فهرست این فصل
🎁 پنل ادمین چیه؟
پنل ادمین جنگو یک رابط وب آمادهست که خودکار از روی مدلهات میسازه. میتونی دادهها رو ببینی، اضافه کنی، ویرایش کنی و حذف کنی – بدون نوشتن حتی یک خط HTML!
یکی از دلایلی که جنگو خیلی محبوبه، همین پنل ادمینه. خیلی از فریمورکهای دیگه باید با کلی پلاگین این کار رو بکنی!
👤 ساخت Superuser
قبل از هر چیز، باید یه کاربر مدیر بسازیم:
python manage.py createsuperuser
ازت اسم کاربری، ایمیل و رمز عبور میخواد. بعد به آدرس http://127.0.0.1:8000/admin/ برو و وارد شو!
📝 ثبت مدل در ادمین
سادهترین روش:
from django.contrib import admin
from .models import Product, Category
# ثبت ساده
admin.site.register(Product)
admin.site.register(Category)
این کافیه تا تو پنل ادمین بتونی محصولات و دستهها رو مدیریت کنی!
🎨 سفارشیسازی نمایش
برای کنترل بیشتر، یه کلاس ModelAdmin میسازیم:
from django.contrib import admin
from .models import Product, Category
@admin.register(Product)
class ProductAdmin(admin.ModelAdmin):
# ستونهای لیست
list_display = ['name', 'price', 'stock', 'is_active', 'created_at']
# فیلتر کناری
list_filter = ['is_active', 'category', 'created_at']
# جستجو
search_fields = ['name', 'description']
# ترتیب
ordering = ['-created_at']
# تعداد در هر صفحه
list_per_page = 25
# فیلدهای قابل ویرایش از همون لیست
list_editable = ['price', 'stock', 'is_active']
# لینک به جزئیات
list_display_links = ['name']
# فیلتر تاریخی
date_hierarchy = 'created_at'
# فیلدهای فقط خواندنی
readonly_fields = ['created_at', 'updated_at']
# پر کردن خودکار slug از name
prepopulated_fields = {'slug': ('name',)}
تنظیمات پرکاربرد ModelAdmin
| تنظیم | کاربرد |
|---|---|
list_display |
ستونهای لیست |
list_filter |
فیلتر کناری |
search_fields |
فیلدهای قابل جستجو |
ordering |
ترتیب پیشفرض |
list_per_page |
تعداد در هر صفحه |
list_editable |
فیلدهای قابل ویرایش از لیست |
readonly_fields |
فقط خواندنی |
prepopulated_fields |
پر کردن خودکار |
date_hierarchy |
فیلتر تاریخی بالای صفحه |
raw_id_fields |
برای ForeignKey با تعداد زیاد |
autocomplete_fields |
جستجوی خودکار |
متد سفارشی برای list_display
میتونی متد بنویسی و تو لیست نمایش بدی:
from django.utils.html import format_html
@admin.register(Product)
class ProductAdmin(admin.ModelAdmin):
list_display = ['name', 'price_display', 'image_tag', 'status_display']
def price_display(self, obj):
return f'{obj.price:,.0f} تومان'
price_display.short_description = 'قیمت' # نام ستون
price_display.admin_order_field = 'price' # قابل مرتبسازی
def image_tag(self, obj):
if obj.image:
return format_html(
'<img src="{}" width="50" height="50" />',
obj.image.url
)
return '-'
image_tag.short_description = 'تصویر'
def status_display(self, obj):
if obj.is_active:
color = 'green'
text = 'فعال'
else:
color = 'red'
text = 'غیرفعال'
return format_html(
'<span style="color: {};">●</span> {}',
color, text
)
status_display.short_description = 'وضعیت'
📦 گروهبندی فیلدها
تو صفحه ویرایش، میتونی فیلدها رو گروهبندی کنی:
@admin.register(Product)
class ProductAdmin(admin.ModelAdmin):
fieldsets = (
('اطلاعات اصلی', {
'fields': ('name', 'slug', 'description', 'category')
}),
('قیمت و موجودی', {
'fields': ('price', 'discount_price', 'stock'),
'classes': ('collapse',), # قابل بازشدن
}),
('تصاویر', {
'fields': ('image', 'gallery'),
}),
('وضعیت', {
'fields': ('is_active', 'is_featured'),
'description': 'کنترل نمایش محصول در سایت'
}),
('زمانها', {
'fields': ('created_at', 'updated_at'),
'classes': ('collapse',),
}),
)
readonly_fields = ('created_at', 'updated_at')
🔗 Inline – مدلهای وابسته
اگه مدلی به مدل دیگه ForeignKey داره، میتونی همون صفحه ویرایششون کنی:
from django.contrib import admin
from .models import Order, OrderItem
class OrderItemInline(admin.TabularInline): # یا StackedInline
model = OrderItem
extra = 1 # تعداد ردیف خالی برای اضافه کردن
fields = ['product', 'quantity', 'price']
readonly_fields = ['price']
@admin.register(Order)
class OrderAdmin(admin.ModelAdmin):
list_display = ['id', 'user', 'status', 'total_price', 'created_at']
list_filter = ['status', 'created_at']
search_fields = ['user__username', 'id']
inlines = [OrderItemInline] # ← آیتمهای سفارش رو همینجا نمایش بده
TabularInline: نمایش جدولی، فشرده، برای مواقعی که فیلدها کم هستن
StackedInline: نمایش عمودی، برای فیلدهای زیاد
⚡ اکشنهای سفارشی
عملیات گروهی روی چند رکورد:
@admin.register(Product)
class ProductAdmin(admin.ModelAdmin):
list_display = ['name', 'price', 'is_active']
actions = ['make_active', 'make_inactive', 'apply_discount']
def make_active(self, request, queryset):
count = queryset.update(is_active=True)
self.message_user(request, f'{count} محصول فعال شد')
make_active.short_description = 'فعال کردن انتخابشدهها'
def make_inactive(self, request, queryset):
count = queryset.update(is_active=False)
self.message_user(request, f'{count} محصول غیرفعال شد')
make_inactive.short_description = 'غیرفعال کردن انتخابشدهها'
def apply_discount(self, request, queryset):
for product in queryset:
product.price = product.price * 0.9 # ۱۰٪ تخفیف
product.save()
self.message_user(request, f'{queryset.count()} محصول ۱۰٪ تخفیف خورد')
apply_discount.short_description = 'اعمال ۱۰٪ تخفیف'
🎯 مثالهای کاربردی
مثال ۱: ادمین ساده برای وبلاگ
from django.contrib import admin
from .models import Post, Tag
@admin.register(Post)
class PostAdmin(admin.ModelAdmin):
list_display = ['title', 'author', 'is_published', 'published_at']
list_filter = ['is_published', 'tags']
search_fields = ['title', 'content']
prepopulated_fields = {'slug': ('title',)}
list_editable = ['is_published']
date_hierarchy = 'published_at'
admin.site.register(Tag)
مثال ۲: ادمین کاربر سفارشی با پروفایل
from django.contrib import admin
from django.contrib.auth.admin import UserAdmin
from django.contrib.auth.models import User
from .models import Profile
class ProfileInline(admin.StackedInline):
model = Profile
can_delete = False
fk_name = 'user'
class CustomUserAdmin(UserAdmin):
inlines = (ProfileInline,)
list_display = ['username', 'email', 'get_phone', 'is_staff', 'date_joined']
def get_phone(self, obj):
try:
return obj.profile.phone
except Profile.DoesNotExist:
return '-'
get_phone.short_description = 'تلفن'
# جایگزینی ادمین پیشفرض User
admin.site.unregister(User)
admin.site.register(User, CustomUserAdmin)
مثال ۳: ادمین کامل فروشگاه با کلی قابلیت
from django.contrib import admin
from django.utils.html import format_html
from django.urls import reverse
from django.db.models import Sum, Count
from .models import Order, OrderItem, Product
class OrderItemInline(admin.TabularInline):
model = OrderItem
extra = 0
readonly_fields = ['get_total']
def get_total(self, obj):
return f'{obj.quantity * obj.price:,.0f}'
get_total.short_description = 'جمع'
@admin.register(Order)
class OrderAdmin(admin.ModelAdmin):
list_display = [
'id', 'user_link', 'status_badge',
'total_display', 'items_count', 'created_at'
]
list_filter = ['status', 'created_at']
search_fields = ['user__username', 'user__email', 'id']
readonly_fields = ['created_at', 'total_price']
inlines = [OrderItemInline]
actions = ['mark_paid', 'mark_shipped', 'export_csv']
date_hierarchy = 'created_at'
def user_link(self, obj):
url = reverse('admin:auth_user_change', args=[obj.user.id])
return format_html('<a href="{}">{}</a>', url, obj.user.username)
user_link.short_description = 'کاربر'
def status_badge(self, obj):
colors = {
'pending': '#f59e0b',
'paid': '#3b82f6',
'shipped': '#8b5cf6',
'delivered': '#10b981',
'cancelled': '#ef4444',
}
color = colors.get(obj.status, '#6b7280')
return format_html(
'<span style="background:{}; color:white; padding:3px 10px; border-radius:10px;">{}</span>',
color, obj.get_status_display()
)
status_badge.short_description = 'وضعیت'
def total_display(self, obj):
return f'{obj.total_price:,.0f} تومان'
total_display.short_description = 'مبلغ'
total_display.admin_order_field = 'total_price'
def items_count(self, obj):
return obj.items.count()
items_count.short_description = 'تعداد آیتمها'
def mark_paid(self, request, queryset):
count = queryset.update(status='paid')
self.message_user(request, f'{count} سفارش پرداختشده شد')
mark_paid.short_description = 'تغییر به وضعیت پرداختشده'
def mark_shipped(self, request, queryset):
count = queryset.update(status='shipped')
self.message_user(request, f'{count} سفارش ارسال شد')
mark_shipped.short_description = 'تغییر به وضعیت ارسالشده'
def get_queryset(self, request):
# بهینهسازی کوئری
return super().get_queryset(request).select_related('user').prefetch_related('items')
تنظیمات کلی پنل ادمین
from django.contrib import admin
# عنوان بالای پنل
admin.site.site_header = 'پنل مدیریت ICSD'
admin.site.site_title = 'ICSD Admin'
admin.site.index_title = 'به پنل مدیریت خوش آمدید'
📝 تمرینها
تمرین ۱
مدل Book رو با list_display شامل عنوان، نویسنده، قیمت ثبت کن. اضافه کن که قیمت قابل جستجو باشه.
تمرین ۲
یه ادمین برای Order بساز که OrderItemها بهصورت Inline نمایش داده بشن. ستون مجموع قیمت رو با متد سفارشی نشون بده.
تمرین ۳
یه اکشن سفارشی بنویس که محصولات انتخابشده رو در یک فایل CSV خروجی بده.