基于类视图的Django表单验证

时间:2015-05-01 04:41:28

标签: django forms validation django-class-based-views

我有一个非常简单的基于类的视图:

在views.py中:

class IncidentEdit(UpdateView):
    model=Incident
    fields = visible_field_list
    sucess_url = '/status'

按原样正常工作。我已关联CreateViewDeleteView等。我可以创建编辑和删除记录。现在要微调项目,我需要添加字段验证。

我的问题:在基于'model='而不是'form='的视图时,我在哪里放置基本验证码?

我可以更改所有内容以使用基于表单的视图,但整个想法是保持简单并且它有效,我只是没有表单验证,除了基本的' Field Required'在模型声明中定义的类型验证。

例如, 我需要确保一个字段等于另外两个字段的总和。喜欢,

ClassRoomTotal = NumBoys + NumGirls

如果总和与总数不匹配,则为validation error字段提出ClassRoomTotal

提前致谢。
我知道这是一个简单的答案。

建议如,"您不能这样做,您必须使用form=IncidentForm并定义表单类。"会有所帮助。

3 个答案:

答案 0 :(得分:10)

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)

另外,希望此链接有用。 http://ccbv.co.uk/projects/Django/1.7/django.views.generic.edit/UpdateView/

答案 1 :(得分:4)

那么,

  

你不能这样做,你必须使用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'

答案 2 :(得分:0)

同样的问题让我感到困惑,非常感谢aumo和Vinayak因为他们的答案给了我太多的启发!

作为初学者,我总是尝试直接使用“基于模型+类的视图+模板”结构来避免我的应用程序失控。

与CBV中覆盖form_valid函数的行为相同(由Vinayak回答),通过自定义Mixin类来封闭函数可能看起来更好。我的代码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

我希望这个答案可以帮助那些像我一样的初学者和自学朋友。