Django Many-to-Many relation insertion control

£可爱£侵袭症+ 提交于 2019-12-01 05:18:45

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 argument through 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 this:

# -*- 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()

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.

  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 your Collection model, which is responsible for checking if an 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
    
  2. 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.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!