我设法创建了一个 ModelForm,它基本上在 DB 中插入一个 Alert 对象,该对象链接到环境上的应用程序,该环境将向联系人发送邮件(由 ForeignKey 管理)。我的表单(基于基于 CreateView 类的表单)由 3 个字段组成:
- 应用程序的 CharField
- 环境的 ModelChoiceField
- 联系人的 EmailField
CreateView 与一些 AJAX 逻辑混合在一起,以使提交内容整体动态化(即为错误绘制工具提示,而无需在每个字段旁边重新加载页面,而不是在表单上方出现大错误)。
ModelForm 的逻辑是:
- 检查应用程序是否已存在于数据库中。否则会引发 ValidationError
- 检查联系人邮件地址是否存在于数据库中。否则它会创建它
- 检查生成的要创建的警报对象是否已存在于数据库中。如果存在,则会引发 ValidationError。
到目前为止一切正常,除非我想使用不存在的联系邮件地址提交表单,ModelForm 会引发“此字段不能为空”验证错误。
我真的没有发现我做错了什么,因为我在 ModelForm 的 clean_contact() 方法中使用 get_or_create() 方法在需要时插入此联系人,然后返回结果对象以更新 self.cleaned_data 字典。最糟糕的是,当我第二次提交表单而不更改任何字段时,一切都运行顺利(不再出现验证错误)......
当我在数据库中使用现有邮件地址提交表单时,在第一次提交表单时一切正常。
如果你们能帮助指出我的代码中有什么问题以及为什么在每个发布的数据都是正确的情况下会引发此错误,我将非常感激。
但是我对带有我的 CreateView 的 AJAX 混合有点怀疑,因为可能是当邮件地址未知时, get_or_create() 创建并返回它但是 - 我无法想象为什么 - 创建 Alert 对象还不能引用新创建的联系人对象。这可以解释为什么第二次提交有效......我相信你们会得到最后的决定:-)
下面是上述问题涉及的不同应用程序部分。我自愿删除了一些不用于此应用程序的 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')
Ajax响应混合
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