~/icsd.ir — bash
SYSTEM_ONLINE

🛡️ فصل ۶: پنل ادمین

یکی از قدرتمندترین قابلیت‌های جنگو - یه پنل مدیریت کامل که خودکار ساخته میشه!

یکی از قدرتمندترین قابلیت‌های جنگو – یه پنل مدیریت کامل که خودکار ساخته میشه!

🎁 پنل ادمین چیه؟

پنل ادمین جنگو یک رابط وب آماده‌ست که خودکار از روی مدل‌هات می‌سازه. می‌تونی داده‌ها رو ببینی، اضافه کنی، ویرایش کنی و حذف کنی – بدون نوشتن حتی یک خط HTML!

💎

شعار جنگو

یکی از دلایلی که جنگو خیلی محبوبه، همین پنل ادمینه. خیلی از فریم‌ورک‌های دیگه باید با کلی پلاگین این کار رو بکنی!

👤 ساخت Superuser

قبل از هر چیز، باید یه کاربر مدیر بسازیم:

📟 ترمینال
python manage.py createsuperuser

ازت اسم کاربری، ایمیل و رمز عبور می‌خواد. بعد به آدرس http://127.0.0.1:8000/admin/ برو و وارد شو!

📝 ثبت مدل در ادمین

ساده‌ترین روش:

products/admin.py
from django.contrib import admin
from .models import Product, Category

# ثبت ساده
admin.site.register(Product)
admin.site.register(Category)

این کافیه تا تو پنل ادمین بتونی محصولات و دسته‌ها رو مدیریت کنی!

🎨 سفارشی‌سازی نمایش

برای کنترل بیشتر، یه کلاس ModelAdmin می‌سازیم:

products/admin.py
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

می‌تونی متد بنویسی و تو لیست نمایش بدی:

products/admin.py
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 = 'وضعیت'

📦 گروه‌بندی فیلدها

تو صفحه ویرایش، می‌تونی فیلدها رو گروه‌بندی کنی:

products/admin.py
@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 داره، می‌تونی همون صفحه ویرایششون کنی:

orders/admin.py
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؟

TabularInline: نمایش جدولی، فشرده، برای مواقعی که فیلدها کم هستن
StackedInline: نمایش عمودی، برای فیلدهای زیاد

⚡ اکشن‌های سفارشی

عملیات گروهی روی چند رکورد:

products/admin.py
@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 = 'اعمال ۱۰٪ تخفیف'

🎯 مثال‌های کاربردی

آسان

مثال ۱: ادمین ساده برای وبلاگ

blog/admin.py
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)

متوسط

مثال ۲: ادمین کاربر سفارشی با پروفایل

accounts/admin.py
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)

پیشرفته

مثال ۳: ادمین کامل فروشگاه با کلی قابلیت

shop/admin.py
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')

تنظیمات کلی پنل ادمین

myproject/urls.py یا admin.py
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 خروجی بده.

نمایش سایت

رنگ سایت
حالت نمایش
اندازهٔ متن
خوانایی

این تنظیمات فقط روی مرورگر شما ذخیره می‌شود.