0

我有一个相当神秘的 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 admin 编辑ModelChoiceField 查询集中 OrderLine多个的查询集时: Item在此处输入图像描述

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

仔细检查日志后,似乎ModelChoiceField通过了正确的Itemid/pk,但self.queryset.get(**{key:value})不知何故从单个 id/pk 返回了 2 个项目,即使这些项目具有不同的 id/pk(49 和 50): 在此处输入图像描述

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

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

PS 我从几年前发现这个https://code.djangoproject.com/ticket/23354似乎在这种情况下引用了错误,但票说它是固定的。

4

1 回答 1

0

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

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

文档union

此外,生成的 QuerySet 仅允许LIMIT OFFSET COUNT (*)ORDER BY和指定列(即切片、count()exists()order_by()values() / values_list() ) . 此外,数据库对组合查询中允许的操作设置了限制。例如,大多数数据库不允许在组合查询中使用LIMITOFFSET 。

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

  1. 使用|运算符

     available_items = current_item | get_available_items(...)
    

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

  2. 使用Q对象

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

     from django.db.models import Q
    
    
     available_items = Item.objects.filter(Q(pk=self.instance.item.pk) | Q(some_condition_for_available=True))
    
于 2021-04-15T03:51:28.620 回答