Search code examples
pythondjangodjango-modelsm2m

Access m2m relationships on the save method of a newly created instance


I'd like to send emails (only) when Order instances are created. In the email template, I need to access the m2m relationships. Unfortunatly, its seems like the m2m relations are ont yet populated, and the itemmembership_set.all() method returns an empty list.

Here is my code:

class Item(models.Model):
   ...

class Order(models.Model):
    ...
    items = models.ManyToManyField(Item, through='ItemMembership')

    def save(self, *args, **kwargs):
        pk = self.pk

        super(Order, self).save(*args, **kwargs)

        # If the instance is beeing created, sends an email with the order
        # details.
        if not pk:
            self.send_details_email()

    def send_details_email(self):
        assert len(self.itemmembership_set.all()) != 0

class ItemMembership(models.Model):
    order = models.ForeignKey(Order)
    item = models.ForeignKey(Item)
    quantity = models.PositiveSmallIntegerField(default=1)

Solution

  • Some of the comments suggested using signals. While you can use signals, specifically the m2m_changed signal, this will always fire whenever you modify the m2m fields. As far as I know, there is no way for the sender model (in your sample, that is ItemMembership) to know if the associated Order instance was just created or not.

    Sure, you can probably use the cache framework to set a temporary flag upon calling save() of an Order object, then read that same flag on the m2m_changed signal and delete the flag when it is over. The downside is you have to validate the process, and it beats the purpose of using signals which is to decouple stuff.

    My suggestion is to totally remove all those email sending functionalities from your models. Implement it as a helper function instead, and then just invoke the helper function explicitly after an Order object with its associated ItemMembership objects have been successfully created. IMHO, it also makes debugging a lot easier.