django内联编辑 - 只有在填写了至少一个字段时才需要内联表单

时间:2009-12-31 16:18:30

标签: django django-forms

我创建了一个视图,它返回一个包含联系表单和两个phone_number表单的表单,如下例所示:

multiple forms

只有当用户在电话号码表格中至少插入一个字段的值时,才应验证电话号码表格。例如:电话号码具有类型和编号。如果用户正在选择类型,则需要该号码。

现在我想知道如何在视图中检查用户是否插入了值/选择了类型或插入了数字。它应该像管理员一样在内联编辑模型。

我的观点如下:

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 }}

2 个答案:

答案 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 )。

表单集足够智能,如果在表单集的一种形式中没有输入任何信息,它就不会强制执行所需的元素 - 但如果填写了一个元素,它将强制执行所有验证要求。这听起来应该可以解决你的问题。