每天40分玩转Django:Django管理界面

Django管理界面

一、今日学习内容概述

学习模块 重要程度 预计学时 主要内容
基础管理配置 ⭐⭐⭐⭐⭐ 1小时 ModelAdmin基本配置
自定义管理界面 ⭐⭐⭐⭐⭐ 2小时 界面定制、动作添加
内联模型管理 ⭐⭐⭐⭐ 1.5小时 内联显示、关联编辑
高级定制功能 ⭐⭐⭐⭐ 1.5小时 权限控制、界面美化

二、基础模型定义

python 复制代码
# models.py
from django.db import models
from django.contrib.auth.models import User

class Category(models.Model):
    name = models.CharField('分类名称', max_length=100)
    description = models.TextField('描述', blank=True)
    created_at = models.DateTimeField('创建时间', auto_now_add=True)

    class Meta:
        verbose_name = '分类'
        verbose_name_plural = verbose_name

    def __str__(self):
        return self.name

class Product(models.Model):
    STATUS_CHOICES = [
        ('draft', '草稿'),
        ('published', '已发布'),
        ('archived', '已归档'),
    ]
    
    name = models.CharField('商品名称', max_length=200)
    category = models.ForeignKey(
        Category, 
        on_delete=models.CASCADE,
        related_name='products',
        verbose_name='分类'
    )
    price = models.DecimalField('价格', max_digits=10, decimal_places=2)
    stock = models.PositiveIntegerField('库存')
    description = models.TextField('描述')
    status = models.CharField(
        '状态',
        max_length=10,
        choices=STATUS_CHOICES,
        default='draft'
    )
    created_by = models.ForeignKey(
        User,
        on_delete=models.CASCADE,
        verbose_name='创建者'
    )
    created_at = models.DateTimeField('创建时间', auto_now_add=True)
    updated_at = models.DateTimeField('更新时间', auto_now=True)

    class Meta:
        verbose_name = '商品'
        verbose_name_plural = verbose_name

    def __str__(self):
        return self.name

class ProductImage(models.Model):
    product = models.ForeignKey(
        Product,
        on_delete=models.CASCADE,
        related_name='images',
        verbose_name='商品'
    )
    image = models.ImageField('图片', upload_to='products/')
    is_primary = models.BooleanField('是否主图', default=False)
    created_at = models.DateTimeField('创建时间', auto_now_add=True)

    class Meta:
        verbose_name = '商品图片'
        verbose_name_plural = verbose_name

三、自定义管理界面

3.1 基础管理类

python 复制代码
# admin.py
from django.contrib import admin
from django.utils.html import format_html
from .models import Category, Product, ProductImage

@admin.register(Category)
class CategoryAdmin(admin.ModelAdmin):
    list_display = ('name', 'description', 'products_count', 'created_at')
    search_fields = ('name',)
    list_per_page = 20
    
    def products_count(self, obj):
        return obj.products.count()
    products_count.short_description = '商品数量'

class ProductImageInline(admin.TabularInline):
    model = ProductImage
    extra = 1
    fields = ('image', 'is_primary')

@admin.register(Product)
class ProductAdmin(admin.ModelAdmin):
    # 列表页配置
    list_display = ('name', 'category', 'price', 'stock', 'status', 
                   'created_by', 'created_at', 'get_image')
    list_filter = ('status', 'category', 'created_at')
    search_fields = ('name', 'description')
    list_editable = ('price', 'stock', 'status')
    list_per_page = 20
    
    # 详情页配置
    fieldsets = (
        ('基本信息', {
            'fields': ('name', 'category', 'price', 'stock')
        }),
        ('详细描述', {
            'fields': ('description',),
            'classes': ('collapse',)
        }),
        ('状态信息', {
            'fields': ('status', 'created_by')
        })
    )
    
    # 内联显示图片
    inlines = [ProductImageInline]
    
    def get_image(self, obj):
        primary_image = obj.images.filter(is_primary=True).first()
        if primary_image:
            return format_html(
                '<img src="{}" width="50" height="50" />',
                primary_image.image.url
            )
        return '无图片'
    get_image.short_description = '主图'
    
    def save_model(self, request, obj, form, change):
        if not change:  # 新建商品时
            obj.created_by = request.user
        super().save_model(request, obj, form, change)
        
    def get_queryset(self, request):
        qs = super().get_queryset(request)
        if request.user.is_superuser:
            return qs
        return qs.filter(created_by=request.user)

3.2 自定义动作

python 复制代码
# admin.py
from django.contrib import admin, messages
from django.http import HttpResponse
import csv

@admin.register(Product)
class ProductAdmin(admin.ModelAdmin):
    actions = ['export_as_csv', 'publish_products', 'archive_products']
    
    def export_as_csv(self, request, queryset):
        meta = self.model._meta
        field_names = [field.name for field in meta.fields]
        
        response = HttpResponse(content_type='text/csv')
        response['Content-Disposition'] = 'attachment; filename={}.csv'.format(meta)
        
        writer = csv.writer(response)
        writer.writerow(field_names)
        for obj in queryset:
            writer.writerow([getattr(obj, field) for field in field_names])
            
        return response
    export_as_csv.short_description = '导出所选商品为CSV'
    
    def publish_products(self, request, queryset):
        updated = queryset.update(status='published')
        self.message_user(
            request,
            f'成功发布 {updated} 个商品',
            messages.SUCCESS
        )
    publish_products.short_description = '发布所选商品'
    
    def archive_products(self, request, queryset):
        updated = queryset.update(status='archived')
        self.message_user(
            request,
            f'成功归档 {updated} 个商品',
            messages.SUCCESS
        )
    archive_products.short_description = '归档所选商品'

四、自定义表单

python 复制代码
# forms.py
from django import forms
from .models import Product

class ProductAdminForm(forms.ModelForm):
    class Meta:
        model = Product
        fields = '__all__'
        
    def clean_price(self):
        price = self.cleaned_data.get('price')
        if price and price < 0:
            raise forms.ValidationError('价格不能为负数')
        return price

# admin.py中使用自定义表单
@admin.register(Product)
class ProductAdmin(admin.ModelAdmin):
    form = ProductAdminForm

五、管理界面流程图

六、高级定制功能

6.1 自定义后台主题

python 复制代码
# admin.py
admin.site.site_header = '商品管理系统'
admin.site.site_title = '商品管理'
admin.site.index_title = '欢迎使用商品管理系统'

# templates/admin/base_site.html
{% extends "admin/base.html" %}
{% load static %}

{% block extrastyle %}
<style>
    #header {
        background: #2c3e50;
        color: #fff;
    }
    .module h2, .module caption {
        background: #34495e;
    }
    div.breadcrumbs {
        background: #34495e;
    }
    a:link, a:visited {
        color: #2c3e50;
    }
</style>
{% endblock %}

6.2 自定义列表显示

python 复制代码
# admin.py
@admin.register(Product)
class ProductAdmin(admin.ModelAdmin):
    def get_list_display(self, request):
        """动态设置列表显示字段"""
        if request.user.is_superuser:
            return ('name', 'category', 'price', 'stock', 'status', 
                    'created_by', 'created_at', 'get_image')
        return ('name', 'category', 'price', 'stock', 'status')
    
    def get_readonly_fields(self, request, obj=None):
        """动态设置只读字段"""
        if request.user.is_superuser:
            return ()
        if obj:  # 编辑时
            return ('created_by', 'created_at')
        return ('created_at',)

6.3 自定义过滤器

python 复制代码
# admin.py
from django.contrib.admin import SimpleListFilter

class StockStatusFilter(SimpleListFilter):
    title = '库存状态'
    parameter_name = 'stock_status'
    
    def lookups(self, request, model_admin):
        return (
            ('out', '无库存'),
            ('low', '库存不足'),
            ('enough', '库存充足'),
        )
    
    def queryset(self, request, queryset):
        if self.value() == 'out':
            return queryset.filter(stock=0)
        if self.value() == 'low':
            return queryset.filter(stock__gt=0, stock__lt=10)
        if self.value() == 'enough':
            return queryset.filter(stock__gte=10)

@admin.register(Product)
class ProductAdmin(admin.ModelAdmin):
    list_filter = (StockStatusFilter, 'status', 'category')

七、内联模型示例

python 复制代码
# admin.py
from django.contrib import admin
from .models import Order, OrderItem

class OrderItemInline(admin.TabularInline):
    model = OrderItem
    extra = 1
    fields = ('product', 'quantity', 'price')
    raw_id_fields = ('product',)
    
    def get_readonly_fields(self, request, obj=None):
        if obj:  # 如果是编辑已存在的订单
            return ('price',)
        return ()

@admin.register(Order)
class OrderAdmin(admin.ModelAdmin):
    list_display = ('order_number', 'customer', 'total_amount', 
                   'status', 'created_at')
    list_filter = ('status', 'created_at')
    search_fields = ('order_number', 'customer__username')
    inlines = [OrderItemInline]
    readonly_fields = ('order_number', 'total_amount')

八、权限控制

python 复制代码
# admin.py
@admin.register(Product)
class ProductAdmin(admin.ModelAdmin):
    def has_add_permission(self, request):
        """控制添加权限"""
        return request.user.has_perm('products.add_product')
    
    def has_change_permission(self, request, obj=None):
        """控制修改权限"""
        if not obj:
            return True
        # 只允许超级管理员和创建者修改
        return request.user.is_superuser or obj.created_by == request.user
    
    def has_delete_permission(self, request, obj=None):
        """控制删除权限"""
        if not obj:
            return True
        # 只允许超级管理员删除
        return request.user.is_superuser

九、总结

  1. 今日要点回顾:

    • Django管理界面的基本配置
    • 自定义管理界面的外观和功能
    • 内联模型的使用方法
    • 权限控制的实现
  2. 应用场景:

    • 内容管理系统
    • 电商后台管理
    • 数据维护界面
    • 运营管理系统
  3. 注意事项:

    • 合理设置权限
    • 优化查询性能
    • 注意数据安全
    • 保持界面友好
  4. 扩展建议:

    • 集成富文本编辑器
    • 添加数据导入导出
    • 自定义管理命令
    • 添加数据统计功能

怎么样今天的内容还满意吗?再次感谢朋友们的观看,关注GZH:凡人的AI工具箱,回复666,送您价值199的AI大礼包。最后,祝您早日实现财务自由,还请给个赞,谢谢!

相关推荐
我叫啥都行7 分钟前
计算机基础复习12.22
java·jvm·redis·后端·mysql
Code out the future20 分钟前
【C++——临时对象,const T&】
开发语言·c++
Zmxcl-00723 分钟前
IIS解析漏洞
服务器·数据库·microsoft
Stark、23 分钟前
【Linux】文件IO--fcntl/lseek/阻塞与非阻塞/文件偏移
linux·运维·服务器·c语言·后端
taoyong00124 分钟前
Java线程核心01-中断线程的理论原理
java·开发语言
一雨方知深秋24 分钟前
智慧商城:封装getters实现动态统计 + 全选反选功能
开发语言·javascript·vue2·foreach·find·every
海威的技术博客27 分钟前
关于JS中的this指向问题
开发语言·javascript·ecmascript
觅远29 分钟前
python实现word转html
python·html·word
coding侠客1 小时前
Spring Boot 多数据源解决方案:dynamic-datasource-spring-boot-starter 的奥秘
java·spring boot·后端
froginwe111 小时前
PostgreSQL表达式的类型
开发语言