Md. Tanvir Raihan
Md. Tanvir Raihan

Reputation: 4285

Validate django inline formset

i have used django inline formset so make a single crud on two models. Unlike a django form,django formset works with multiple forms at a time.So its really confusing for me to validate django formset.but i need to validate my django formset.I really i hasn't found any solution yet.

I have two models namely Discount and Discount products.I have used django formset for discount products formsets among with discount form.

By default formsets allows user to avoid all the form when creating a new instance or update a new instance,which actually make sense,because there can be 15 or 20 forms.In my system , i am also following the same way.

In my discount formsets, there are two fields namely primary variant and variants.

If user don't fill up any field of any forms of the formsets,he can do it and can create or update only discount form object,which is fine.(remember discount form is reside above the discount products formsets).

but if he fill only variant field and emty the primary variant field ,then the user should not allow to do this,because in my system ,a variant can not be created or updated without primary variant.But in this point i can't validating it.Because its a formsets.I really need idea how to do this type of validation.

here is he Create view where i am combining the discount and discount products.(Providing only the create view, i hope solution will be same for the update view also)

class DiscountCreate(RequestPassingFormViewMixin, WammuCreateView):
    model = Discount
    template_name = 'dashboard/discount_form.html'
    form_class = DiscountForm

    def get_form_kwargs(self, *args, **kwargs):
        kwargs = super(DiscountCreate, self).get_form_kwargs(*args, **kwargs)
        common = get_object_or_404(Common, pk=self.kwargs['common_pk'])
        discount = Discount(common=common)
        kwargs['instance'] = discount
        return kwargs

    def get(self, request, *args, **kwargs):
        self.object = None
        form_class = self.get_form_class()
        form = self.get_form(form_class)
        DiscountProductInlineFormSet = inlineformset_factory(Discount, DiscountProducts,
                                                        form=DiscountProductForm,
                                                        can_delete=True, extra=5)
        DiscountProductInlineFormSet.form = staticmethod(curry(DiscountProductForm, request=request))
        discount_product_formset = DiscountProductInlineFormSet(instance=self.object)
        return self.render_to_response(
        self.get_context_data(form=form, discount_formset=discount_product_formset))

   def post(self, request, *args, **kwargs):
        self.object = Discount()
        form = self.get_form(self.form_class)
        DiscountProductInlineFormSet = inlineformset_factory(Discount, DiscountProducts,
                                                        form=DiscountProductForm,
                                                        can_delete=True, extra=5)
        DiscountProductInlineFormSet.form = staticmethod(curry(DiscountProductForm, request=request))
        if form.is_valid():
            self.object = form.save(commit=False)
        else:
            discount_product_formset = DiscountProductInlineFormSet(request.POST,
                                                              instance=self.object)
            return self.render_to_response(
            context=self.get_context_data(form=form, discount_formset=discount_product_formset))

        promotion_product_formset = DiscountProductInlineFormSet(request.POST, instance=self.object)
        if discount_product_formset.is_valid():
            self.object.save()
            discount_product_formset.save()
            return super(Discountreate, self).form_valid(form)
        else:
            return self.render_to_response(
                context=self.get_context_data(form=form, discount_formset=discount_product_formset))

and this is my forms where both Discount and Disount Products resides,

class DiscountForm(RequestPassingFormMixin, ModelForm):
    class Meta:
        model = Discount

    def __init__(self, *args, **kwargs):
        super(PromotionForm, self).__init__(*args, **kwargs)
        common = Chain.objects.get(pk=self.request.session.get('chain_pk'))

    class DiscountProductForm(autocomplete_light.ModelForm):    
        class Meta:
            model = DiscountProducts
            fields = ['code', 'variants', 'primary_variant']

    def __init__(self, *args, **kwargs):
        self.request = kwargs.pop("request")
        super(DiscountProductForm, self).__init__(*args, **kwargs)
        common = Chain.objects.get(pk=self.request.session.get('common_pk'))
        self.fields['variants'].queryset = Variant.objects.filter(product__common__pk=common.pk)
        self.fields['primary_variant'].queryset = Variant.objects.filter(product__common__pk=common.pk) 

Upvotes: 2

Views: 3808

Answers (3)

Alex Polekha
Alex Polekha

Reputation: 1680

Fields in formset are validated if at least one field is not empty, so everything should work as you expect it by default. You can also read about cleaning and validating fields that depend on each other.

Upvotes: 2

Stiig
Stiig

Reputation: 1294

Just add arguments min_num=1and validate_min=True into inlineformset_factory

Upvotes: 1

Dean Christian Armada
Dean Christian Armada

Reputation: 7364

This is my solution when I encountered your problem, In your views.py

from django.forms.formsets import formset_factory, BaseFormSet

# Enables field required on formset even without filling up a singlefield
class RequiredFormSet(BaseFormSet):
     def __init__(self, *args, **kwargs):
         super(RequiredFormSet, self).__init__(*args, **kwargs)
        for form in self.forms:
            form.empty_permitted = False

def view(request):
    formset = formset_factory(CollegeForm, extra=5, formset=RequiredFormSet)

Upvotes: 0

Related Questions