在FK上使用limit_choices_to会导致ModelForm提交时出现MultipleObjectsReturned错误。

时间:2018-08-21 12:24:27

标签: django duplicates django-queryset

TL; DR:

根据相关对象的值过滤查询集可能会导致结果中出现重复的值。

以类似方式使用时,此行为会在模型字段的limit_choices_to FK属性上传播,在使用与此模型相关联的模型形式并选择重复值时,会导致MultipleObjectsReturned错误。 / p>

是否可以在模型的外键distinct()上应用limit_choices_to或等效名称,以避免在模型表单字段的选项中重复?


重现问题:

使用python manage.py shell(并解决):

让两个模型AB

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时,是否有更合适的方法来避免字段的查询集中重复出现?

0 个答案:

没有答案