【问题标题】:"This field cannot be null" error in a django 1.5 ModelFormdjango 1.5 ModelForm 中的“此字段不能为空”错误
【发布时间】:2013-11-08 17:16:41
【问题描述】:

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

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

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

ModelForm的逻辑是:

  1. 检查应用程序是否已存在于数据库中。否则会引发 ValidationError
  2. 检查联系人邮件地址是否存在于数据库中。否则它会创建它
  3. 检查生成的要创建的警报对象是否已存在于 DB 中。如果存在,则会引发 ValidationError。

到目前为止一切正常,除了当我想使用不存在的联系邮件地址提交表单时,ModelForm 会引发“此字段不能为空”验证错误。

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

当我在数据库中使用现有邮件地址提交表单时,在第一次提交表单时一切正常。

如果你们能帮助指出我的代码有什么问题,以及为什么在每个 POST 数据都是正确的情况下会引发此错误,我将不胜感激。

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

在上述问题涉及的不同应用程序部分下方。我自愿删除了一些不用于此应用程序的 Model 字段以及从我的 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

模型表单

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

创建视图

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

【问题讨论】:

  • 如果你添加一些traceback,就会更容易分辨出哪里出了问题
  • 感谢您的评论。不幸的是,没有追溯。只有一个由 form_invalid 方法引发的状态码为 400 的 HttpResponse。我试图在不同的地方添加几个断点,却没有机会找到一些东西。我还尝试在引发状态 400 时转储正确的并包含每个模型对象的cleaned_data。
  • 但是This field cannot be null 异常来自哪里?和 http 400 可能是一个不同的问题,即将到来的 SuspitiousOperation,例如 csrf 保护;您是否将 csrf_token 添加到 ajax 请求中?
  • This field cannot be null 也不例外。它包含在 form.errors 中,用于通过 form_invalid 方法在 JSON 中返回的联系人字段键,我在客户端的 $.ajax 查询中进入 fail() 方法。顺便说一句,我没有将 csrf_token 添加到 Ajax 请求中。我会这样做,但我不确定这是否是根本原因。
  • 您确定您的问题中有最新版本的模型和表格吗?我问是因为 AlertForm 上的 Meta 类不包括不在 Alert 模型上的 custom_rule。

标签: python django forms


【解决方案1】:

答案描述here

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

【讨论】:

    猜你喜欢
    • 2014-04-05
    • 2021-02-13
    • 2023-03-28
    • 2016-05-22
    • 2014-03-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-09-24
    相关资源
    最近更新 更多