Django 多对多字段复制

5 投票
4 回答
2669 浏览
提问于 2025-04-16 23:03

我有一个Django模型,这个模型里有两个多对多的字段。当我从管理界面保存这个模型的时候,我需要检查第二个字段是否为空。如果第二个字段是空的,我就需要把第一个字段里的内容复制到第二个字段里。我该怎么做呢?

更新

Matthew的回答看起来很不错,但我在复制字段后无法保存这个实例。我尝试过使用instance.save(),但没有成功。

4 个回答

0

你可以重写clean方法来进行额外的验证,这样如果第二个多对多字段是空的,你可以设置默认值,比如:

def clean(self):
    super(MyClassModel, self).clean()

    if not self.MySecondMany2Many:
        # fill data

你应该把这段代码放在你的models.py文件里,放在你的类里面。如果clean方法不起作用,因为模型需要被保存,你也可以重写save函数,步骤是一样的。

我没有测试过,我觉得你可能不能通过那种方式检查多对多字段是否为空,但你应该能明白这个意思 :)

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中的信号。

4

这里要用的信号不是 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))

撰写回答