【问题标题】:Django Form Validation on Class Based View基于类的视图上的 Django 表单验证
【发布时间】:2015-07-10 23:30:27
【问题描述】:

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

在views.py中:

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

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

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

我可以更改所有内容以使用基于表单的视图,但整个想法是保持简单且有效,我只是没有表单验证,除了模型中定义的基本“必填字段”类型验证声明。

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

ClassRoomTotal = NumBoys + NumGirls

如果总和与总数不匹配,则为 ClassRoomTotal 字段生成 validation error。

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

诸如“你不能这样做,你必须使用form=IncidentForm 并定义一个表单类”​​之类的建议。会有帮助的。

【问题讨论】:

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


    【解决方案1】:

    同样的问题让我很困惑,非常感谢 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找到。

    希望这个回答对和我一样的初学者和自学朋友有所帮助。

    【讨论】:

      【解决方案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)
      

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

      【讨论】:

      • 这真的很有帮助。只是想问一下,与声明自己的表单相比,这种方法在任何方面都不会那么有效;给一个form_class?
      • @ShivamSharma:我从来没有遇到过这种方法缺乏有效性的问题。我也没有看到任何性能瓶颈。如果您发现任何请提及,我想寻找解决方案。在我的示例中,用 values_list 查询集方法替换列表理解将是一个好主意。当您需要完成多个验证时,编写表单可能看起来更简洁。
      【解决方案3】:

      嗯,

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

      【讨论】:

      • 您的示例看起来很有希望。按照这个示例,我在 forms.py 文件中创建了一个 ModelForm。在视图中从“model=Incident”切换到“form=IncidentForm”时,出现以下错误:“IncidentEdit 缺少 QuerySet。定义 IncidentEdit.model、IncidentEdit.queryset 或覆盖 IncidentEdit.get_queryset()。”但是,如果我同时添加 model=Incident 和 form=IncidentForm,它的行为与添加 form=IncidentForm 之前的行为相同。鉴于我最初的基于四行类的视图......使用我定义的模型表单应该是什么样子?
      • 您需要使用form_class = IncidentForm并保留model = Incident。
      • 好的...感谢aumo...它有效。对于那里的其他新手:在视图中,我需要添加 form_class=IncidentForm NOT form= ...并且您也需要 model=Incident ...它使用该行来检索要编辑的项目和要处理的 form_class带有表单验证。同样在 forms.py 文件中,我需要添加“from django.core.exceptions import ValidationError”,否则 django 会说“未定义 ValidationError”。感谢 aumo 的回答...我尝试阅读文档,这让我感到困惑,但您的示例帮助我修复了它并重新开始编码。
      猜你喜欢
      • 1970-01-01
      • 2021-07-17
      • 1970-01-01
      • 2018-08-31
      • 2020-08-25
      • 2015-08-25
      • 1970-01-01
      • 2017-04-06
      • 1970-01-01
      相关资源
      最近更新 更多