【问题标题】:Django Many-to-Many relation insertion controlDjango 多对多关系插入控件
【发布时间】:2013-05-19 19:29:03
【问题描述】:

我有以下型号:

class Item(models.Model):
    # fields
    # ...

class Collection(models.Model):
    items = models.ManyToManyField(Item, related_name="collections")
    # other fields
    # ...

现在我想要两件事:

  1. 我想控制是否可以将 Item 添加到 Collection
  2. 如果添加或删除了 Item,我希望 Collection 更新其部分字段。

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

【问题讨论】:

  • 对于问题 1,您可能应该使用表单的清理周期来验证数据(这简化了验证消息传递),然后将其发送到 save_m2m
  • @Hedde:我更喜欢接近模型的解决方案,因为我的数据很可能不会从表单中更改。 (更有可能通过 CLI 工具和公开的 API)。
  • 您可以覆盖模型保存方法,至少对于部分逻辑,但如果您正在实现 API,那么这种逻辑似乎属于 API 的授权层。 Tastypie 是一个非常丰富的 API,可以很好地与 Django 配合使用。
  • 嗯,我的问题与user授权无关。是否允许CollectionItem关系是应用程序逻辑。
  • 我不是在谈论身份验证,但是像tastepie 这样的框架可以让您检查是否在某些条件下允许发布或放置(您可以自己指定)。跨度>

标签: django django-signals django-models


【解决方案1】:

我认为实现这两种行为的最佳方法不是使用信号,而是使用 through 表上的重写 save() 和 delete() 方法,您可以使用参数 through 显式定义该方法见:https://docs.djangoproject.com/en/dev/ref/models/fields/#django.db.models.ManyToManyField.through。还有这个:https://docs.djangoproject.com/en/dev/topics/db/models/#overriding-predefined-model-methods

类似这样的:

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

顺便说一句,你会发现添加一个关系在这个直通模型上引起一个保存信号。

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

如果您的一个或两个模型是由第三方提供的,而您确实无法创建直通表,那么,是的,信号路由可能是唯一的方法。

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

在这种情况下,您可以侦听 m2m_changed 事件并触发对您的集合对象的更新(您的问题的第 2 部分)并追溯删除不当创建的关系(您的问题的第 1 部分)。但是,由于后一点很笨拙,如果可以的话,我会坚持使用显式直通表。

【讨论】:

  • 我非常喜欢这种方法。我从没想过使用trhough 模型来处理这个问题,但这是有道理的。我看到的唯一缺点是现在我不能使用ManyToManyFieldadd 方法,而是需要自己创建CollectionItem。这不是问题,只是习惯了。感谢您的回答。
  • 这也是一个不错的选择,提供了更大的灵活性
  • 这特别有用,因为 m2m_changed 信号不会因管理员中的更改而触发 [code.djangoproject.com/ticket/16073]
【解决方案2】:
  1. 在保存实例之前调用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. 将实例添加到 m2m 字段时,不会调用 save 方法。你是对的,m2m_changed 信号是要走的路。您可以在那里安全地更新集合实例。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2012-11-23
    • 2011-12-23
    • 1970-01-01
    • 1970-01-01
    • 2020-09-01
    • 1970-01-01
    • 2023-03-20
    • 2017-02-12
    相关资源
    最近更新 更多