【问题标题】:ManyToMany field not saved when using Django admin使用 Django admin 时未保存 ManyToMany 字段
【发布时间】:2011-09-06 05:06:36
【问题描述】:

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

我正在重写模型的 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 关系的所有手动更改。这与它处理表单的方式有关吗?

【问题讨论】:

  • 我不知道你的问题出在哪里,但是self.holidays = self.copy_holidays_from.holidays.all() 看起来比清除和迭代要好得多。
  • 感谢您的提示,我不知道这是可能的。你可以在下面看到我做错了什么。

标签: python django m2m django-orm


【解决方案1】:

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

【讨论】:

    【解决方案2】:

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

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

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

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

    【讨论】:

    • 您好,Jyrsa,我遇到了与 Django Admin 类似的问题。我们有一个 m2m 字段,有时在保存页面的其余部分时会清除该字段。我不知道 Django 单独保存它。您能否将我链接到有关如何处理 m2m 的更多信息?您能否更具体地说明在什么情况下清除保存方法的操作?谢谢!
    【解决方案3】:

    在 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)
    

    【讨论】:

      【解决方案4】:

      对我来说,管理员只保存了许多字段的最后一个选择实例(最后一个“假期”选择)。所以我不得不重写 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)
      

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

      【讨论】:

        【解决方案5】:

        更新 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 的更改将在单独的事务中执行。

        【讨论】:

          猜你喜欢
          • 2021-10-18
          • 2018-12-07
          • 2012-03-08
          • 1970-01-01
          • 2017-10-18
          • 2013-05-23
          • 1970-01-01
          • 2019-02-15
          • 2020-09-29
          相关资源
          最近更新 更多