使用Django管理后台时ManyToMany字段未保存

14 投票
6 回答
8997 浏览
提问于 2025-04-16 18:45

我遇到了一个奇怪的问题,希望这里有人能帮我解答。

我在一个模型中重写了save()方法,目的是在调用super()之后给一个多对多字段添加一些值。我的问题是,当我在Django的管理后台保存时,这些值似乎被添加到了关系中,但又会变成空的。

不过,如果我在manage.py shell中操作,就没有问题,一切正常。

我在代码里放了两个打印语句,无论是在Django管理后台还是在shell中运行,它们的输出都是完全一样的。

class Store(models.Model):
    holidays = models.ManyToManyField(StoreHoliday, blank=True)
    copy_holidays_from = models.ForeignKey('Store', blank=True, null=True)

    def save(self):
        print '====  BEFORE SAVE:', self.holidays.all()
        super(Store, self).save()
        self.copy_holidays()
        print '====  AFTER SAVE:', self.holidays.all()

    def copy_holidays(self):
        if self.pk and self.copy_holidays_from:
            self.holidays.clear()
            for h in self.copy_holidays_from.holidays.all():
                self.holidays.add( h )

这是print语句的输出:

====  BEFORE SAVE: []
====  AFTER SAVE: [<StoreHoliday: 10 Mar 2010, Chuck Norris birthday (Closed)>]

有没有人能给我一些建议,可能是什么原因导致这个问题?

编辑:在save()方法中对多对多关系的所有手动更改似乎在通过管理界面保存时都被Django忽略了。这和它处理表单的方式有关吗?

6 个回答

1

在Django 2.1.4版本中,我的解决办法是使用save_related()这个方法。

def save_related(self, request, form, formsets, change):
    super().save_related(request, form, formsets, change)
    form.instance.permissions.add(request.user)
3

我今天碰到过类似的问题,你说得对,这跟Django处理数据的方式有关。

Django的管理后台在处理多对多字段(ManyToMany field)时,会单独进行操作,而不是直接修改对象本身。(记住,多对多关系的数据是保存在一个单独的数据库表里的)。

在我的情况下,如果我在管理后台的多对多字段里什么都不选,这就相当于对这个多对多关系进行了清空操作(clear())。你在保存(save())方法里做的任何操作都会被这个清空给覆盖掉。即使是在post_save信号处理器里做的操作也是一样。

对我来说,解决办法是把多对多字段分离到一个内嵌表单(inline)里,这样在修改对象时就不会自动保存为空了。

17

结果发现,上面的做法并不是正确的实现方式。代码应该放在StoreAdmin里,通过重写model_save()方法来实现。

这是我解决问题的方法:

class StoreAdmin(admin.ModelAdmin):
    def save_model(self, request, obj, form, change):
        if obj.copy_holidays_from:
            form.cleaned_data['holidays'] = obj.copy_holidays_from.holidays.all()

        super(StoreAdmin, self).save_model(request, obj, form, change)

撰写回答