Django models 为什么Django QuerySet和Q()表达式返回重复的值?
我有以下两款Django车型: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
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()
对我来说似乎很直接。我也面临同样的问题,你找到了解决方案吗?我有一个多对多字段导致了这个问题,如果我删除了该字段,那么它就不会给出任何重复项