Django How to override a child form in inlineformset_factory

1.1k views Asked by At

I'm trying to override concept queryset in my child form, to get a custom list concepts based on the area got from request.POST, here is my list of concepts, which i need to filter based on the POST request, this lists is a fk of my child form (InvoiceDetail). is it possible to have these filters?

List of concepts

after doing some test when I pass the initial data as the documentation says initial=['concept'=queryset_as_dict], it always returns all the concepts, but i print the same in the view and its ok the filter, but is not ok when i render in template, so I was reading that I need to use some BaseInlineFormset. so when I test I obtained different errors:

django.core.exceptions.ValidationError: ['ManagementForm data is missing or has been tampered with']
'InvoiceDetailFormFormSet' object has no attribute 'fields'

so here is my code: models.py

class ConceptDetail(CreateUpdateMixin): # here, is custom list if area='default' only returns 10 rows. 
    name = models.CharField(max_length=150)
    area = models.ForeignKey('procedure.Area')

class Invoice(ClusterableModel, CreateUpdateMixin): # parentForm
    invoice = models.SlugField(max_length=15)

class InvoiceDetail(CreateUpdateMixin):  # childForm
    tax = models.FloatField()
    concept = models.ForeignKey(ConceptDetail, null=True, blank=True) # fk to override using custom queryset
    invoice = models.ForeignKey('Invoice', null=True, blank=True)

views.py

class CreateInvoiceProcedureView(LoginRequiredMixin, PermissionRequiredMixin, CreateView):
    template_name = 'invoice/invoice_form.html'
    model = Invoice
    permission_required = 'invoice.can_check_invoice'
        def post(self, request, *args, **kwargs):
    self.object = None
    form = InvoiceForm(request=request)
    # initial initial=[{'tax': 16, }] removed
    invoice_detail_form = InvoiceDetailFormSet(request.POST, instance=Invoice,
                                               request=request)
    return self.render_to_response(
        self.get_context_data(
            form=form,
            invoice_detail_form=invoice_detail_form
        )
    )

forms.py

class BaseFormSetInvoice(BaseInlineFormSet):
    def __init__(self, *args, **kwargs):
        # call first to retrieve kwargs values, when the class is instantiated
        self.request = kwargs.pop("request")
        super(BaseFormSetInvoice, self).__init__(*args, **kwargs)
        self.queryset.concept = ConceptDetail.objects.filter(
            Q(area__name=self.request.POST.get('area')) | Q(area__name='default')
        )

class InvoiceForm(forms.ModelForm):
    class Meta:
        model = Invoice
        fields = ('invoice',)

class InvoiceDetailForm(forms.ModelForm):
    class Meta:
        model = InvoiceDetail
        fields = ('concept',)

InvoiceDetailFormSet = inlineformset_factory(Invoice, InvoiceDetail,
                                             formset=BaseFormSetInvoice,
                                             form=InvoiceDetailForm,
                                             extra=1)

How can i fix it?, what do i need to read to solve this problem, I tried to debug the process, i didn't find answers. i try to do this:

def FooForm(forms.ModelForm):
    def __init__(self, *args, **kwargs):
        super(FooForm, self).__init__(*args, **kwargs)
        self.fields['concept'].queryset = ConceptDetail.objects.filter(area__name='default')

In a inlineformset_factory how can do it?.

1

There are 1 answers

0
paridin On

After a lot of tests, my solution is override the formset before to rendering, using get_context_data.

def get_context_data(self, **kwargs):
        context = super(CreateInvoiceProcedureView, self).get_context_data(**kwargs)
        for form in context['invoice_detail_form']:
            form.fields['concept'].queryset = ConceptDetail.objects.filter(area__name=self.request.POST.get('area'))
        return context