如何在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
相关产品推荐
相关产品推荐

