I have the following models:
class Item(models.Model):
# fields
# ...
class Collection(models.Model):
items = models.ManyToManyField(Item, related_name="collections")
# other fields
# ...
Now I want two things:
- I want to control if an
Item
can be added to aCollection
. - I want the
Collection
to update some of its fields if anItem
was added or removed.
For the second issue I know that there is the django.db.models.signals.m2m_changed
which I can use to hook into changes of the relation. Is it allowed/ok to change the Collection
within the signal callback? Can I use the signal also for "aborting" the insertion for issue 1?
The
pre_save
signal is called before saving an instance. But you are not able to abort the save operation from there. A better solution would be to add a new method to yourCollection
model, which is responsible for checking if anItem
can be added:When adding an instance to a m2m field, the save method does not get called. You are right, the
m2m_changed
signal is the way to go. You can safely update the collection instance in there.I think the best way to approach both of your desired behaviors is not with signals, but rather with an overridden save() and delete() method on the
through
table which you would define explicitly using the argumentthrough
see: https://docs.djangoproject.com/en/dev/ref/models/fields/#django.db.models.ManyToManyField.through. and this: https://docs.djangoproject.com/en/dev/topics/db/models/#overriding-predefined-model-methodsSomething like this:
Incidentally, you'll find that adding a relationship will cause a save-signal on this through model.
And, regarding signals, once you have the through table in place, you'd be able to listen for pre_save and/or post_save signals, but neither of them will allow you to directly veto the creation of the relationship.
If one or both of your models are supplied by a 3rd party and you really cannot create the through table, then, yes, the signal route may be the only way to go.
https://docs.djangoproject.com/en/dev/ref/signals/#m2m-changed
In which case, you could listen for the m2m_changed event and trigger updates to your collection objects (part 2 of your question) and retroactively delete inappropriately created relationships (part 1 of your question). However, as this latter bit is a fugly kludgy, I'd stick with the explicit through table if you can.