【问题标题】:Django MultipleObjectsReturned error on form submit表单提交时出现 Django MultipleObjectsReturned 错误
【发布时间】:2021-07-09 18:29:45
【问题描述】:

我遇到了一个相当神秘的 MultipleObjectsReturned 错误,该错误是在数周没有问题后突然出现的。我希望只是我缺少的一些简单的东西。

我有一个Order 模型,一个OrderLine 模型,它有一个Item 外键。每个Item 都有一个指向Product 的外键。以下是简化模型:

class OrderLine(models.Model):
    order = models.ForeignKey(Order, related_name="lines", on_delete=models.CASCADE)
    item = models.ForeignKey(Item, on_delete=models.SET_NULL, blank=True, null=True)


class Product(TimeStampedModel):
   ...


class Item(TimeStampedModel):
   product = models.ForeignKey(Product, related_name='items', on_delete=models.CASCADE)

OrderLineForm 和 OrderLineAdmin 供参考:

class OrderLineForm(forms.ModelForm):
    class Meta:
        model = OrderLine
        ...

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.fields['category'].queryset = ItemType.objects.all()
        self.fields['product'].queryset = Product.objects.none()
        self.fields['item'].queryset = Item.objects.none()

        if self.instance.pk:
            try: # When changing an existing OrderLine
                ...
                current_item = Item.objects.filter(pk=self.instance.item.pk)
                available_items = current_item.union(
                    get_available_items(...)
                )
                self.fields['item'].queryset = available_items
                self.fields['category'].initial = item_type_id
            except:
                self.fields['item'].queryset = Item.objects.all()
        ...


@admin.register(OrderLine)
class OrderLineAdmin(admin.ModelAdmin):
    form = OrderLineForm

现在,当我使用 Django 管理员编辑 OrderLine 时,它在 ModelChoiceField 查询集中具有多个 Item

我在清理表单时收到以下错误:get() returned more than one Item -- it returned 2!

在仔细检查日志后,ModelChoiceField 似乎通过了正确的 Item id/pk,但 self.queryset.get(**{key:value}) 以某种方式从单个 id/pk 返回 2 个项目,即使项目有不同的 id/pks(49 和 50):

同样,只有当OrderLine 表单的Item 字段在查询集中有多个对象时才会发生这种情况。如果它只是一个Item,它保存得很好。任何想法为什么我现在收到此错误?谢谢!

我认为在数据库关系方面唯一发生变化的是我将formset.save_m2m() 添加到 Item 模型管理员,但是 Item 不是 m2m 关系,所以这可能导致一些数据库索引错误?

附:我从几年前发现了这个https://code.djangoproject.com/ticket/23354,它似乎引用了这个上下文中的错误,但票上说它已修复。

【问题讨论】:

  • 我不太明白“OrderLine 在 ModelChoiceField 查询集中有多个项目”是什么意思?你能展示你的 ModelAdmin 吗?
  • @AbdulAzizBarkat 抱歉,我不知道如何简洁地说出这个词。我已将OrderLineForm 添加到OrderLineAdmin 使用的问题中。在 OrderLineForm 初始化时,当前选择的 Item 和任何其他可用项目将作为 self.fields['item'].queryset 提供。因此,表单查询集可以包含 1 个或多个 Items,并且当此查询集中有多个 Item 时,它当前会给出错误
  • 我看到你在查询集上调用union。请注意,根据文档,只能在结果查询集上调用特定方法(get 不是其中之一)。如果没有union,就没有办法做到这一点吗?你能展示一下get_available_items 到底做了什么吗?
  • @AbdulAzizBarkat 就是这样!!!太感谢了。 get_available_items 返回一个 Items 的查询集。由于当前正在编辑的OrderLineItem 在此查询集中不存在,因此我决定使用unioncurrent_itemavailable items 结合起来。当我移除工会时,它完美无缺。那么有没有不使用union的标准方法来组合QuerySets?

标签: django django-admin django-queryset modelform modelchoicefield


【解决方案1】:

从这两行我们可以看出您执行了一个联合并将其设置为字段查询集:

current_item = Item.objects.filter(pk=self.instance.item.pk)
available_items = current_item.union(
    get_available_items(...)
)

来自documentation 上的union

另外,只有LIMITOFFSETCOUNT(*)ORDER BY、 并指定列(即切片、count()exists()order_by()values()/values_list()) 可用于生成的 QuerySet。此外,数据库对什么设置了限制 在组合查询中允许操作。例如,大多数 数据库不允许在组合查询中使用 LIMITOFFSET

考虑到该字段将在此查询集上调用 get 以验证所选的选择,联合对其不可行。考虑到您的用例,实际上我们有更好的选择,只使用 SQL OR 运算符。主要有两种方法:

  1. 使用| operator

     available_items = current_item | get_available_items(...)
    

    这相当于说SELECT ... WHERE (condition for current item) OR (conditions for available items)

  2. 使用Q objects:

    考虑到我们可能想要进行具有相当复杂条件的查询,以前的方法不是很好。这将导致我们编写一堆查询集,然后在它们上使用|&。除了这样做,我们还有一个很好的选择,即使用 Q 对象,它可以将您传递给过滤器的相同参数作为关键字参数:

     from django.db.models import Q
    
    
     available_items = Item.objects.filter(Q(pk=self.instance.item.pk) | Q(some_condition_for_available=True))
    

【讨论】:

    猜你喜欢
    • 2013-07-13
    • 2020-05-30
    • 2014-11-11
    • 2011-07-28
    • 2014-02-25
    • 2018-11-24
    • 2017-08-24
    • 2011-01-06
    相关资源
    最近更新 更多