XelharK
XelharK

Reputation: 609

Django multiple many to many and post_save processing

I have a model that contains multiple many to many fields:

class Author(models.Model):
    name = models.CharField(max_length=30)

class Topic(models.Model):
    description = models.CharField(max_length=30)

class Article(models.Model):
    authors = models.ManyToManyField(Author, related_name='articles')
    topics = models.ManyToManyField(Topic, related_name='articles')

I need something pretty simple:

A method to be executed after the article is saved where I can access both authors and topics of that instance.

My first attempt has been with a post_save signal, but the signal is fired when the model itself is saved, not after its relationships are saved, which come from a through model obviously.

After some online reading I realized that I probably need to create my own signal and connect to it. The problem is that I have no idea what to overwrite and where to fire that signal.

Since I need this on multiple models, I thought I could create some M2MPostSaveModel class and have my models inherit it so I can just catch the signals..

But where does Django sends the signals? How can I overwrite it? I honestly have no idea and I had no luck searching in the docs, so apologies if it was there already and I didn't see it.

Upvotes: 1

Views: 659

Answers (2)

XelharK
XelharK

Reputation: 609

I finally did it, here's how: I created a new signal

import django.dispatch
m2m_post_save = django.dispatch.Signal(providing_args=["instance"])

Then I created a subclass of the ModelAdmin class that fires it after it finishes saving the related elements:

class M2MPostSaveModelAdmin(ModelAdmin):

    def save_related(self, request, form, formsets, change):
        super(M2MPostSaveModelAdmin, self).save_related(request, form, formsets, change)
        m2m_post_save.send(sender=self.__class__, instance=form.instance)

Now all I have to do is to subclass my ModelAdmin class with my M2MPostSaveModelAdmin class and hook up the signal to my method.

def doo_something_with_updated_instance(instance, **kwargs):
    # Here your instance has all the m2m relationships updated

m2m_post_save.connect(doo_something_with_updated_instance sender=YourModelAdminClass)

Upvotes: 3

Raja Simon
Raja Simon

Reputation: 10315

I used m2m_changed for separate processing.

def topics_changed(sender, instance, **kwargs):
    # Do something

m2m_changed.connect(topics_changed, sender=Article.topics.through)

so do with authors also

Upvotes: 0

Related Questions