Django Form Validation on Class Based View

给你一囗甜甜゛ 提交于 2020-08-02 04:21:51

问题


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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!