django+admin常用功能

一、编辑页面增加自定义按钮:

利用原来的input框下面的help_text,可以直接添加html类内容,制作各种按钮。

    def get_form(self, request, obj=None, **kwargs):
        form = super().get_form(request, obj, **kwargs)


        # ✅ 公司名称:按钮放在 help_text 里
        form.base_fields['gongyingshang'].help_text = (
            '<div style="margin-top:5px;">'
            '<div id="btn_fill_customer" '
            'class="el-button el-button--primary el-button--small" '
            'style="cursor:pointer; display:inline-block;">'
            '<i class="el-icon-check"></i> 填充选中供应商和ID号'
            '</div>'
            '</div>'
        )

        # ✅ 客户ID:只加说明,不加按钮
        form.base_fields['chanpinmingcheng'].help_text = (
            '系统自动回填,无需手动填写'
        )
  

        return form
效果图如下:


二、编辑页调用自定义js代码:
    # 如果需要自定义媒体文件
    class Media:
        js = (
            'admin/js/jquery.init.js',  # Admin自带的jQuery初始化            
            'admin/js/admin.list.caigou.js', #自定义js1
            'admin/js/custom-autocomplete.js', #自定义js2
            
        )
在admin.py的指定模型里添加后,编辑页面就能调用到指定位置的自定义js文件了。


三、常用的admin.py中字段意义详解
# 定义Admin类
Q: @admin.register(Fapiao_admin)
A: 将Fapiao_admin这个类注册到默认的django的admin.py中,必须在头部引用
from .models import Fapiao_admin

Q:class FapiaoAdmin(admin.ModelAdmin): #设置一个FapiaoAdmin菜单独立管理
    # 设置actions
    actions = [direct_total_nopay_action_fapiao] #设置一个批量管理功能
 
actions如下这义,必须和模型同级别,没有缩进 。
# app1/admin.py
from django.contrib import admin
from django.urls import reverse
from django.utils.safestring import mark_safe
from django.db.models import Sum
from decimal import Decimal
from datetime import datetime
from .models import Order_admin,ProductImage


# ==================================
# 1 ️⃣ Action:批量切换合同状态
# ==================================
def batch_update_hetong_status(modeladmin, request, queryset):
    """
    将选中的“执行中”合同批量改为“完毕”
    """
    updated = queryset.filter(hetongzhuangtai='执行中').update(
        hetongzhuangtai='完毕'
    )

    modeladmin.message_user(
        request,
        f"成功切换 {updated} 条数据的合同状态为【完毕】",
        level='success'
    )

batch_update_hetong_status.short_description = "批量切换合同状态为【完毕】"


# ==================================
# 2 ️⃣ Action:统计未收金额
# ==================================
def direct_total_nopay_action(modeladmin, request, queryset):
    """
    统计选中记录的未收金额
    """
    if not queryset.exists():
        modeladmin.message_user(
            request,
            "请先选择要统计的记录",
            level='info'
        )
        return

    total = queryset.aggregate(
        sum_amount=Sum('weishoujine')
    )['sum_amount'] or Decimal('0')

    count = queryset.count()

    html = f"""
    <div style="
        background:#2f3640;
        color:white;
        padding:15px;
        border-radius:8px;
        margin:10px 0;
    ">
        <h3 style="margin:0 0 10px;">🧮 金额统计结果</h3>
        <p>记录数:<b>{count}</b></p>
        <p>未收金额:<b>¥{total:.4f}</b></p>
        <small>统计时间:{datetime.now()}</small>
    </div>
    """

    modeladmin.message_user(
        request,
        mark_safe(html),
        level='success'
    )

direct_total_nopay_action.short_description = "🧮 统计未收金额总和"
这样设置就可以了。








   
    # 新窗口打开编辑 START
    def edit_link(self, obj):
        if obj.pk:
            return format_html('<a href="/admin/app1/fapiao_admin/{}/change/" target="_blank">编辑</a>', obj.id)
        return "-"
    
    edit_link.short_description = '操作'
    # 新窗口打开编辑 END
    
    # 双下划线语法的模糊查询,包含某个公司名查询,设置可以搜索的列
    search_fields = [
        'cw_guanlianhetong_cg',  
        'cw_guanlianhetong_xs',
        'cw_beizhu',
    ]
    
    # 列表显示字段,在列表显示的字段,每一行是一列,列的位置和这里行对应
    list_display = [
        'id',
        'edit_link',
        'cw_fapiaobianhao', 
        'cw_fapiaoleibie',
        'cw_fapiaoleixing',
        'cw_guanlianhetong_cg',
        'cw_guanlianhetong_xs',
        'cw_kaipiaojine',
        'cw_yingshoushuie',
        'cw_shishoushuie',
        'cw_jinxiangshuie',
        'cw_dikouchae',
        'colored_cw_shifoukaijv',
        'cw_kehukaipiaoxinxi',
        'get_xiaoguotu_JS',  # 改为新的方法名
        'get_fapiaofujian_JS',  # 改为新的方法名
        'cw_beizhu',
        'cw_chuangjianshijian',
        'cw_gengxinshijian'
    ]

    # 如果需要,可以添加只读字段,添加了只读字段的,编辑时的值是不可以修改的
    readonly_fields = ['get_xiaoguotu_JS', 'get_fapiaofujian_JS']  
      
    # 在Django admin中,需要单独定义fieldsets或使用filter_horizontal/filter_vertical处理多对多字段
    # 如果cw_guanlianhetong和cw_guanliancaigouhetong是多对多字段,可以这样处理:
    filter_horizontal = ['cw_guanlianhetong', 'cw_guanliancaigouhetong']  # 或者使用filter_vertical
    
    # 设置过滤器,通过过滤器搜索对应的列字段
    list_filter = [
        'cw_fapiaoleibie',
        'cw_fapiaoleixing',
        'cw_fapiaobianhao',
        'cw_shifoukaijv',
        'cw_gengxinshijian'
    ]
    
    # 设置可以点击进入编辑的字段

四、内联管理
from .models import ProductImage #导入模型中定义的一个类

# 1. 内联类
class ProductImageInline(admin.TabularInline):  #将这个模型定义为一个内联,也就是在指定的编辑页的最底部显示,没有独立的菜单项管理
    model = ProductImage  #指定model为导入的这个Model
    extra = 1 #作为内联时默认显示的条数,为0则不显示,需要点按钮才能添加一个
    # 可选:控制内联显示的字段,内联的列显示哪些字段
    fields = ('p_name','p_sku','image','p_color','p_period', 'order')
    readonly_fields = ('image_preview',) #设置为只读就没办法编辑了
    autocomplete_fields = ['p_sku']  # 将p_sku变成带搜索的下拉框,前提是p_sku必须是   
 这种类型:
   p_sku = models.ForeignKey(
        ProductSpec, 
        on_delete=models.CASCADE,
        verbose_name='所属分类ID',
        related_name='product_images'  # 更新related_name
    )
    # 从 autocomplete_fields 改为 raw_id_fields,字段必须为ForeignKey
    raw_id_fields = ['p_sku']  # ✅ 弹窗选择器,点击一个放大镜弹窗搜索,数据量大于1万条,切换用这个 2026-5-7 13:47:21



#直接在html中显示灯箱弹窗显示图片
    def image_preview(self, obj):
        """在HTML中直接嵌入JS,最可靠"""
        if obj.image:
            return format_html(
                '''
                <div style="position:relative;">
                    <img src="{0}" 
                         onclick="
                             var overlay = document.createElement('div');
                             overlay.style.cssText = 'position:fixed;top:0;left:0;width:100%;height:100%;background:rgba(0,0,0,0.92);z-index:9999;display:flex;align-items:center;justify-content:center;';
                             
                             var img = document.createElement('img');
                             img.src = '{0}';
                             img.style.maxWidth = '90vw';
                             img.style.maxHeight = '90vh';
                             img.style.borderRadius = '8px';
                             img.style.boxShadow = '0 10px 50px rgba(0,0,0,0.5)';
                             
                             var closeBtn = document.createElement('div');
                             closeBtn.innerHTML = '×';
                             closeBtn.style.cssText = 'position:absolute;top:20px;right:20px;color:white;font-size:40px;cursor:pointer;z-index:10000;width:50px;height:50px;border-radius:50%;display:flex;align-items:center;justify-content:center;background:rgba(0,0,0,0.5);';
                             
                             overlay.onclick = function(e) {{
                                 if(e.target === overlay || e.target === closeBtn) {{
                                     document.body.removeChild(overlay);
                                 }}
                             }};
                             
                             overlay.appendChild(closeBtn);
                             overlay.appendChild(img);
                             document.body.appendChild(overlay);
                         "
                         style="max-height:80px; max-width:80px; border:1px solid #ddd; border-radius:4px; padding:2px; cursor:zoom-in;"
                         title="点击放大" />
                </div>
                ''',
                obj.image.url
            )
        return "—"
    image_preview.short_description = '图片预览'
 

如果想在哪个admin.py中的模型使用这个内联,内联定义和模型同级别的缩进,在模型里缩进一个字段,设置:
    # 内联
    inlines = [ProductImageInline]
这样就会在当前编辑页面里,显示ProductImageInline 这个内联了。
   


    # 🔴 关键:添加这行,引入自定义JS
    class Media:
        js = (
            #'admin/js/debug.js',  # 调试文件
            'admin/js/admin.list.chanpinguanli.js',
            'admin/js/jquery.init.js',  # Admin自带的jQuery初始化
            'admin/js/admin.paste_upload.js', # 粘贴上传功能
            
        )

# 🔴 关键:添加这行,引入自定义CSS

         css = {
            'all': ('admin/css/paste_upload.css',)
        }


五、让后台默认显示日志
# admin.py
from django.contrib.admin.models import LogEntry

# 注册 LogEntry 到 Admin 后台,让后台显示日志版块 2026-4-30 00:46:06
@admin.register(LogEntry)
class LogEntryAdmin(admin.ModelAdmin):
    """显示操作日志"""
    list_display = ['action_time', 'user', 'content_type', 'object_repr', 'action_flag']
    list_filter = ['action_time', 'user', 'content_type']
    readonly_fields = ['action_time', 'user', 'content_type', 'object_id', 'action_flag', 'change_message']
    search_fields = ['object_repr', 'change_message']
    
    # 禁止在日志页面进行增删改操作(日志是只读的)
    def has_add_permission(self, request):
        return False
    def has_change_permission(self, request, obj=None):
        return False
    def has_delete_permission(self, request, obj=None):
        return False

六、常用(一个完整的列表管理标签)
# 标签管理 START 
# 先注册标签模型,因为产品模型要用到
@admin.register(Biaoqian_admin)  # 使用装饰器注册
class BiaoqianAdmin(ImportExportActionModelAdmin):  # 继承 ModelAdmin
    """标签管理后台"""
    
    # 排序配置
    ordering = ['id']  # 同 Xadmin 的 ordering,按id降序排列,-id就是按id升序排列,这种方式可以结合系统的sort功能实现拖动的排序。
    list_per_page = 20  # 同 Xadmin 的 list_per_page,每页显示几条数据
    
    # 列表页显示字段
    list_display = ('biaoqianmingcheng', 'paixv', 'chuangjianshijian')
    
    # 搜索字段(需要取消注释并修改),哪些可以搜索
    search_fields = ('biaoqianmingcheng',)
    
    # 列表页可点击的字段(需要取消注释并修改),哪些列可以点击进入编辑页面
    list_display_links = ('biaoqianmingcheng',)
    
    # 只读字段(需要在表单页使用),表单页只显示不能修改
    readonly_fields = ('some_field',)
    
    # 可选:添加过滤器,筛选器
    list_filter = ('paixv',)
    
    # 可选:添加日期分层导航,显示一个2026年5月,5月20日、5月22日这种时间归档的筛选
    date_hierarchy = 'chuangjianshijian'
    
    # 可选:字段别名显示
    def get_list_display(self, request):
        """自定义显示字段,可以添加计算字段"""
        base_list = list(super().get_list_display(request))
        # 可以在这里添加自定义计算字段
        return base_list
# 标签管理 END 

实际应用示例

示例1:根据用户权限显示不同字段

def get_list_display(self, request):
    base_list = list(super().get_list_display(request))
    
    # 管理员可以看到更多字段
    if request.user.is_superuser:
        base_list.append('caigou_rmb')  # 添加采购金额
        base_list.append('caigou_pay')  # 添加支付状态
    
    # 普通用户看不到敏感字段
    else:
        if 'caigou_rmb' in base_list:
            base_list.remove('caigou_rmb')
    
    return base_list

为什么要用这个方法?

优点:

  1. 动态控制:根据条件动态显示/隐藏字段
  2. 权限控制:不同用户看到不同字段
  3. 灵活性:可以在运行时修改字段
  4. 计算字段:可以添加非数据库字段

缺点:

  1. 稍微复杂:比静态 list_display复杂
  2. 性能:每次加载列表都要执行

何时使用?

  1. 需要权限控制
  2. 需要动态字段
  3. 需要添加计算字段
  4. 字段固定不变时,直接用 list_display更简单

七、字段分组
            
    # 字段分组(编辑页)
    fieldsets = (
        ('基本信息', {
            'fields': ('chanpinmingcheng', 'chanpinguige', 'chanpintupian', )
        }),
        ('关联信息', {
            'fields': ('related_gongyingshang','related_gongyingshang_id', 'chanpinbiaoqian'),
            'description': '使用左右穿梭框选择'
        }),
        ('库存成本', {
            'fields': ('chanpinchengben', 'chanpinkucun','kucunchengben','pandianshijian', 'weizhi')
        }),
        ('其他信息', {
            'fields': ('erweima', 'beizhu'),
            'classes': ('collapse',)  # 可折叠
        }),
    )

设置了fieldsets之后,字段就按这个分类显示了,最后一个可以设置为折叠模式,默认不显示。注意在admin+django原生环境中,很多必须设置在这里才会显示。比如左右穿插等。

 

八、设置新窗口打开编辑
    # 新窗口打开编辑
    def edit_link(self, obj):
        return format_html('<a href="/admin/app1/customer_admin/{}/change/" target="_blank">编辑</a>', obj.id)
    edit_link.short_description = '操作'
    edit_link.allow_tags = True

可以直接在list_display中显示这个链接,打开后新窗口编辑 。








声明:本站所有文章,如无特殊说明或标注,均为本站原创发布。任何个人或组织,在未征得本站同意时,禁止复制、盗用、采集、发布本站内容到任何网站、书籍等各类媒体平台。如若本站内容侵犯了原著者的合法权益,可联系我们进行处理。