为什么Django出现“违反非空约束”错误?
错误信息:
列 "postal_code_id" 中的空值违反了非空约束
表单内容:
def add(request):
if request.method == 'POST':
address_form = AddressForm(request.POST)
company_form = CompanyForm(request.POST)
if address_form.is_valid() and company_form.is_valid():
print address_form.cleaned_data['postal_code'] # <-- prints (<PostalCode: V4N 1K6>, False)
address_form.save() # <------------------------------- occurs here
else:
print 'Address errors',address_form.errors
print 'Company errors', company_form.errors
else:
address_form = AddressForm()
company_form = CompanyForm()
return render(request, 'company/add.html', locals())
很明显,表单里确实有一个有效的 PostalCode
对象,所以我不明白为什么会说违反了非空约束。当然,我在处理表单时做了一些奇怪的事情:
class AddressForm(ModelForm):
postal_code = CharField(max_length=10, validators=[validate_postal_code])
city = CharField(max_length=50, validators=[validate_non_whitespace])
province = CharField(max_length=50, validators=[validate_non_whitespace])
country = CharField(max_length=50, initial='Canada', validators=[validate_non_whitespace])
def clean_postal_code(self):
code = self.cleaned_data['postal_code']
code = code.upper()
code = re.sub('[^A-Z0-9]', '', code)
code = code[:3] + ' ' + code[-3:]
return code
def clean_country(self):
country = self.cleaned_data['country']
try:
country = Country.objects.get(name__iexact=country)
except Country.DoesNotExist:
raise ValidationError('Country does not exist')
return country
def clean_province(self):
province = self.cleaned_data['province']
if not Province.objects.filter(name__iexact=province).exists():
raise ValidationError('Province does not exist')
return province
def clean(self):
data = self.cleaned_data
if 'country' in data and 'province' in data:
try:
data['province'] = Province.objects.get(country=data['country'], name__iexact=data['province'])
if 'city' in data:
data['city'] = City.objects.get_or_create(name__iexact=data['city'], province=data['province'], defaults={'name':data['city']})[0]
if 'postal_code' in data:
data['postal_code'] = PostalCode.objects.get_or_create(code=data['postal_code'], city=data['city'])
except Province.DoesNotExist:
self._errors['province'] = self.error_class(['Province does not exist in that Country'])
del data['province']
return data
class Meta:
exclude = ['postal_code']
model = Address
具体来说,我把 postal_code
字段替换成了一个文本字段,然后在 "clean" 方法里查找或创建这个对象。为什么这样会让 Django 感到困惑呢?最后它不是得到了需要的对象吗?
4 个回答
1
我之所以关注这个问题,是因为我想在表单保存之前,自动给表单数据添加一个 uploaded_by
字段。我使用的是 ModelForm
,并且把 uploaded_by
字段排除了。后来我发现,关于这个问题的一个相关问题的答案,给出了一个简洁的解决方案。
1
我不太懂Django,但也许你需要确保邮政编码在保存地址之前已经保存好?
2
你现在把 postal_code
这个字段给排除了,这样在保存的时候,模型表单就会跳过这个字段。我之前也遇到过类似的问题,最后不得不去查 django 的代码才能搞明白这个行为。顺便说一下,这个过程是值得的。
你应该做的是,直接设置 postal_code
字段使用的控件,而不是先排除再重新包含。
class AddressForm(ModelForm):
class Meta:
model = Address
widgets = {
'postal_code': CharField(max_length=10),
}
这样做可以让模型表单正确验证这个字段并保存。我为了简洁省略了你表单的其他部分。
补充:
在模型表单中用 CharField 来处理外键是非常麻烦的。相反,建议你把它转换成一个普通的表单。看起来你已经在定义大部分字段了。这样的话,就需要你自己来验证这些字段是否有效,并且是否已经在数据库中存在。你可以创建一个保存方法,让它的行为像模型表单的保存方法一样,这样就可以了。