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

如何在Django视图函数中将表单实例化为只读模式?

问题描述

我有一个Django 4表单,包含供用户选择值的若干组件:

from django import forms
from .app.model import MyModel
from bootstrap_datepicker_plus.widgets import DatePickerInput

class FooForm(forms.ModelForm):
    # stuff
    class Meta:
        model = FooModel
        fields = [
            "user_id",
            "created_at",
            "some_other_field",
        ]

        widgets = {
            "user_id": forms.NumberInput(),
            "created_at": DatePickerInput(),
            "some_other_field": forms.NumberInput(),
        }

我在views.py的多个函数中实例化该表单:

my_form_instance = forms.FooForm(
    data=data,
    user=request.user,
)

其中create_or_edit_foo()函数用于创建新记录或编辑现有记录,delete_foo()函数用于删除记录。在删除函数对应的/foo/12/delete端点中,我希望以只读模式显示该表单。

该如何实现这一需求?

我知道可以为每个组件添加attrs={'readonly': True,}使其不可编辑,但这会影响create_or_edit_foo函数中使用的表单实例。

我使用的环境为Django 4.2、Ubuntu 22.04、Python 3.10.6。


解决方案

这里提供三种不影响原有表单正常使用的实现方式,可根据场景选择:

方式一:实例化表单时动态添加只读/禁用属性

在删除视图中,实例化表单后遍历所有字段,为字段的widget添加属性。readonly允许用户选中但无法修改,disabled则完全禁用交互(提交表单时不会携带该字段值,更适合删除场景)。

# views.py 中的 delete_foo 函数
def delete_foo(request, pk):
    foo_instance = FooModel.objects.get(pk=pk)
    # 仅传入instance,无需data(只读场景无需提交表单)
    my_form_instance = forms.FooForm(instance=foo_instance)
    
    # 遍历字段添加禁用属性
    for field in my_form_instance.fields.values():
        # 日期选择器用disabled彻底禁用交互,普通输入框可选readonly
        if isinstance(field.widget, DatePickerInput):
            field.widget.attrs['disabled'] = True
        else:
            field.widget.attrs['readonly'] = True
    
    return render(request, 'foo_delete.html', {'form': my_form_instance})

方式二:创建只读表单子类

如果需要在多个场景使用只读表单,可以创建继承自FooForm的子类,在子类中统一配置字段属性,完全不影响原表单的编辑功能。

# forms.py
class FooReadOnlyForm(FooForm):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        # 遍历所有字段设置禁用,并取消必填要求
        for field in self.fields.values():
            field.widget.attrs['disabled'] = True
            field.required = False

在删除视图中直接使用该子类:

# views.py
def delete_foo(request, pk):
    foo_instance = FooModel.objects.get(pk=pk)
    my_form_instance = forms.FooReadOnlyForm(instance=foo_instance)
    return render(request, 'foo_delete.html', {'form': my_form_instance})

方式三:在模板中控制只读状态

如果不想修改Python代码,可在模板渲染时为字段添加属性。需确保模板仅用于删除场景,或通过视图传递上下文变量判断状态。

模板示例(foo_delete.html)

{% for field in form %}
    <div class="form-group">
        <label>{{ field.label }}</label>
        {% if field.widget_type == 'datepickerinput' %}
            {{ field|attr:'disabled' }}
        {% else %}
            {{ field|attr:'readonly' }}
        {% endif %}
    </div>
{% endfor %}

带上下文判断的模板写法

先在视图中传递状态变量:

# views.py 的 delete_foo 函数
return render(request, 'foo_delete.html', {'form': my_form_instance, 'is_readonly': True})

模板中根据变量判断:

{% for field in form %}
    <div class="form-group">
        <label>{{ field.label }}</label>
        {% if is_readonly %}
            {% if field.widget_type == 'datepickerinput' %}
                {{ field|attr:'disabled' }}
            {% else %}
                {{ field|attr:'readonly' }}
            {% endif %}
        {% else %}
            {{ field }}
        {% endif %}
    </div>
{% endfor %}

注意事项

  • disabled属性的字段不会随表单提交,适合删除场景;若需保留字段值提交,使用readonly。
  • 针对bootstrap_datepicker_plus的DatePickerInput,disabled能彻底禁用日期选择交互,体验比readonly更好。

内容的提问来源于stack exchange,提问作者swiss_knight

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.02 16:33:29