为啥 Django 中的只读表单字段是个坏主意?
Posted
技术标签:
【中文标题】为啥 Django 中的只读表单字段是个坏主意?【英文标题】:Why are read-only form fields in Django a bad idea?为什么 Django 中的只读表单字段是个坏主意? 【发布时间】:2011-02-23 12:38:50 【问题描述】:我一直在寻找一种创建只读表单域的方法,我发现的每篇关于该主题的文章都带有“这是一个坏主意”的声明。现在对于单个表单,我可以理解还有其他方法可以解决问题,但是在模型表单集中使用只读表单字段似乎是一个完全自然的想法。
考虑一个教师成绩册应用程序,教师希望能够通过一次提交输入所有学生(注意复数学生)的成绩。 modelformset 可以遍历所有学生的成绩,使学生姓名是只读的,成绩是可编辑的字段。我喜欢使用模型表单集获得的错误检查和错误报告的强大功能和便利性,但在这样的表单集中让学生姓名可编辑太疯狂了。
由于 django 专家的共识是只读表单字段是一个坏主意,我想知道对于上面的示例学生级示例,标准 django 最佳实践是什么?
【问题讨论】:
【参考方案1】:您不想这样做的原因是有人可以将您的禁用字段更改为启用,然后提交表单。您必须更改保存功能以不插入“禁用”数据。
执行此操作的标准方法是不将名称放在输入中,而是将其显示为文本
<form>
<div>
<label>Name</label>
<p>Johnny Five</p>
</div>
<div>
....
这在 django 中是不可能的。
我说如果你真的相信你的用户群不会“搞砸”事情,那就去吧,但如果它是一个面向公众的网站,可能包含敏感数据,那就远离吧。
【讨论】:
感谢 Galen 解释了大部分问题,但我仍然很好奇人们如何(或是否)使用 Django 来实现我上面建议的成绩册页面。放弃内置工具? 看看这个答案***.com/questions/324477/… 相当多的阅读“只读”请求的人将其解释为向小部件添加“只读”或“禁用”属性。这有你上面提到的所有问题(并且会在紧要关头工作),但理想情况下会有其他解决方案,比如上面的(或 )标签。我接下来要试试这个:lazypython.blogspot.com/2008/12/…
正确的禁用输入是危险的,就像我说的那样,我还以为你在寻求解决方案。 @suhail:不,CSRF 不会阻止用户使用 DOM 检查器删除只读/禁用属性并提交表单。【参考方案2】:就您的情况而言,这是理想的答案:
https://***.com/a/2242468/1004781
即,只需在模板中打印模型变量:
form.instance.LastName
【讨论】:
【参考方案3】:使用禁用的字段时,如果表单验证失败,您还需要确保它保持正确填充。这是我的方法,它还可以防止恶意尝试更改提交的数据:
class MyForm(forms.Form):
MY_VALUE = 'SOMETHING'
myfield = forms.CharField(
initial=MY_VALUE,
widget=forms.TextInput(attrs='disabled': 'disabled')
def __init__(self, *args, **kwargs):
# If the form has been submitted, populate the disabled field
if 'data' in kwargs:
data = kwargs['data'].copy()
self.prefix = kwargs.get('prefix')
data[self.add_prefix('myfield')] = MY_VALUE
kwargs['data'] = data
super(MyForm, self).__init__(*args, **kwargs)
【讨论】:
【参考方案4】:对于学生/评分示例,我提出了一个解决方案,其中学生是不可编辑的字段,并且可以根据需要编辑和更新成绩。 something like this
我正在使用 zip 函数在 view.py 中的 Grade_edit 类中组合学生对象和表单集以获取成绩。
def grade_edit(request, id):
student = student.objects.get(id=id)
grades = grades.objects.filter(studentId=id)
gradeformset = GradeFormSet(request.POST or None)
if request.POST:
gradeformset = GradeFormSet(request.POST, request.FILES, instance=student)
if gradeformset.is_valid():
gradeformset.save()
grades = grades.objects.filter(studentId=id)
return render(request, 'grade_details.html', 'student': student, 'grades': grades)
else:
gradeformset = GradeFormSet(instance=student)
grades = grades.objects.filter(studentId=id)
zips = zip(grades, gradeformset)
return render(request, 'grade_edit.html', 'zips': zips, 'student': student, 'gradeformset': gradeformset )
我的模板看起来像这样
<table>
<tr>
% for field in gradeformset.forms.0 %
% if not field.is_hidden %
<th> field.label </th>
% endif %
% endfor %
</tr>
% for f in gradeformset.management_form %
f
% endfor %
% for student, gradeform in zips %
<tr>
% for hidden in form.hidden_fields %
hidden
% endfor %
<td> student.name </td>
<td> gradeform.gradeA </td>
<td> gradeform.gradeB </td>
</tr>
% endfor %
</table>
您可以在此处阅读有关 Django 表单集的更多信息 http://whoisnicoleharris.com/2015/01/06/implementing-django-formsets.html
【讨论】:
以上是关于为啥 Django 中的只读表单字段是个坏主意?的主要内容,如果未能解决你的问题,请参考以下文章