Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/cmake/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Django models 为什么Django QuerySet和Q()表达式返回重复的值?_Django Models_Django Queryset_Django Q - Fatal编程技术网

Django models 为什么Django QuerySet和Q()表达式返回重复的值?

Django models 为什么Django QuerySet和Q()表达式返回重复的值?,django-models,django-queryset,django-q,Django Models,Django Queryset,Django Q,我有以下两款Django车型: from mptt.models import MPTTModel, TreeForeignKey from django.db import models from django.db.models import Q class Model1(MPTTModel): random_field = models.IntegerField() parent = TreeForeignKey('self', null=True, blank=True

我有以下两款Django车型:

from mptt.models import MPTTModel, TreeForeignKey
from django.db import models
from django.db.models import Q

class Model1(MPTTModel):
    random_field = models.IntegerField()
    parent = TreeForeignKey('self', null=True, blank=True)


class Model2(models.Model):
    model_1 = models.ManyToManyField(Model1)

    @staticmethod
    def descendants_queryset(model1):
        q = Q()
        for curr_descendant in model1.get_descendants:
            q |= Q(model_1=curr_descendant)
        return q
我创建了如下实例:

>>> a = Model2.objects.create()
>>> b = Model1.objects.create(random_field=1, parent=None)
>>> c = Model1.objects.create(random_field=2, parent=b)
>>> d = Model1.objects.create(random_field=3, parent=b)
>>> a.model_1.add(c)
>>> a.pk
3
当我使用普通的queryset过滤器和Q()表达式时,它会产生相同的结果(如预期的那样):

但是,当我将Model1的另一个实例添加到ManyToMany关系中时,我只在使用Q()表达式进行过滤时才看到奇怪的重复:

>>> a.model_1.add(d)
>>> [i.pk for i in Model2.objects.filter(pk=3)]
[3]
>>> [i.pk for i in Model2.objects.filter(Model2.descendants_queryset(b), pk=3)]
[3, 3]

我不明白为什么会发生这种重复。对我来说,这好像是一只虫子。显然,我可以通过向queryset添加
.distinct()
来解决这个问题。但这似乎没有必要。为什么会发生这种情况?正确的解决方案是什么?

我注意到,当您向添加第三个元素时,您的输出不仅是重复的,而且是三倍的:

>>> 4 = Model1.objects.create(random_field=3, parent=b)
>>> a.model_1.add(e)
>>> [i.pk for i in Model2.objects.filter(Model2.descendants_queryset(b), pk=3)]
[3, 3, 3]
如果你再加上一个,就会翻两番,以此类推

因此,我猜测的是,由于子体_queryset()中的Q()查询是OR查询,因此它会返回每个对象,其中b对象为父对象,并且过滤器会多次匹配a(它对Model1对象有多个引用)

如果我们查看
Model2.objects.filter(Model2.subjects\u queryset(b))
的原始SQL,我们会看到以下内容:

>>> Model2.objects.filter(Model2.descendants_queryset(b)).query.sql_with_params()
(u'SELECT "Foo_model2"."id" FROM "Foo_model2" LEFT OUTER JOIN "Foo_model2_model_1" ON ("Foo_model2"."id" = "Foo_model2_model_1"."model2_id") WHERE ("Foo_model2_model_1"."model1_id" = %s OR "Foo_model2_model_1"."model1_id" = %s OR "Foo_model2_model_1"."model1_id" = %s)', (17, 18, 19))
>>> a2 = Model2.objects.create()
>>> a2.model_1.add(c)
>>> [i.pk for i in Model2.objects.filter(Model2.descendants_queryset(b))]
[3, 3, 3, 4]
或更具可读性:

SELECT "Foo_model2"."id"
FROM "Foo_model2"
  LEFT OUTER JOIN "Foo_model2_model_1"
  ON ("Foo_model2"."id" = "Foo_model2_model_1"."model2_id")
WHERE ("Foo_model2_model_1"."model1_id" = 17
  OR "Foo_model2_model_1"."model1_id" = 18
  OR "Foo_model2_model_1"."model1_id" = 19)
因此,它实际上将由
q |=q(model_1=curr_substant)
生成的查询与OR语句连接起来,OR语句返回的不是一个,而是三个引用(都指向同一个Model2对象,其中包含对三个Model1对象的多个引用)。 这是由于join语句造成的-有关一些示例,请参阅

如果我们为
pk=3
添加额外的过滤器,它不会进一步限制输出,因为所有返回对象的pk都是相同的(3)

如果添加另一个Model2对象,并添加c作为新元素model1 ManyToMany引用的引用,则会得到以下结果:

>>> Model2.objects.filter(Model2.descendants_queryset(b)).query.sql_with_params()
(u'SELECT "Foo_model2"."id" FROM "Foo_model2" LEFT OUTER JOIN "Foo_model2_model_1" ON ("Foo_model2"."id" = "Foo_model2_model_1"."model2_id") WHERE ("Foo_model2_model_1"."model1_id" = %s OR "Foo_model2_model_1"."model1_id" = %s OR "Foo_model2_model_1"."model1_id" = %s)', (17, 18, 19))
>>> a2 = Model2.objects.create()
>>> a2.model_1.add(c)
>>> [i.pk for i in Model2.objects.filter(Model2.descendants_queryset(b))]
[3, 3, 3, 4]
新Model2对象的id也显示在queryset中,因为它还有一个对model1对象的引用


我现在没有任何关于最佳解决方案的好主意,但是在查询集上调用
.distinct()
对我来说似乎很直接。

我也面临同样的问题,你找到了解决方案吗?我有一个多对多字段导致了这个问题,如果我删除了该字段,那么它就不会给出任何重复项