inlineformset_factory最低要求

时间:2014-02-13 14:48:58

标签: django django-forms

使用inlineformset_factory我可以添加/删除与单个客户相关的电话号码。唯一的问题是,我想要为每位客户提供至少1个有效的电话号码。

以下是一些演示代码:

型号:

class Customer( models.Model ):
    name = models.CharField( max_length=255 )

class PhoneNumber( models.Model ):
    customer = models.ForeignKey( Customer )
    number = models.CharField( max_length=10 )

形式:

class CustomerForm( ModelForm ):
    class Meta:
        model = Customer
        fields = ['name']

class PhoneNumberForm( ModelForm ):
    class Meta:
        model = PhoneNumber
        fields = ['number']

好的,所以这很直接。 然后在我看来:

class Create( View ):
    template_name = 'path_to_template'
    CustomerForm = forms.CustomerForm
    PhoneNumberFormSet = inlineformset_factory (
        parent_model = Customer,
        model = PhoneNumber,
        form = PhoneNumberForm,
        extra = 1,
    )

    def get(self, request):
        # Return empty forms
        context = {
            'customer_form': self.CustomerForm,
            'phone_number_formset': self.PhoneNumberFormSet
        }
        render( request, self.template_name, context)

    def post(self, request):
        this_customer_form = self.CustomerForm( request.POST )

        if this_customer_form.is_valid():
            new_customer.save(commit=False)
            this_phone_number_formset = self.PhoneNumberFormSet(request.POST, instance=new_customer)

            if this_phone_number_formset.is_valid():
                new_customer.save()
                this_phone_number_formset.save()
                return HttpResponseRedirect(reverse_lazy('customer-detail', kwargs={'pk': new_customer.pk}))

        # Something is not right, show the forms again
        this_phone_number_formset = self.PhoneNumberFormSet(request.POST)
        context = {
            'customer_form': this_customer_form,
            'phone_number_formset': this_phone_number_formset
        }
        render( request, self.template_name, context)

你认为我明白了这一点。对于客户的编辑/更新视图也是如此。只有这样才能预先填充表格。

此时我需要的是每个客户至少需要1个有效PhoneNumber的方法。 我找到了类似的东西:

class RequiredFormSet(BaseFormSet):
    def __init__(self, *args, **kwargs):
        super(RequiredFormSet, self).__init__(*args, **kwargs)
        for form in self.forms:
            form.empty_permitted = False
来自https://stackoverflow.com/questions/2406537/django-formsets-make-first-required

但是当我在BaseInlineFormSet类上应用它时,它似乎无法工作。

Django 1.7似乎回答了我的意愿,但到目前为止还没有回答InlineModelFormSet。

有什么想法吗?

2 个答案:

答案 0 :(得分:0)

谢谢kezabella(django irc)。 似乎我通过继承BaseInlineFormset找到了solution

class RequiredFormSet(BaseInlineFormSet):
    def clean(self):
        for form in self.initial_forms:
            if not form.is_valid() or not (self.can_delete and form.cleaned_data.get('DELETE')):
                return
        for form in self.extra_forms:
            if form.has_changed():
                return
        raise ValidationError("No initial or changed extra forms")

顺便说一句,这些验证错误不会显示在{{formset.error}}中,而是显示在:

{{ formset.non_form_errors }}

答案 1 :(得分:0)

如果您只想设置最小值或最大值,则可以直接在inlineformset_factory中进行设置,这是我的最少输入一项的代码

from django.forms import inlineformset_factory

SubUnitFormSet = inlineformset_factory(
    Unit, SubUnit, form=SubUnitForm, min_num=1, validate_min=True, extra=0)

您需要在自己的视图中正确处理此问题。我正在使用CBV,这是我的代码供您参考

class UnitCreateView(PermissionRequiredMixin, SuccessMessageMixin, CreateView):
    permission_required = "core.add_unit"

    model = Unit
    form_class = UnitForm
    template_name = 'core/basic-info/unit_form.html'
    success_url = reverse_lazy('core:units')
    success_message = _("%(code)s was added successfully")

    def get_context_data(self, **kwargs):
        data = super(UnitCreateView, self).get_context_data(**kwargs)
        if self.request.POST:
            data['subunits'] = SubUnitFormSet(self.request.POST, )
        else:
            data['subunits'] = SubUnitFormSet()
        return data

    def form_valid(self, form):
        context = self.get_context_data()
        subunits = context['subunits']
        with transaction.atomic():
            if subunits.is_valid():
                self.object = form.save()
                subunits.instance = self.object
                subunits.save()
            else:
                return self.render_to_response(self.get_context_data(form=form))
        return super(UnitCreateView, self).form_valid(form)