Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Using Django's m2m_changed to modify what is being saved pre_add

I am not very familiar with Django's signals and could use some help.

How do I modified the pk_set before the instance is saved? Do I have to return something to the signal caller (like the kwargs)? Or do I save the instance myself?

As a simplified example: I wanted to ensure the Category with pk=1 is included with all my Videos when they are saved. How would I do that with m2m_changed?

class Video(models.Model):
    category = models.ManyToManyField('Category')

def video_category_changed(sender, **kwargs):
    action = kwargs.pop('action', None)
    pk_set = kwargs.pop('pk_set', None)
    instance = kwargs.pop('instance', None)

    if action == "pre_add":
        if 1 not in pk_set:
            pk_set.update( [ 1 ] )  # adding this to the set
            # do something else?
            # profit?

m2m_changed.connect( video_category_changed, sender=Video.category.through )
like image 519
thornomad Avatar asked Oct 21 '14 18:10

thornomad


People also ask

What is m2m_changed?

m2m_changed. Sent when a ManyToManyField is changed on a model instance. Strictly speaking, this is not a model signal since it is sent by the ManyToManyField , but since it complements the pre_save / post_save and pre_delete / post_delete when it comes to tracking changes to models, it is included here.

What is the use of the Post_delete signal in Django?

Django Signals - post_delete() To notify another part of the application after the delete event of an object happens, you can use the post_delete signal.

What is pre save signal in Django?

pre_save) is provoked just before the model save() method is called, or you could say model save method is called only after pre_save is called and done its job free of errors.

How do you use signals in Django?

There are 3 types of signal. pre_save/post_save: This signal works before/after the method save(). pre_delete/post_delete: This signal works before after delete a model's instance (method delete()) this signal is thrown.


1 Answers

Just updating the pk_set is sufficient. You don't need to do any extra work. Once the video instance is saved, it will have a category with pk=1.

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

class Category(models.Model):
    pass

class Video(models.Model):
    category = models.ManyToManyField('Category')

@receiver(m2m_changed, sender=Video.category.through)
def video_category_changed(sender, **kwargs):
    action = kwargs.pop('action', None)
    pk_set = kwargs.pop('pk_set', None)    
    if action == "pre_add":
        if 1 not in pk_set:
            pk_set.update([1])

In the above method, the categories will be saved only after the video instance is saved. If you want to EXPLICITLY save them in the m2m_changed instance, you can also do that as follows.

@receiver(m2m_changed, sender=Video.category.through)
def video_category_changed(sender, **kwargs):
    instance = kwargs.pop('instance', None)
    pk_set = kwargs.pop('pk_set', None)
    action = kwargs.pop('action', None)
    if action == "pre_add":
        if 1 not in pk_set:
            c = Category.objects.get(pk=1)
            instance.category.add(c)
            instance.save()
like image 142
Pandikunta Anand Reddy Avatar answered Oct 12 '22 16:10

Pandikunta Anand Reddy