13

我遇到了一个奇怪的问题,我希望这里的某个人能够解释一下。

我正在覆盖模型的 save() 方法,以便在运行 super() 后向 ManyToMany-field 添加一些值。我的问题是,当我在 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 关系的所有手动更改。这与它如何处理表单有关吗?

4

5 回答 5

15

所以事实证明,上述方法不是实现它的正确方法。代码属于 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)
于 2011-06-01T11:33:14.583 回答
3

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

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

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

解决方案(对我来说)是将 ManyToMany-field 分隔成一个内联字段,这样在修改对象时它就不会自动保存为空。

于 2011-09-16T13:46:54.690 回答
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)
于 2019-01-24T10:57:46.343 回答
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)

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

于 2018-05-28T16:55:38.357 回答
0

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

Django 1.11 and higher

您在更新过程中可以观察到的行为,当您对 m2m 记录所做的更改未保存时,即使您在模型之一或信号中的保存方法中进行了更改,这仅是因为 m2m 表单在 main 之后重写了所有记录对象已更新。

这就是为什么,一步一步:

  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 记录。这就是为什么您在事务结束时看不到您的更改。

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

不幸的是,这个解决方案的缺点 - 您对 m2m 的更改将在单独的事务中执行。

于 2019-02-14T22:58:39.960 回答