问题
I have a very simple Class Based View:
In views.py:
class IncidentEdit(UpdateView):
model=Incident
fields = visible_field_list
sucess_url = '/status'
works fine as-is. I have associated CreateView
, DeleteView
, etc. I can create edit and delete records. Now to fine-tune the project, I need to add field validation.
My question: Where do I put basic validation code when I have based the view on the 'model='
rather than 'form='
?
I could change everything to use form based views, but the whole idea was to keep it simple and it works, I just don't have the form validation except for basic 'Field Required' type validation that was defined in the model declaration.
For example, I need to make sure that one field equals the sum of two other fields. Like,
ClassRoomTotal = NumBoys + NumGirls
and raise a validation error
for the ClassRoomTotal
field if the sum doesn't match the total.
Thanks in advance.
I know it is a simple answer.
Suggestions like, "You can't do that, you have to use form=IncidentForm
and define a form class." would help.
回答1:
Well,
you can't do that, you have to use
form = IncidentForm
or at least it is the simplest solution.
Note that you have to use form_class = IncidentForm
, not form = IncidentForm
and keep model = Incident
.
I don't see using ModelForm
as something that will increase your project's complexity, this is exactly their use case. Doing it another way would be making things complex.
It can be as simple as:
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'
回答2:
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)
Also, hope this link would be useful. http://ccbv.co.uk/projects/Django/1.7/django.views.generic.edit/UpdateView/
回答3:
The same problem confuse me, many thanks to aumo and Vinayak for theirs answers inspired me so much!
As a beginner, I'm always try to use the "Model + Class Based View + Templates" structure directly to avoid my app getting out of control.
Same behavior with overriding form_valid function in CBV (answered by Vinayak), enclose the function by customizing Mixin class might look better. My code asf(base on django version 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>
I also make a unittest, and got passed.
# 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')
For more exact code example and explaining, pls find in django document.
I wish this answer will help those beginners and self-study friends who are like me.
来源:https://stackoverflow.com/questions/29981690/django-form-validation-on-class-based-view