Django Admin自定义表单动作:如何预填充模型字段至审批表单
解决Django Admin自定义批准按钮表单预填充字段的问题
嘿,这个问题我之前在做Django Admin自定义动作的时候也碰到过,给你两个实用的解决方案,看哪种更适合你的场景:
方案一:复用Django Admin内置的修改表单(最简单)
这个方法直接利用Admin自带的changeform视图,只需要在自定义按钮的跳转URL里带上标识参数,然后动态限制表单显示的字段,这样打开页面时会自动预填充模型的现有值。
步骤1:添加自定义批准按钮
在你的CommandAdmin类里添加生成跳转按钮的方法,指向该实例的修改页面并带上approve=1的参数:
from django.urls import reverse from django.utils.html import format_html class CommandAdmin(admin.ModelAdmin): # 你的其他配置(比如list_display、fields等) def approve_button(self, obj): # 生成指向修改页面的URL,附加approve参数 change_url = reverse('admin:your_app_label_command_change', args=[obj.pk]) return format_html('<a class="button" href="{}?approve=1">Approve</a>', change_url) approve_button.short_description = 'Approve' # 按钮在列表页的显示文字 # 记得把approve_button加到list_display里 list_display = [..., 'approve_button', 'deny_button']
步骤2:动态调整表单字段
重写get_form方法,当检测到approve参数时,只保留你需要让用户编辑的字段:
def get_form(self, request, obj=None, **kwargs): form = super().get_form(request, obj, **kwargs) # 判断是否是批准请求 if request.GET.get('approve') == '1': # 只保留需要的字段,这里替换成你的字段名 allowed_fields = ['nonroot_risk', 'nopasswd_risk'] # 过滤表单字段 form.base_fields = {field: form.base_fields[field] for field in allowed_fields} return form
这样用户点击批准按钮后,会打开一个简化版的修改表单,nonroot_risk和nopasswd_risk会自动填充模型里的现有值,提交后直接走Admin的默认保存逻辑,你还可以在save_model方法里添加批准相关的额外处理(比如修改状态)。
方案二:自定义视图与表单(更灵活)
如果需要更多自定义逻辑(比如额外的批准确认步骤),可以自己写一个独立的视图和模板,完全控制表单的展示和处理流程。
步骤1:定义批准专用表单
创建一个只包含需要编辑字段的ModelForm:
from django import forms from .models import Command class ApproveCommandForm(forms.ModelForm): class Meta: model = Command fields = ['nonroot_risk', 'nopasswd_risk']
步骤2:在Admin中添加自定义URL和视图
扩展CommandAdmin的URL配置,添加批准视图:
from django.contrib.admin.views.decorators import staff_member_required from django.shortcuts import get_object_or_404, render, redirect from django.urls import path class CommandAdmin(admin.ModelAdmin): # 其他配置... def get_urls(self): # 合并默认URL和自定义URL urls = super().get_urls() custom_urls = [ path( 'approve/<int:pk>/', self.admin_site.admin_view(self.approve_view), name='command_approve' ), ] return custom_urls + urls def approve_button(self, obj): approve_url = reverse('admin:command_approve', args=[obj.pk]) return format_html('<a class="button" href="{}">Approve</a>', approve_url) approve_button.short_description = 'Approve' @staff_member_required def approve_view(self, request, pk): command = get_object_or_404(Command, pk=pk) if request.method == 'POST': # POST请求时,用提交的数据和实例初始化表单 form = ApproveCommandForm(request.POST, instance=command) if form.is_valid(): # 执行你的批准逻辑,比如修改状态 command.status = 'approved' form.save() # 重定向回命令列表页 return redirect(reverse('admin:your_app_label_command_changelist')) else: # GET请求时,用实例初始化表单,自动预填充现有值 form = ApproveCommandForm(instance=command) # 渲染自定义模板 return render(request, 'admin/approve_command.html', { 'form': form, 'object': command, 'opts': self.model._meta, 'title': f'Approve Command #{command.pk}', })
步骤3:创建Admin风格的模板
在你的模板目录下创建admin/approve_command.html,可以参考Django Admin的change_form.html简化,保持风格一致:
{% extends "admin/base_site.html" %} {% load i18n admin_urls static admin_modify %} {% block content %} <div class="module"> <h1>{{ title }}</h1> <form method="post"> {% csrf_token %} <div> {% if form.errors %} <p class="errornote">{% trans "Please correct the errors below." %}</p> {{ form.errors }} {% endif %} {% for field in form %} <div class="form-row"> {{ field.label_tag }} {{ field }} {% if field.help_text %} <p class="help">{{ field.help_text|safe }}</p> {% endif %} </div> {% endfor %} <div class="submit-row"> <input type="submit" value="{% trans 'Approve and Save' %}" class="default"> </div> </div> </form> </div> {% endblock %}
注意事项
- 确保
nonroot_risk和nopasswd_risk字段在模型中不是editable=False,否则表单无法编辑这些字段。 - 如果用方案一,要保证这些字段在
CommandAdmin的fields或fieldsets配置中存在,否则get_form里可能找不到对应的字段。 - 两种方案都可以在保存后添加自定义逻辑,比如发送通知、更新关联数据等,只需要在
save_model(方案一)或视图的form.is_valid()之后(方案二)添加代码即可。
内容的提问来源于stack exchange,提问作者Red Cricket
相关产品推荐
相关产品推荐

