django 1.5 ModelForm中的“此字段不能为空”错误

时间:2013-11-08 17:16:41

标签: python django forms

我设法创建了一个ModelForm,它基本上在一个链接到环境中的应用程序的DB中插入一个Alert对象,该环境将邮件发送到Contact(使用ForeignKey管理)。我的表单(基于CreateView基于类的表单)由3个字段组成:

  • 应用程序的CharField
  • 环境的ModelChoiceField
  • 联系人的电子邮件地址

CreateView与一些AJAX逻辑混合在一起,使提交事物整体动态化(即绘制错误的工具提示,而不是在每个字段旁边重新加载页面而不是在表单上方出现大错误)。

ModelForm的逻辑是:

  1. 检查数据库中是否已存在该应用程序。否则会引发ValidationError
  2. 检查DB中是否存在联系人邮件地址。否则就会创建它
  3. 检查要创建的结果Alert对象是否已存在于DB中。如果存在,则会引发ValidationError。
  4. 到目前为止一切正常,除非我想使用不存在的联系人邮件地址提交我的表单,ModelForm会引发“此字段不能为空”验证错误。

    我真的没有找到我做错的事,因为我在ModelForm的clean_contact()方法中使用get_or_create()方法来插入此联系人,如果需要,然后返回结果对象以更新self.cleaned_data字典。最糟糕的是,当我第二次提交表单而不更改任何字段时,一切运行顺利(没有更多验证错误)......

    当我在数据库中提交包含现有邮件地址的表单时,在第一个表单提交时一切正常。

    如果你们能帮助指出我的代码中出现了什么问题以及为什么会出现这个错误而每个POSTed数据都是正确的,我真的很感激。

    但是我对我的CreateView的AJAX mixin有点怀疑,因为可能是因为当一个邮件地址未知时,get_or_create()创建并返回它但是 - 我无法想象为什么 - 创建Alert对象尚未引用新创建的Contact对象。这可以解释为什么第二次提交有效...我相信你们会得到最后的结论: - )

    下面涉及上述问题的不同应用部分。我自愿删除了一些未用于此应用程序的模型字段以及从我的CreateView继承的LoginRequiredMixin。

    我再次非常感谢你的帮助,并提前感谢你的每一条建议。

    模型

    class UmsAlerting(models.Model):
        alert_id = models.IntegerField(primary_key=True, editable=False)
        appli = models.ForeignKey('UmsApplication')
        env = models.ForeignKey('UmsEnvironment')
        contact = models.ForeignKey('UmsContacts')
        class Meta:
            db_table = 'ums_alerting'
        def __unicode__(self):
            return u'Alert_Id %d on %s(%s)' %(self.alert_id, self.appli.trigram_ums, self.env.env_name)
    
    
    class UmsApplication(models.Model):
        appli_id = models.IntegerField(primary_key=True)
        trigram_ums = models.CharField(max_length=4L)
        class Meta:
            db_table = 'ums_application'
    
    
    class UmsContacts(models.Model):
        contact_id = models.IntegerField(primary_key=True)
        mail_addr = models.CharField(max_length=100L)
        class Meta:
            db_table = 'ums_contacts'
    
    
    class UmsEnvironment(models.Model):
        env_id = models.IntegerField(primary_key=True)
        env_name = models.CharField(max_length=5L)
        class Meta:
            db_table = 'ums_environment'
    
        def __unicode__(self):
            return self.env_name
    

    的ModelForm

    class AlertForm(ModelForm):
        class Meta:
            model = UmsAlerting
            exclude = ('custom_rule')
    
        appli = forms.CharField(required=True, max_length=3)
        env = forms.ModelChoiceField(required=True,
                                     queryset=UmsEnvironment.objects.all())
        contact = forms.EmailField(required=True)
    
        def clean_appli(self):
            data = self.cleaned_data['appli']
    
            try:
                UmsApplication.objects.get(trigram_ums=data)
            except ObjectDoesNotExist:
                msg = 'Trigram must be known and valid.'
                self._errors['appli'] = self.error_class([msg])
                raise forms.ValidationError(msg)
    
            return UmsApplication.objects.get(trigram_ums=data)
    
        def clean_contact(self):
            data = self.cleaned_data['contact']
            c, created = UmsContacts.objects.get_or_create(mail_addr=data)
    
            return c
    
    
        def clean(self):
            cleaned_data = super(AlertForm, self).clean()
            app = cleaned_data.get('appli')
            contact = cleaned_data.get('contact')
            env = cleaned_data.get('env')
    
            # Do not insert a new alert if it already exists
            if UmsAlerting.objects.filter(appli=app, env=env, contact=contact).count() > 0:
                msg = 'Alert is already configured.'
                self._errors['contact'] = self.error_class([msg])
                raise forms.ValidationError(msg)
    
            # Return the parent's clean method finally
            return cleaned_data
    

    CreateView的

    class AlertView(LoginRequiredMixin, AjaxResponseMixin, CreateView):
         template_name = 'tools/alert_form.html'
         form_class = AlertForm
         success_url = reverse_lazy('alerts_configure')
    

    AjaxResponseMixin

    class AjaxResponseMixin(object):
         def render_to_json_response(self, context, **kwargs):
             data = json.dumps(context)
             kwargs['content_type'] = 'application/json'
             return HttpResponse(data, **kwargs)
    
         def form_invalid(self, form):
             response = super(AjaxResponseMixin, self).form_invalid(form)
             if self.request.is_ajax():
                 return self.render_to_json_response(form.errors, status=400)
             else:
                 return response
    
         # Not really useful actually (yet)
         def form_valid(self, form):
             response = super(AjaxResponseMixin, self).form_valid(form)
             if self.request.is_ajax():
                 return self.render_to_json_response(json.dumps({}))
             else:
                 return response
    

1 个答案:

答案 0 :(得分:2)

答案描述为here

简而言之,UmsContacts模型正在使用IntegerField,因此即使在数据库表上设置了自动增量,也必须指定它来创建像这样的新对象。解决方案是将其更改为AutoField以及在self.cleaned_data['contact']方法中返回已修改的clean_contact