Django多对多关系插入控件

时间:2022-10-04 13:13:37

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:

现在我想要两件事:

  1. I want to control if an Item can be added to a Collection.
  2. 我想控制是否可以将一个Item添加到Collection中。

  3. I want the Collection to update some of its fields if an Item was added or removed.
  4. 如果添加或删除了Item,我希望Collection更新其中的一些字段。

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?

对于第二个问题,我知道有django.db.models.signals.m2m_changed,我可以用来挂钩关系的变化。允许/确定在信号回调中更改Collection吗?我是否可以使用该信号“中止”问题1的插入?

2 个解决方案

#1


8  

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

我认为处理所需行为的最佳方法不是使用信号,而是使用重写的save()和delete()方法,您可以使用参数通过以下方式显式定义:https:// docs .djangoproject.com / EN /开发/ REF /模型/场/#django.db.models.ManyToManyField.through。这个: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.

而且,关于信号,一旦你有了直通表,你就可以监听pre_save和/或post_save信号,但是它们都不允许你直接否决关系的创建。

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.

在这种情况下,您可以侦听m2m_changed事件并触发对集合对象的更新(问题的第2部分),并追溯删除不恰当创建的关系(问题的第1部分)。然而,由于后一位是一个古怪的kludgy,如果可以,我会坚持使用显式通过表。

#2


3  

  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:

    在保存实例之前调用pre_save信号。但是你无法从那里中止保存操作。更好的解决方案是向Collection模型添加一个新方法,该方法负责检查是否可以添加Item:

    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.

    将实例添加到m2m字段时,不会调用save方法。你是对的,m2m_changed信号是要走的路。您可以安全地更新那里的集合实例。

#1


8  

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

我认为处理所需行为的最佳方法不是使用信号,而是使用重写的save()和delete()方法,您可以使用参数通过以下方式显式定义:https:// docs .djangoproject.com / EN /开发/ REF /模型/场/#django.db.models.ManyToManyField.through。这个: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.

而且,关于信号,一旦你有了直通表,你就可以监听pre_save和/或post_save信号,但是它们都不允许你直接否决关系的创建。

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.

在这种情况下,您可以侦听m2m_changed事件并触发对集合对象的更新(问题的第2部分),并追溯删除不恰当创建的关系(问题的第1部分)。然而,由于后一位是一个古怪的kludgy,如果可以,我会坚持使用显式通过表。

#2


3  

  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:

    在保存实例之前调用pre_save信号。但是你无法从那里中止保存操作。更好的解决方案是向Collection模型添加一个新方法,该方法负责检查是否可以添加Item:

    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.

    将实例添加到m2m字段时,不会调用save方法。你是对的,m2m_changed信号是要走的路。您可以安全地更新那里的集合实例。