Django Plenty to Many Paste Management

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 Item can be added to Collection .
  • I want Collection update some of its fields if Item been added or removed.

In the second problem, I know that there is django.db.models.signals.m2m_changed , which I can use to bind to changes in attitude. Is / ok allowed to modify Collection in a signal callback? Can I also use the signal to β€œcancel” the insert for problem 1?

+6
source share
2 answers

I think the best way to get closer to your desired behavior is not signals, but rather an overridden save () and delete () method in the through table, which you would explicitly define using the through argument, 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-methods

Something like that:

 # -*- coding: utf-8 -*- from django.db import models class Item(models.Model): # fields # ... class Collection(models.Model): items = models.ManyToManyField(Item, related_name="collections", through="CollectionItem") # other fields # ... class CollectionItem(models.Model): collection = models.ForeignKey(Collection) item = models.ForeignKey(Item) def save(self, *args, **kwargs): # Only allow this relationship to be created on some_condition # Part 1 of your question. if some_condition: super(CollectionItem, self).save(*args, **kwargs) # Update some fields on Collection when this # relationship is created # Part 2 of your question (1/2) self.Collection.updateSomeFields() def delete(self, *args, **kwargs): collection = self.collection super(CollectionItem, self).delete(*args, **kwargs) # Update some fields on Collection when this relationship # is destroyed. # Part 2 of your question (2/2) collection.updateSomeFields() 

By the way, you will find that adding a relation will cause a save signal on this model.

And, with regard to signals, as soon as you have an end-to-end table, you can listen to the signals pre_save and / or post_save, but none of them will allow you to directly veto the creation of the relationship.

If one or both models are supplied by a third party, and you really cannot create an end-to-end table, then yes, the signal path may be the only way.

https://docs.djangoproject.com/en/dev/ref/signals/#m2m-changed

In this case, you can listen to the m2m_changed event and trigger updates for your collection objects (part 2 of your question) and retroactively delete improper relationships created (part 1 of your question). However, since this last bit is ugly kludgy, I would stick to an explicit table if you can.

+8
source
  • Before saving the instance, the pre_save signal is pre_save . But you cannot cancel the save operation from there. The best solution would be to add a new method to your Collection model, which is responsible for checking if Item can be added:

     class Collection(models.Model): items = models.ManyToManyField(Item, related_name="collections") ... def add_item(self, item): if check_if_item_can_be_added(item): items.add(item) self.save() def check_if_item_can_be_added(self, item): # do your checks here 
  • When an instance is added to the m2m field, the save method is not called. You are right, the m2m_changed signal is the way to go. You can safely update the collection instance there.

+3
source

Source: https://habr.com/ru/post/945754/


All Articles