Django MultipleObjectsReturned 表单提交错误
Django MultipleObjectsReturned error on form submit
我遇到了相当神秘的 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 查询集中有 多个 Item
的 OrderLine
时:
我在清理表单时遇到以下错误:get() returned more than one Item -- it returned 2!
仔细检查日志后,ModelChoiceField
似乎通过了正确的 Item
id/pk,但 self.queryset.get(**{key:value})
以某种方式返回了 2 个项目单个 id/pk,即使项目具有不同的 id/pk(49 和 50):
同样,只有当 OrderLine
表单的 Item
字段在查询集中有多个对象时才会发生这种情况。如果只有一个Item
,保存起来就好了。任何想法为什么我现在收到此错误?谢谢!
我认为在数据库关系方面唯一发生变化的是我将 formset.save_m2m()
添加到 Item 模型管理中,但是 Item 不是 m2m 关系,所以这可能会导致一些数据库索引错误?
P.S。我发现这个 https://code.djangoproject.com/ticket/23354 多年前似乎引用了此上下文中的错误,但票证说它已修复。
从这两行中我们可以看到您执行了联合并将其设置为查询集字段:
current_item = Item.objects.filter(pk=self.instance.item.pk)
available_items = current_item.union(
get_available_items(...)
)
来自 union
上的 documentation:
In addition, only LIMIT, OFFSET, COUNT(*), ORDER BY,
and specifying columns (i.e. slicing, count(), exists(),
order_by(), and values()/values_list()) are allowed on the resulting QuerySet. Further, databases place restrictions on what
operations are allowed in the combined queries. For example, most
databases don’t allow LIMIT or OFFSET in the combined queries.
考虑到该字段将在此查询集上调用 get
来验证所选的选择,联合对其不可行。考虑到您的用例,实际上我们有一个更好的选择,就是使用 SQL OR 运算符。主要有两种方法:
使用|
operator:
available_items = current_item | get_available_items(...)
这相当于说SELECT ... WHERE (condition for current item) OR (conditions for available items)
。
使用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))
我遇到了相当神秘的 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 查询集中有 多个 Item
的 OrderLine
时:
我在清理表单时遇到以下错误:get() returned more than one Item -- it returned 2!
仔细检查日志后,ModelChoiceField
似乎通过了正确的 Item
id/pk,但 self.queryset.get(**{key:value})
以某种方式返回了 2 个项目单个 id/pk,即使项目具有不同的 id/pk(49 和 50):
同样,只有当 OrderLine
表单的 Item
字段在查询集中有多个对象时才会发生这种情况。如果只有一个Item
,保存起来就好了。任何想法为什么我现在收到此错误?谢谢!
我认为在数据库关系方面唯一发生变化的是我将 formset.save_m2m()
添加到 Item 模型管理中,但是 Item 不是 m2m 关系,所以这可能会导致一些数据库索引错误?
P.S。我发现这个 https://code.djangoproject.com/ticket/23354 多年前似乎引用了此上下文中的错误,但票证说它已修复。
从这两行中我们可以看到您执行了联合并将其设置为查询集字段:
current_item = Item.objects.filter(pk=self.instance.item.pk)
available_items = current_item.union(
get_available_items(...)
)
来自 union
上的 documentation:
In addition, only LIMIT, OFFSET, COUNT(*), ORDER BY, and specifying columns (i.e. slicing, count(), exists(), order_by(), and values()/values_list()) are allowed on the resulting QuerySet. Further, databases place restrictions on what operations are allowed in the combined queries. For example, most databases don’t allow LIMIT or OFFSET in the combined queries.
考虑到该字段将在此查询集上调用 get
来验证所选的选择,联合对其不可行。考虑到您的用例,实际上我们有一个更好的选择,就是使用 SQL OR 运算符。主要有两种方法:
使用
|
operator:available_items = current_item | get_available_items(...)
这相当于说
SELECT ... WHERE (condition for current item) OR (conditions for available items)
。使用
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))