Django Admin视图按钮设为只读:点击仍可修改的问题求助
解决Django Admin中View按钮对应页面只读的问题
方案一:自定义只读详情视图
这种方式完全独立于修改页面,自定义展示逻辑,安全可控性更强。
- 在
CustomerAdmin中添加自定义视图与URL路由
修改admin.py,新增只读视图并注册对应的路由:
from django.contrib import admin from .models import Customer from .forms import CustomerForm from django.utils.html import format_html from django.shortcuts import render from django.urls import path class CustomerAdmin(admin.ModelAdmin): form = CustomerForm list_display = ['id','first_name','last_name','profile_picture', 'gender', 'date_of_birth', 'email', 'phone_number', 'address', 'marry_status','country','state','city','view_button', 'edit_button', 'delete_button'] list_display_links = ['id'] ordering = ('id',) # 自定义只读视图逻辑 def view_customer(self, request, object_id): customer = self.get_object(request, object_id) # 整理需要展示的字段(可根据业务需求增减) display_fields = [ ('ID', customer.id), ('First Name', customer.first_name), ('Last Name', customer.last_name), ('Email', customer.email), ('Phone Number', customer.phone_number), ('Address', customer.address), ('Gender', customer.gender), ('Date of Birth', customer.date_of_birth), ] return render(request, 'admin/customer_view.html', { 'title': f'View Customer #{customer.id}', 'customer': customer, 'display_fields': display_fields, 'opts': self.model._meta, 'has_change_permission': False, # 隐藏Admin默认的修改按钮 }) # 注册自定义URL def get_urls(self): urls = super().get_urls() custom_urls = [ path('<path:object_id>/view/', self.admin_site.admin_view(self.view_customer), name='customer-view'), ] return custom_urls + urls # 更新View按钮的跳转路径 def view_button(self, obj): return format_html('<a class="button" style="background-color: darkgreen;" href="/admin/customer_form/customer/{}/view/">View</a>'.format(obj.id)) view_button.allow_tags = True view_button.short_description = 'View' # 保留原有编辑、删除按钮逻辑 def edit_button(self, obj): return format_html ('<a class="button" style="color: black; background-color: yellow;" href="/admin/customer_form/customer/{}/change/">Edit</a>'.format(obj.id)) edit_button.allow_tags = True edit_button.short_description = 'Edit' def delete_button(self, obj): return format_html('<a class="button" style="background-color: #ba2121;" href="/admin/customer_form/customer/{}/delete/">Delete</a>'.format(obj.id)) delete_button.allow_tags = True delete_button.short_description = 'Delete' admin.site.register(Customer, CustomerAdmin)
- 创建只读视图模板
在项目的templates/admin/目录下新建customer_view.html文件,内容如下:
{% extends "admin/base_site.html" %} {% block content %} <div class="module"> <h1>{{ title }}</h1> <div class="object-tools"> <a href="{% url 'admin:customer_form_customer_change' customer.id %}" class="button">Edit</a> <a href="{% url 'admin:customer_form_customer_delete' customer.id %}" class="button" style="background-color: #ba2121;">Delete</a> </div> <table class="table"> {% for label, value in display_fields %} <tr> <th style="width: 200px;">{{ label }}</th> <td>{{ value|default:"-" }}</td> </tr> {% endfor %} </table> </div> {% endblock %}
方案二:复用修改页面实现条件只读
这种方式利用现有表单,通过参数控制字段只读,开发成本更低。
- 修改表单添加只读模式支持
更新forms.py,让表单接收只读参数并自动禁用所有字段:
from django import forms from .models import Customer class CustomerForm(forms.ModelForm): class Meta: model = Customer fields = '__all__' def __init__(self, *args, **kwargs): # 接收只读参数,默认关闭只读模式 self.read_only = kwargs.pop('read_only', False) super().__init__(*args, **kwargs) if self.read_only: # 禁用所有字段,前端无法编辑 for field in self.fields.values(): field.widget.attrs['disabled'] = True field.widget.attrs['readonly'] = True
- 修改
CustomerAdmin控制表单与保存逻辑
更新admin.py,添加只读模式的判断与拦截逻辑:
from django.contrib import admin from .models import Customer from .forms import CustomerForm from django.utils.html import format_html from django.urls import path class CustomerAdmin(admin.ModelAdmin): form = CustomerForm list_display = ['id','first_name','last_name','profile_picture', 'gender', 'date_of_birth', 'email', 'phone_number', 'address', 'marry_status','country','state','city','view_button', 'edit_button', 'delete_button'] list_display_links = ['id'] ordering = ('id',) # 重写修改视图,传递只读参数 def changeform_view(self, request, object_id=None, form_url='', extra_context=None): # 通过路径或GET参数判断是否为查看模式 is_view_mode = request.path.endswith('/view/') or 'view' in request.GET extra_context = extra_context or {} if is_view_mode: # 隐藏所有保存相关按钮 extra_context.update({ 'show_save': False, 'show_save_and_continue': False, 'show_save_and_add_another': False }) # 向表单传递只读参数 return super().changeform_view( request, object_id, form_url, extra_context, form_kwargs={'read_only': True} ) return super().changeform_view(request, object_id, form_url, extra_context) # 阻止只读模式下的保存操作,防止恶意提交 def save_model(self, request, obj, form, change): is_view_mode = request.path.endswith('/view/') or 'view' in request.GET if not is_view_mode: super().save_model(request, obj, form, change) # 注册查看模式的URL(可选,也可以直接用?view=1的GET参数) def get_urls(self): urls = super().get_urls() custom_urls = [ path('<path:object_id>/view/', self.changeform_view, name='customer-view'), ] return custom_urls + urls # 更新View按钮的跳转路径 def view_button(self, obj): return format_html('<a class="button" style="background-color: darkgreen;" href="/admin/customer_form/customer/{}/view/">View</a>'.format(obj.id)) view_button.allow_tags = True view_button.short_description = 'View' # 保留原有编辑、删除按钮逻辑 def edit_button(self, obj): return format_html ('<a class="button" style="color: black; background-color: yellow;" href="/admin/customer_form/customer/{}/change/">Edit</a>'.format(obj.id)) edit_button.allow_tags = True edit_button.short_description = 'Edit' def delete_button(self, obj): return format_html('<a class="button" style="background-color: #ba2121;" href="/admin/customer_form/customer/{}/delete/">Delete</a>'.format(obj.id)) delete_button.allow_tags = True delete_button.short_description = 'Delete' admin.site.register(Customer, CustomerAdmin)
两种方案对比
- 方案一:完全自定义展示内容,不受Admin表单限制,安全性更高,但需要额外编写模板代码。
- 方案二:复用现有表单与Admin界面,开发速度更快,但需要额外处理后端校验,防止恶意提交修改。
内容的提问来源于stack exchange,提问作者n0cuous
相关产品推荐
相关产品推荐

