You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Django Admin视图按钮设为只读:点击仍可修改的问题求助

解决Django Admin中View按钮对应页面只读的问题

方案一:自定义只读详情视图

这种方式完全独立于修改页面,自定义展示逻辑,安全可控性更强。

  1. 在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)
  1. 创建只读视图模板
    在项目的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 %}

方案二:复用修改页面实现条件只读

这种方式利用现有表单,通过参数控制字段只读,开发成本更低。

  1. 修改表单添加只读模式支持
    更新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
  1. 修改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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.06 01:45:43