Search code examples
pythondjangoformset

Formsets + Many to Many Relationship Problem


I have partially implemented the formsets + many to many relationship feature with an Invoice and Inventory Model.

My problem is the form does not show the available Inventory (even though they exist). See problem here: https://www.dropbox.com/s/mtqkfee2pisyh5a/dj005_formset_many_to_many_relationship_working.jpg?dl=0

Here is the working code:

# MODELS.PY
class Invoice_Test_M2M(models.Model):
    id = models.BigAutoField(primary_key=True)
    ref_num = models.CharField(max_length=100)

    def __str__(self):
        return self.ref_num

class Inventory_Test_M2M(models.Model):
    id = models.BigAutoField(primary_key=True)
    inventory_name = models.CharField(blank=True, max_length=100)
    invoice = models.ManyToManyField('Invoice_Test_M2M', through= "Invoice_Inventory_Through")
    
    def __str__(self):
         return self.inventory_name

class Invoice_Inventory_Through(models.Model):
    invoice = models.ForeignKey(Invoice_Test_M2M, on_delete=models.CASCADE)
    inventory = models.ForeignKey(Inventory_Test_M2M, on_delete=models.CASCADE)
    price = models.DecimalField(max_digits=9, decimal_places=2, blank=True, null=True)
    quantity = models.DecimalField(max_digits=9, decimal_places=2, blank=True, null=True)
    amount = models.DecimalField(max_digits=9, decimal_places=2, blank=True, null=True)

# FORMS.PY
Inventory_TestLineItem_M2M_Formset = inlineformset_factory(Invoice_Test_M2M, Invoice_Inventory_Through, fields = '__all__', exclude=[], can_delete=True)

# VIEWS.PY
class Invoice_M2M_CreateView(CreateView):
    model = Invoice_Test_M2M
    fields = '__all__'

    def get_context_data(self, **kwargs):
        context = super(Invoice_M2M_CreateView, self).get_context_data(**kwargs)
        if self.request.POST:
            context['track_formset'] = Inventory_TestLineItem_M2M_Formset(self.request.POST)
        else:
            context['track_formset'] = Inventory_TestLineItem_M2M_Formset()
        return context

    def form_valid(self, form):
        context = self.get_context_data(form=form)
        formset = context['track_formset']
        if formset.is_valid():
            response = super().form_valid(form)
            formset.instance = self.object
            formset.save()
            return response
        else:
            return super().form_invalid(form)

Is there a way around this?

PS. INTERESTINGLY, if I just use a pseudo many to many MODEL (i.e. using a foreign field and not a many to many field). It works.

You can see it working here: https://www.dropbox.com/s/32x84k8roa88jvf/dj005_formset_many_to_many_relationship_working_B.jpg?dl=0

So why not use this method? The main reason is the M2M has a built-in API in showing its relevant members (i.e. Shows the inventories of a specific Invoice). In the method above, I am under the impression I have to do it manually.

P.P.S. Related Stackoverflow post. I already implemented the solutions but I still get the same problem:

pendant to inline formsets for many-to-many relations

Accessing Many to Many "through" relation fields in Formsets


Solution

  • And just like that. After posting the question, I found the answer. Stupid me.

    Basically, the code I posted above works. The problem is I created a new model so technically speaking its empty so I couldn't find them in my drop down list.