我创建了一个视图,它返回一个包含联系表单和两个phone_number表单的表单,如下例所示:
只有当用户在电话号码表格中至少插入一个字段的值时,才应验证电话号码表格。例如:电话号码具有类型和编号。如果用户正在选择类型,则需要该号码。
现在我想知道如何在视图中检查用户是否插入了值/选择了类型或插入了数字。它应该像管理员一样在内联编辑模型。
我的观点如下:
def contact_add(request):
user = request.user
if request.method == 'POST':
cform = ContactForm(request.POST)
pforms = [PhoneNumberForm(request.POST, prefix=str(x)) for x in range(0,3)]
if cform.is_valid() and all([pf.is_valid() for pf in pforms]):
new_contact = cform.save(commit=False)
new_contact.created_by = user
new_contact.save()
for pf in pforms:
new_phone_number = pf.save(commit=False)
new_phone_number.contact = new_contact
new_phone_number.save()
request.user.message_set.create(message='Contact %s has been added.' % new_contact.__str__())
return HttpResponseRedirect("/crm/contacts/?oby=1")
else:
cform = ContactForm()
pforms = [PhoneNumberForm(prefix=str(x)) for x in range(0,3)]
return render_to_response(
'crm/contact_add.html',
{'cform': cform, 'pforms': pforms,},
context_instance = RequestContext(request),
)
在下面的第一个回复后编辑:
我尝试使用自定义验证来完成此任务,但没有达到令人满意的目的。为了简化我的任务,我稍微改变了用例。我创建了一个表格,其中包括一个联系表格和一个地址表格。只有填写了地址表格的至少一个字段才能验证地址表格,因为应该可以在不创建相应地址的情况下创建联系人。
首先,我尝试使用自定义验证,如下所示:
class AddressForm(forms.ModelForm):
class Meta:
model = Address
exclude = ('contact',)
def clean(self):
cleaned_data = self.cleaned_data
street = cleaned_data.get("street")
postal_code = cleaned_data.get("postal_code")
city = cleaned_data.get("city")
country = cleaned_data.get("country")
if not street and not postal_code and not city and not country:
#searching a better idea here
return 0
else:
return cleaned_data
但这并没有真正帮助,因为这样我就不会摆脱验证错误。
这使我认为clean方法是进行此验证的错误位置,我想我必须在POST.request中检查是否缺少Address Form的所有值。如果它们丢失了,我不会为地址表单调用is_valid()而只是忽略它。如果至少有一个值可用,我只是对地址表进行正常验证,而不覆盖clean()方法。
好主意还是坏主意? 如果这是一个好主意,我怎样才能轻松检查POST请求中的地址表的值。
可能我想要复杂化了: - )
编辑:使用FormSets的解决方案:
@login_required
def contact_add(request):
user = request.user
if request.method == 'POST':
cform = ContactForm(request.POST)
phonenumberformset = PhoneNumberFormSet(request.POST)
if cform.is_valid() and classificationformset.is_valid() and addressformset.is_valid() and phonenumberformset.is_valid():
new_contact = cform.save(commit=False)
new_contact.created_by = user
new_contact.save()
new_phonenumber_instances = phonenumberformset.save(commit=False)
for new_phonenumber in new_phonenumber_instances:
new_phonenumber.contact = new_contact
new_phonenumber.save()
request.user.message_set.create(message='Contact %s has been added.' % new_contact.__str__())
return HttpResponseRedirect("/crm/contacts/?oby=1")
else:
cform = ContactForm()
#By default, when you create a formset from a model, the formset will use
#a queryset that includes all objects in the model (e.g., Author.objects.all()).
#Here we want to present an empty formset in order to add a new object
phonenumberformset = PhoneNumberFormSet(queryset=PhoneNumber.objects.none())
return render_to_response(
'crm/contact_add.html',
{'cform': cform, 'phonenumberformset': phonenumberformset,},
context_instance = RequestContext(request),
)
请注意,这也可以使用inlineformset_factory来完成,有关详细信息,请参阅我的其他帖子:link
请注意,如果您使用的是FormSet,则必须在模板中为每个form_set包含一个management_form。 docs
否则会出现此错误:
[u'ManagementForm data is missing or has been tampered with']
在视图中使用formset就像使用常规Form类一样简单。您唯一需要注意的是确保在模板中使用管理表单。
{{ context.phonenumberformset.management_form }}
答案 0 :(得分:2)
您要做的是在表单上定义custom validation。
class PhoneNumberForm(forms.Form):
# Everything as before.
...
def clean(self):
cleaned_data = self.cleaned_data
phone1 = cleaned_data.get("phone1")
if phone1:
# validate manually, and if it doesn't pass:
self._errors["phone1"] = ErrorList(["Hey, this field is wrong."])
del cleaned_data["phone1"]
# Always return the full collection of cleaned data.
return cleaned_data
然后在视图中,您希望依赖Django的内置错误表单验证错误处理:
{{ pforms.phone1 }}
{{ pforms.phone1.errors }}
答案 1 :(得分:2)
你应该使用formsets而不是搞乱你的PhoneNumber子表单的动态前缀 - 它会使一切变得更容易,这确实是管理员管理内联表单的方式(另请参阅model formsets documentation )。
表单集足够智能,如果在表单集的一种形式中没有输入任何信息,它就不会强制执行所需的元素 - 但如果填写了一个元素,它将强制执行所有验证要求。这听起来应该可以解决你的问题。