【问题标题】:Django many-to-many field copyDjango 多对多字段复制
【发布时间】:2011-10-21 17:07:09
【问题描述】:

我有一个带有 2 个多对多字段的 Django 模型。从管理界面保存模型时,我需要检查第二个字段是否为空白,如果为空白,则我需要将第一个字段中的项目复制到第二个字段。我该怎么做?

更新

马修的回答似乎很好用,但我复制字段后无法保存实例。我试过 instance.save() 没有任何成功。

【问题讨论】:

  • 我一般用python的pdb调试看看是什么情况,试着放一个import pdb; pdb.set_trace() 就在 instance.save() 行之前,然后按“s”进入函数并查看发生了什么,接下来使用“n”。这是tutorial以防万一

标签: python django django-models many-to-many


【解决方案1】:

要使用的信号不是post_save,而是m2m_changed,它是在模型保存到数据库后发送的。

@models.signals.m2m_changed(sender=MyModel.second_m2m.through)
def duplicate_other_on_this_if_empty(sender, instance, action, reverse, model, pk_set, **kwargs):
    # just before adding a possibly empty set in "second_m2m", check and populate.
    if action == 'pre_add' and not pk_set:
        instance.__was_empty = True
        pk_set.update(instance.first_m2m.values_list('pk', flat=True))

@models.signals.m2m_changed(sender=MyModel.first_m2m.through)
def duplicate_this_on_other_if_empty(sender, instance, action, reverse, model, pk_set, **kwargs):
    # Just in case the "first_m2m" signals are sent after the other
    # so the actual "population" of the "second_m2m" is wrong:
    if action == 'post_add' and not pk_set and getattr(instance, '__was_empty'):
        instance.second_m2m = list(pk_set)
        delattr(instance, '__was_empty')

编辑:下一个代码更简单,并且基于模型定义的新知识

在您的代码中,“first_m2m”信号在“second_m2m”之前发送(这实际上取决于您的模型定义)。所以我们可以假设当接收到“second_m2m”信号时,“first_m2m”已经填充了当前数据。

这让我们更开心,因为现在您只需要检查 m2m-pre-add:

@models.signals.m2m_changed(sender=MyModel.second_m2m.through)
def duplicate_other_on_this_if_empty(sender, instance, action, reverse, model, pk_set, **kwargs):
    # just before adding a possibly empty set in "second_m2m", check and populate.
    if action == 'pre_add' and not pk_set:
        pk_set.update(instance.first_m2m.values_list('pk', flat=True))

【讨论】:

  • 根据docs.djangoproject.com/en/dev/ref/signals/…,没有“before_add”和“after_add”。你的意思是“pre_add”和“post_add”吗?
  • 是的,当然。对不起,我会纠正它。 (更复杂的版本)这是我目前使用的一种方法,以确保 m2m 的内容始终是另一个的子集(比如“启用”和“活动”事物,必须启用活动,否则它们会被忽略)
  • 这对我不起作用,我不知道为什么。我正在打印几个日志,我注意到missing_field_2 在missing_field_1 之前执行。然后它继续抛出一个错误,说 __was_empty 没有定义。有什么想法吗?
  • 我写的代码的逻辑有几个错误。我现在会更正它们。
【解决方案2】:

你可以从这里看到admin save override 的定义,直接覆盖它并检查相关字段是否为空,如果是,则保存对象,并从第二个M2M获取相关数据并将其设置为第一个M2M并再次保存...

【讨论】:

    【解决方案3】:

    您可以使用保存后信号。这看起来可能是在这里处理您的需求的最佳方式:好处是它也可以在管理员之外工作。

    @models.signals.post_save(sender=MyModel)
    def duplicate_missing_field(sender, instance, **kwargs):
        if not instance.my_second_m2m.count():
            instance.my_second_m2m.add(*instance.my_first_m2m.all())
            # or *instance.my_first_m2m.values_list('pk', flat=True), I think
    

    我的代码可能不太正确:您需要阅读 django 中的信号。

    【讨论】:

    • 似乎是一个很好的解决方案,但添加值后我无法保存实例。我试过 instance.save() 但那不起作用。有什么想法吗?
    • 保存后发送post_save信号,但是m2m插入队列已经完成,所以不能在那里修改。请改用 m2m_changed。
    【解决方案4】:

    你可以重写 clean 方法做额外的验证,所以如果第二个 many2many 为空你可以设置默认值,例如:

    def clean(self):
        super(MyClassModel, self).clean()
    
        if not self.MySecondMany2Many:
            # fill data
    

    您应该在类中的 models.py 中设置此代码。如果由于模型需要保存而导致 clean 不起作用,您也可以覆盖保存功能,这是相同的过程。

    我还没有测试过,我认为你不能这样检查 many2many 字段是否为空白,但你应该明白:)

    【讨论】:

    • 嗯。在保存之前使用 m2m 字段做一些事情可能会严重失败:它会尝试在没有 PK 之前使用它。
    • 那么您可以更好地覆盖保存。但是现在 Matthew 发布了关于 post_save 信号的帖子,我同意这是解决问题的更清洁的方法。
    猜你喜欢
    • 2013-04-27
    • 1970-01-01
    • 2011-02-05
    • 2021-11-20
    • 2016-02-23
    • 2012-05-15
    • 2011-03-08
    • 1970-01-01
    相关资源
    最近更新 更多