使用Django admin时,ManyToMany字段未保存

时间:2011-06-01 11:12:58

标签: python django m2m django-orm

我遇到了一个奇怪的问题,我希望这里的某些人能够对此有所了解。

我正在覆盖模型的save()方法,以便在运行super()之后向ManyToMany字段添加一些值。我的问题是,当我在Django admin中保存时,值似乎会被添加到关系中,但之后会再次为空。

但是,如果我从manage.py shell这样做,它可以毫无问题地工作。

我在那里放了两个打印语句,无论我是通过Django admin还是通过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)>]

有没有人对可能导致这种情况的原因有任何建议?

编辑:当通过管理界面保存时,Django似乎会丢弃save()中对m2m关系的所有手动更改。这与处理表单的方式有关吗?

5 个答案:

答案 0 :(得分:12)

事实证明,上述不是实现它的正确方法。该代码属于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)

答案 1 :(得分:3)

我今天可能遇到了同样的行为,是的,你认为它与django处理数据的方式有关是正确的。

django admin对ManyToMany字段的更改与更改实际对象分开。 (请记住,m2m保存在不同的数据库表中。)

在我的情况下,如果我没有在管理站点的ManyToMany字段中选择任何内容,这将转换为对ManyToMany关系的clear()操作。您在save()方法中执行的所有操作都会立即被删除。我在post_save信号处理程序中做的事情也一样。

解决方案(对我来说)是将ManyToMany字段分隔为内联,因此在修改对象时它不会自动保存为空。

答案 2 :(得分: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 :(得分:0)

对我来说,管理员只保存了许多字段的最后一个选定实例(选择了最后一个'假日')的问题。所以我必须覆盖save_model方法,例如:

@admin.register(Store)
class StoreAdmin(admin.ModelAdmin):

    def save_model(self, request, obj, form, change):
        form.cleaned_data['holidays'] = StoreHoliday.objects.filter(pk__in=dict(request.POST).get('holidays'))
        super(StoreAdmin, self).save_model(request, obj, form, change)

我花了很多时间在上面,其他解决方案都没有用,所以我希望它会有所帮助。

答案 4 :(得分:0)

一种更新m2m的解决方案,以及更新您的一个模型。

Django 1.11 and higher

在更新过程中可以观察到的行为,即使没有使用模型或信号之一的保存方法保存对m2m记录所做的更改,也仅由于m2m格式重写了所有记录,所以即使未保存这些更改在更新主要对象之后。

这就是为什么要逐步进行:

  1. 主要对象已更新。

  2. 您的代码(在保存方法或信号中)进行了更改(您可以 看看它们,只需在ModelAdmin中放置一个断点):

 def save_related(self, request, form, formsets, change):
     breakpoint()
     form.save_m2m()
     for formset in formsets:
         self.save_formset(request, form, formset, change=change)
  1. form.save_m2m()接受放置在页面上的所有m2m值(大致而言),并通过相关的管理器替换所有m2m记录。这就是为什么在交易结束时看不到更改的原因。
  

有一个解决方案:通过m2m进行更改   transaction.on_commit。 transaction.on_commit将进行您的更改   提交事务后,在form.save_m2m()之后。

不幸的是,此解决方案的缺点-您使用m2m进行的更改将在单独的事务中执行。