on_save() is not triggered when model is created

Viewed 22

I got the two following models:

class Stuff(models.Model):
   ... 
   def custom_function(self):
   ...


class MyModel(models.Model):
    name = models.CharField(max_length=200)
    many_stuff = models.ManyToManyField(Stuff, related_name="many_stuff+")
    many_other_stuff = models.ManyToManyField(Stuff, related_name="many_other_stuff+")
 
    def __init__(self, *args, **kwargs):
        super(MyModel, self).__init__(*args, **kwargs)
        for stuff in self.many_stuff.all():
            many_stuff.custom_function()
            
    def on_save(self, *args, **kwargs):
        for stuff in self.many_stuff.all():
            many_stuff.custom_function()
        super(MyModel, self).save(*args, **kwargs)

When I create a new instance of MyModel through the Django Admin, I want to execute for each Stuff object, a function. It works on_save but I cannot make it work on __init__ for some reason. If I create the new instance, the functions don't get executed. If I save the newly created instance, the functions do get executed.

To be more specific, when I use this __init__ method, the model breaks on instance creation with the error:

MyModel needs to have a value for field "id" before this many-to-many relationship can be used.

I also attempted doing the same thing with the post_save signal, instead of using __init__ and on_save but I had the same problem. On object creation, the function custom_function did not get executed, but it did when I saved the object after it has been created by clicking on the save button.

Any ideas?

1 Answers

You simply cant create M2M join while creating object.

Because M2M model fields don't have real db fields, but have intermediary table that stores id from both model. When you create relation between two objects, both objects must have an id value ready to use.

First create MyModel instance without M2M fields, than set M2M fields. Something like that;

newModel = MyModel.objects.create(name=someting)
newStuff = Stuff.objects.create(..)

newModel.many_stuff.add(newStuff)
Related