TL; DR:
根据相关对象的值过滤查询集可能会导致结果中出现重复的值。
以类似方式使用时,此行为会在模型字段的limit_choices_to
FK属性上传播,在使用与此模型相关联的模型形式并选择重复值时,会导致MultipleObjectsReturned
错误。 / p>
是否可以在模型的外键distinct()
上应用limit_choices_to
或等效名称,以避免在模型表单字段的选项中重复?
python manage.py shell
(并解决):让两个模型A
和B
:
class A(models.Model):
pass
class B(models.Model):
a = models.ForeignKey(A)
d = models.BooleanField(default=False)
以及以下条目:
>>> a = A.objects.create()
>>> b1 = B.objects.create(a=a, d=True)
>>> b2 = B.objects.create(a=a, d=True)
以下查询集使用get()
会导致错误:
>>> A.objects.filter(b__d=True).get(id=1)
Traceback (most recent call last):
File "<console>", line 1, in <module>
File "/home/vmonteco/.venvs/django/lib/python3.6/site-packages/django/db/models/query.py", line 384, in get
(self.model._meta.object_name, num)
app.models.MultipleObjectsReturned: get() returned more than one A -- it returned 2!
由于a
的结果中两次出现filter()
,因此听起来很正常:
>>> A.objects.filter(b__d=True)
<QuerySet [<A: A object>, <A: A object>]>
使用简单的distinct()
可以轻松解决此错误:
>>> A.objects.filter(b__d=True).distinct().get(id=1)
<A: A object>
让我们添加第三个模型:
class C(models.Model):
a = models.ForeignKey(A, limit_choices_to={'b__d': True})
我可以使用modelform创建/编辑实例:
class CForm(forms.ModelForm):
class Meta:
model = C
fields = ['a',]
填充a
字段选择的查询集应如下所示:
>>> A.objects.filter(b__d=True)
<QuerySet [<A: A object>, <A: A object>]>
其中只包含两次相同的对象:
>>> A.objects.filter(b__d=True).values('id')
<QuerySet [{'id': 1}, {'id': 1}]>
然后,在表单提交时,django在字段的查询集上应用get(id=selected_value)
。如果所选值是重复值,则会发生我在上一部分中暴露的问题。
到目前为止,我发现的唯一解决方案是覆盖模型窗体中的字段的查询集,以确保没有重复项:
class CForm(forms.ModelForm):
class Meta:
model = C
fields = ['a',]
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.fields['a'].queryset = self.fields['a'].queryset.distinct()
但是由于此查询集是在模型字段的定义之后直接定义的,因此该解决方案让人感到不满意,并且看起来更像是一种解决方法。 limit_choices_to
似乎没有记录这种情况。
使用limit_choices_to
时,是否有更合适的方法来避免字段的查询集中重复出现?