Search code examples
djangoinline-formset

django inline_formset - set queryset for a field based on another field


I have three models - Item, Invoice and InvoiceItem. Each Item may have one or more tax-groups (name,rate, etc) associated with them.

class Item(models.Model):
    name=models.CharField(max_length=100, unique=True, db_index=True)
    tax_group=models.ManyToManyField(TaxGroup)

class Invoice(models.Model):
    number=models.CharField("invoice number",max_length=20,unique=True,default=invoiceIncrement,  )
    date=models.DateField("invoice date")
    contact=models.ForeignKey(Contact, on_delete=models.CASCADE)
    total=models.DecimalField(decimal_places=2, max_digits=12)

class InvoiceItem(models.Model):
    invoice=models.ForeignKey(Invoice, on_delete=models.CASCADE, related_name='invoice_items')
    item=models.ForeignKey(Item, on_delete=models.CASCADE)
    tax=models.ForeignKey(TaxGroup, on_delete=models.CASCADE)
    quantity=models.PositiveIntegerField()
    rate=models.DecimalField(decimal_places=2, max_digits=12)
    total=models.DecimalField(decimal_places=2, max_digits=12,null=True)

I have made an inline_formset using the following form, as given below.

class InvoiceItemForm(forms.ModelForm):
    class Meta:
        model=InvoiceItem
        exclude=()

ItemInlineFormSet = inlineformset_factory(Invoice, 
    InvoiceItem, form=InvoiceItemForm, 
    extra=1, can_delete=False,
      validate_min=True, min_num=1)

Now, I need to make sure that, corresponding to each selected Item in the formset, the field tax should contain only the taxes associated with them.

For the client side, I have added some ajax code to populate the tax fields based on selection of each item field.

How do I make sure that the selected tax field is one among the ManyToMany value of the Item object?

I have tried specified a custom formset to set a queryset to the field, like

class BaseItemFormSet(forms.BaseInlineFormSet):
    def __init__(self, *args, **kwargs):
        super(BaseItemFormSet, self).__init__(*args, **kwargs)

        for form in self.forms:
            form.fields['tax'].queryset = Item.objects.get(form.fields['item']).tax_group.all()

This doesn't work (and I'm not sure whether this is the right way to proceed).

Do I need to use a clean() method in the formset to validate each of these fields?

Please help,

Thanks.


Solution

  • Here's how I did it.

    def __init__(self, *args, **kwargs):
            super(InvoiceItemFormSet, self).__init__(*args, **kwargs)
            if self.data:
                i=0
                for form in self.forms:
                    key = 'item-'+str(i)+'-item'
                    if self.data[key]:
                        form.fields['tax'].queryset= Item.objects.get(id=self.data[key]).tax_group.all()
                    i=i+1
    

    (possible simplifications are welcomed)