基于 Class 视图的 Django 表单验证
Django Form Validation on Class Based View
我有一个非常简单的 Class 基于视图:
在views.py中:
class IncidentEdit(UpdateView):
model=Incident
fields = visible_field_list
sucess_url = '/status'
按原样工作正常。我关联了 CreateView
、DeleteView
等。我可以创建编辑和删除记录。现在要微调项目,我需要添加字段验证。
我的问题:当我的视图基于 'model='
而不是 'form='
时,我应该将基本验证代码放在哪里?
我可以更改所有内容以使用基于表单的视图,但整个想法是保持简单且有效,我只是没有表单验证,除了已定义的基本 'Field Required' 类型验证在模型声明中。
例如,
我需要确保一个字段等于另外两个字段的总和。比如,
ClassRoomTotal = NumBoys + NumGirls
如果总和与总数不匹配,则为 ClassRoomTotal
字段提出 validation error
。
提前致谢。
我知道这是一个简单的答案。
像 "You can't do that, you have to use form=IncidentForm
and define a form class." 这样的建议会有所帮助。
嗯,
you can't do that, you have to use form = IncidentForm
或者至少这是最简单的解决方案。
请注意,您必须使用 form_class = IncidentForm
,而不是 form = IncidentForm
,并保留 model = Incident
。
我不认为使用 ModelForm
会增加项目的复杂性,这正是他们的用例。换一种方式会使事情变得复杂。
它可以像这样简单:
class IncidentForm(ModelForm):
class Meta:
model = Incident
# Define fields you want here, it is best practice not to use '__all__'
fields = [...]
def clean(self):
cleaned_data = super(IncidentForm, self).clean()
field_1 = cleaned_data.get('field_1')
field_2 = cleaned_data.get('field_2')
field_3 = cleaned_data.get('field_3')
# Values may be None if the fields did not pass previous validations.
if field_1 is not None and field_2 is not None and field_3 is not None:
# If fields have values, perform validation:
if not field_3 == field_1 + field_2:
# Use None as the first parameter to make it a non-field error.
# If you feel is related to a field, use this field's name.
self.add_error(None, ValidationError('field_3 must be equal to the sum of field_1 and filed_2'))
# Required only if Django version < 1.7 :
return cleaned_data
class IncidentEdit(UpdateView):
model = Incident
form_class = IncidentForm
fields = visible_field_list
success_url = '/status'
class IncidentEdit(UpdateView):
...
def form_valid(self, form):
if form.cleaned_data['email'] in \
[i.email for i in Incident.objects.exclude(id=get_object().id)]:
# Assume incident have email and it should be unique !!
form.add_error('email', 'Incident with this email already exist')
return self.form_invalid(form)
return super(IncidentEdit, self).form_valid(form)
此外,希望这篇 link 有用。
http://ccbv.co.uk/projects/Django/1.7/django.views.generic.edit/UpdateView/
同样的问题让我很困惑,非常感谢aumo和Vinayak的回答给了我很大的启发!
作为初学者,我总是尝试直接使用 "Model + Class Based View + Templates" 结构来避免我的应用失控。
与 CBV 中覆盖 form_valid 函数的行为相同(由 Vinayak 回答),通过自定义 Mixin class 包含函数可能看起来更好。我的代码 asf(基于 django 2.0 版):
# models.py
class Incident(models.Model):
numboys = models.SmallIntegerField(default=0)
numgirls = models.SmallIntegerField(default=0)
classttl = models.SmallIntegerField(default=0)
# views.py
def retunTestPassedResp(request):
return HttpResponse()
class NumValidationMixin:
def form_valid(self, form):
data = self.request.POST
boys = data.get('numboys')
girls = data.get('numgirls')
ttl = data.get('classttl')
if boys and girls and ttl:
if int(ttl) == int(boys) + int(girls):
return super().form_valid(form)
# use form.errors to add the error msg as a dictonary
form.errors['input invalid'] = '%s + %s not equal %s'%(boys, girls, ttl)
form.errors['input invalid'] = 'losing input with boys or other'
return self.form_invalid(form)
class UpdateIncident(NumValidationMixin, UpdateView):
model = Incident
fields = ['numboys', 'numgirls', 'classttl']
success_url = reverse_lazy('test-passed')
# templates/.../Incident_form.html
[...]
<body>
{{form}}
{% if form.errors %}
<p>get error</p>
{{form.errors}}
{% endif %}
</body>
我也做了单元测试,通过了。
# tests.py
class IncidentUpdateTest(TestCase):
def setUp(self):
Incident.objects.create()
def test_can_update_with_right_data(self):
[...]
def test_invalid_error_with_illegal_post(self):
response = self.client.post(
reverse('update-incident', args=(1,)),
data={'numboys': '1', 'numgirls': '1', 'classttl': '3'}
)
self.assertEqual(Incident.objects.first().classttl, 0)
# testing response page showing error msg
self.assertContains(response, 'not equal')
更准确的代码示例和解释,请在 django document.
中找到
希望这个回答能对初学者和和我一样self-study的朋友有所帮助
我只是偶然发现了这个问题,虽然它是一个很老的问题,但我想我会补充一点。
以前这个问题让我有些困扰,我使用了一个直接的解决方案(如下所述):
在forms.py
中,假设我们有这样的形式:
forms.py
class MyForm(forms.ModelForm):
class Meta:
model = MyModel
fields = ('field_a', 'field_b')
...
...
def clean(self):
super(MyForm, self).clean()
# We do some kind of validation
if self['field_a'].value() and self['field_b'].value() is not None or '': # Check for none zero or null values
if self['field_a'].value() == self['field_b'].value(): # Check and raise error on some condition not being met, eg. here we don't want that the fields should have the same values
raise forms.ValidationError('The field values can not be equal!!')
接下来在 UpdateView 中我们声明 form_class
和基础模型。
最后在 模板 中,像这样显示 non-field errors:
abc.html
...
...
{{ form.non_field_errors }}
如果验证失败,预期的消息会显示在表单上。
相关 Django 文档 here.
我有一个非常简单的 Class 基于视图:
在views.py中:
class IncidentEdit(UpdateView):
model=Incident
fields = visible_field_list
sucess_url = '/status'
按原样工作正常。我关联了 CreateView
、DeleteView
等。我可以创建编辑和删除记录。现在要微调项目,我需要添加字段验证。
我的问题:当我的视图基于 'model='
而不是 'form='
时,我应该将基本验证代码放在哪里?
我可以更改所有内容以使用基于表单的视图,但整个想法是保持简单且有效,我只是没有表单验证,除了已定义的基本 'Field Required' 类型验证在模型声明中。
例如, 我需要确保一个字段等于另外两个字段的总和。比如,
ClassRoomTotal = NumBoys + NumGirls
如果总和与总数不匹配,则为 ClassRoomTotal
字段提出 validation error
。
提前致谢。
我知道这是一个简单的答案。
像 "You can't do that, you have to use form=IncidentForm
and define a form class." 这样的建议会有所帮助。
嗯,
you can't do that, you have to use
form = IncidentForm
或者至少这是最简单的解决方案。
请注意,您必须使用 form_class = IncidentForm
,而不是 form = IncidentForm
,并保留 model = Incident
。
我不认为使用 ModelForm
会增加项目的复杂性,这正是他们的用例。换一种方式会使事情变得复杂。
它可以像这样简单:
class IncidentForm(ModelForm):
class Meta:
model = Incident
# Define fields you want here, it is best practice not to use '__all__'
fields = [...]
def clean(self):
cleaned_data = super(IncidentForm, self).clean()
field_1 = cleaned_data.get('field_1')
field_2 = cleaned_data.get('field_2')
field_3 = cleaned_data.get('field_3')
# Values may be None if the fields did not pass previous validations.
if field_1 is not None and field_2 is not None and field_3 is not None:
# If fields have values, perform validation:
if not field_3 == field_1 + field_2:
# Use None as the first parameter to make it a non-field error.
# If you feel is related to a field, use this field's name.
self.add_error(None, ValidationError('field_3 must be equal to the sum of field_1 and filed_2'))
# Required only if Django version < 1.7 :
return cleaned_data
class IncidentEdit(UpdateView):
model = Incident
form_class = IncidentForm
fields = visible_field_list
success_url = '/status'
class IncidentEdit(UpdateView):
...
def form_valid(self, form):
if form.cleaned_data['email'] in \
[i.email for i in Incident.objects.exclude(id=get_object().id)]:
# Assume incident have email and it should be unique !!
form.add_error('email', 'Incident with this email already exist')
return self.form_invalid(form)
return super(IncidentEdit, self).form_valid(form)
此外,希望这篇 link 有用。 http://ccbv.co.uk/projects/Django/1.7/django.views.generic.edit/UpdateView/
同样的问题让我很困惑,非常感谢aumo和Vinayak的回答给了我很大的启发!
作为初学者,我总是尝试直接使用 "Model + Class Based View + Templates" 结构来避免我的应用失控。
与 CBV 中覆盖 form_valid 函数的行为相同(由 Vinayak 回答),通过自定义 Mixin class 包含函数可能看起来更好。我的代码 asf(基于 django 2.0 版):
# models.py
class Incident(models.Model):
numboys = models.SmallIntegerField(default=0)
numgirls = models.SmallIntegerField(default=0)
classttl = models.SmallIntegerField(default=0)
# views.py
def retunTestPassedResp(request):
return HttpResponse()
class NumValidationMixin:
def form_valid(self, form):
data = self.request.POST
boys = data.get('numboys')
girls = data.get('numgirls')
ttl = data.get('classttl')
if boys and girls and ttl:
if int(ttl) == int(boys) + int(girls):
return super().form_valid(form)
# use form.errors to add the error msg as a dictonary
form.errors['input invalid'] = '%s + %s not equal %s'%(boys, girls, ttl)
form.errors['input invalid'] = 'losing input with boys or other'
return self.form_invalid(form)
class UpdateIncident(NumValidationMixin, UpdateView):
model = Incident
fields = ['numboys', 'numgirls', 'classttl']
success_url = reverse_lazy('test-passed')
# templates/.../Incident_form.html
[...]
<body>
{{form}}
{% if form.errors %}
<p>get error</p>
{{form.errors}}
{% endif %}
</body>
我也做了单元测试,通过了。
# tests.py
class IncidentUpdateTest(TestCase):
def setUp(self):
Incident.objects.create()
def test_can_update_with_right_data(self):
[...]
def test_invalid_error_with_illegal_post(self):
response = self.client.post(
reverse('update-incident', args=(1,)),
data={'numboys': '1', 'numgirls': '1', 'classttl': '3'}
)
self.assertEqual(Incident.objects.first().classttl, 0)
# testing response page showing error msg
self.assertContains(response, 'not equal')
更准确的代码示例和解释,请在 django document.
中找到希望这个回答能对初学者和和我一样self-study的朋友有所帮助
我只是偶然发现了这个问题,虽然它是一个很老的问题,但我想我会补充一点。
以前这个问题让我有些困扰,我使用了一个直接的解决方案(如下所述):
在forms.py
中,假设我们有这样的形式:
forms.py
class MyForm(forms.ModelForm):
class Meta:
model = MyModel
fields = ('field_a', 'field_b')
...
...
def clean(self):
super(MyForm, self).clean()
# We do some kind of validation
if self['field_a'].value() and self['field_b'].value() is not None or '': # Check for none zero or null values
if self['field_a'].value() == self['field_b'].value(): # Check and raise error on some condition not being met, eg. here we don't want that the fields should have the same values
raise forms.ValidationError('The field values can not be equal!!')
接下来在 UpdateView 中我们声明 form_class
和基础模型。
最后在 模板 中,像这样显示 non-field errors:
abc.html
...
...
{{ form.non_field_errors }}
如果验证失败,预期的消息会显示在表单上。
相关 Django 文档 here.