Question

We have a class-based-generic view that adds an object with a ManyToManyField relationship. We're trying to modify some values on save() override for the relationships created on that ManyToManyField, but it doesn't seem like they ever get created in save(), so we're forced to call a post_save method in get_success_url so the relationships are created. This seems very, very ugly. How do we force the relationships to be created before we leave save()?

I've seen this answer on SO, which says to override form_valid() to create the relationship, but why do I need to do this if they're created automatically somewhere between form_valid() and get_success_url()?

# models.py
class Payment(models.Model):
    invoice = models.ManyToManyField(Invoice)
    deposit_date = models.DateField()
    check_number = models.CharField(max_length=100, blank=True, null=True)
    description = models.TextField(blank=True, null=True)

    def post_save(self):
        """
        ManyToManyField relationships aren't formed yet when overriding save() for Class Based Generic
        Views. Call this whenever you're saving a Payment object.
        """
        for invoice in self.invoice.all():
            # Do some calcualtions that make us happy
            invoice.save()

# views.py - We override get_success_url() in UpdateView too.
class PaymentAdd(CreateView):
    model = Payment
    form_class = PaymentForm
    context_object_name = 'object'
    template_name = 'form.html'

    def get_success_url(self):
        self.object.post_save()
        return reverse('payment-page')
Was it helpful?

Solution

If this has to be done each time a Payment is saved, it's better to leave that responsibility to the model instead of moving it to view. The best way is to use the post_save signal, as something like this:

In models.py, somewhere below the definition of the Payment model:

from django.db.models.signals import post_save
from django.dispatch import receiver

@receiver(post_save, sender=Payment)
def my_handler(sender, instance, **kwargs):
    for invoice in instance.invoice.all():
        # Do some calcualtions that make us happy
        invoice.save()

On an unrelated note, I strongly suggest you to use plural attribute names for M2M fields: invoices instead of invoice in this case. It makes it much clearer to your fellow developers.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top