查询更改 Q 对象顺序的不同结果
Posted
技术标签:
【中文标题】查询更改 Q 对象顺序的不同结果【英文标题】:Different results on query changing Q object order 【发布时间】:2013-11-19 08:54:29 【问题描述】:使用Q
对象创建查询集时遇到问题。我得到不同的结果取决于我如何订购一些Q
条件。我将稍微简化我的模型,以便以简洁的方式描述我的问题。
class D(models.Model):
one_attr = models.BooleanField()
s_attr = models.ManyToManyField(S, through='DRelatedToS')
d_to_p = models.ForeignKey(P)
class S(models.Model):
other_attr = models.BooleanField()
s_to_p = models.ForeignKey(P)
class DRelatedToS(models.Model):
to_s = models.ForeignKey(S)
to_d = models.ForeignKey(D)
date = models.DateField()
class P(models.Model):
the_last_attr = models.PositiveIntegerField()
关系总结:
D <-- DRelatedToS --> S --> P | ^ | | -------->------->------>----^
使用这些模型和关系,我得到两个不同的结果,具体取决于我如何安排 Q 条件: 第一个查询,给出一个结果
D.objects.filter(
Q(one_attr=True, s_attr__s_to_p__the_last_attr=5)
|
Q(one_attr=False, d_to_p__the_last_attr=10)
)
第二次查询,给出另一个结果,不同于第一次查询
D.objects.filter(
Q(one_attr=False, d_to_p__the_last_attr=10)
|
Q(one_attr=True, s_attr__s_to_p__the_last_attr=5)
)
我的问题是:为什么会这样?我的查询方式有什么问题吗?
当我查看从这些查询派生的 SQL 语句时,我得到两个不同的语句:一个生成 LEFT OUTER JOIN
和很多 INNER JOIN
s,第二个生成所有 INNER JOIN
s。真正返回我想要的东西是制作LEFT OUTER JOIN
的那个。这让我觉得我的所有查询都可能返回糟糕的结果,具体取决于我如何安排它的条件。这是一个错误还是我做错了什么(或一切)?
【问题讨论】:
简化模型是Good Thing™ 【参考方案1】:在您的示例中,不同的顺序应该返回相同的结果。
尽管如此,我测试了您的代码(使用我在问题代码中所做的更正),但无法生成您描述的错误。也许您在简化代码时引入了其他错误并错过了,您可以发布您使用的示例数据吗? (见下面我的数据)。
首先,您的示例代码有问题,我已编辑您的问题,建议进行以下更正以解决问题、简化和改进测试,但我没有看到更改已更新,因此我在此重复:
更正 1: diff 格式的模型更改:
3,4c6,10
< s_attr = models.ManyToMany(S, through='DRelatedToS')
< d_to_p = models.ForeignKey(P)
---
> s_attr = models.ManyToManyField('S', through='DRelatedToS')
> d_to_p = models.ForeignKey('P')
>
> def __unicode__(self):
> return 'D:(%s,%s,%s)' % (self.id, self.one_attr, self.d_to_p.the_last_attr)
8,9c14
< other_attr = models.BooleanField()
< s_to_p = models.ForeignKey(P)
---
> s_to_p = models.ForeignKey('P')
13d17
< to_p = models.ForeignKey(P)
15c19
< date = models.DateField()
---
> to_s = models.ForeignKey(S)
19a24
>
所以在应用校正后模型如下所示:
class D(models.Model):
one_attr = models.BooleanField()
s_attr = models.ManyToManyField('S', through='DRelatedToS')
d_to_p = models.ForeignKey('P')
def __unicode__(self):
return 'D:(%s,%s,%s)' % (self.id, self.one_attr, self.d_to_p.the_last_attr)
class S(models.Model):
s_to_p = models.ForeignKey('P')
class DRelatedToS(models.Model):
to_d = models.ForeignKey(D)
to_s = models.ForeignKey(S)
class P(models.Model):
the_last_attr = models.PositiveIntegerField()
更正 2:您在查询中的查找字段错误(已修复)。
以下是我所做的:
创建名为testso
的项目和应用程序:
django-admin.py startproject marianobianchi
cd marianobianchi
python manage.py startapp testso
添加您的模型并调整项目设置(数据库设置,将testso
添加到INSTALLED_APPS
)
添加示例数据:
mkdir testso/fixtures
cat > testso/fixtures/initial_data.json
[
"pk": 1, "model": "testso.d", "fields": "one_attr": true, "d_to_p": 3,
"pk": 2, "model": "testso.d", "fields": "one_attr": true, "d_to_p": 4,
"pk": 3, "model": "testso.d", "fields": "one_attr": false, "d_to_p": 5,
"pk": 4, "model": "testso.d", "fields": "one_attr": false, "d_to_p": 5,
"pk": 1, "model": "testso.s", "fields": "s_to_p": 1,
"pk": 2, "model": "testso.s", "fields": "s_to_p": 2,
"pk": 3, "model": "testso.s", "fields": "s_to_p": 3,
"pk": 1, "model": "testso.drelatedtos", "fields": "to_d": 2, "to_s": 1,
"pk": 2, "model": "testso.drelatedtos", "fields": "to_d": 1, "to_s": 2,
"pk": 3, "model": "testso.drelatedtos", "fields": "to_d": 1, "to_s": 3,
"pk": 1, "model": "testso.p", "fields": "the_last_attr": 5,
"pk": 2, "model": "testso.p", "fields": "the_last_attr": 5,
"pk": 3, "model": "testso.p", "fields": "the_last_attr": 3,
"pk": 4, "model": "testso.p", "fields": "the_last_attr": 4,
"pk": 5, "model": "testso.p", "fields": "the_last_attr": 10
]
python manage.py syncdb
python manage.py shell
在外壳中:
>>> from testso.models import *
>>> from django.db.models import Q
>>> D.objects.filter(Q(one_attr=True, s_attr__s_to_p__the_last_attr=5) | Q(one_attr=False, d_to_p__the_last_attr=10))
[<D: D:(1,True,3)>, <D: D:(2,True,4)>, <D: D:(3,False,10)>, <D: D:(4,False,10)>]
>>> D.objects.filter(Q(one_attr=False, d_to_p__the_last_attr=10) | Q(one_attr=True, s_attr__s_to_p__the_last_attr=5))
[<D: D:(1,True,3)>, <D: D:(2,True,4)>, <D: D:(3,False,10)>, <D: D:(4,False,10)>]
同样的结果! ...正如预期的那样。
【讨论】:
感谢您的回答!我稍后会尝试这个,看看它是否有效。关于您的更正,我编辑了我的问题以修复我的错误,但其中一些不是错误(例如“P”而不是 P)......如果您想编辑您的答案以使其更短。我想接受您的编辑,但我认为我没有所需的声誉...... 引用一个你之前没有定义过的模型会引发一个异常,所以这就是为什么你需要'P' insted of P ...我会再次提出更正,我认为你不需要接受此提案的声誉;) 提案更改完成,在我看到您的问题更新后,我将从我的答案中删除 好的...我明白您的意思,但您知道这不是问题...关于您的提议,我看到了有关提议的版本的通知,但是当我点击那里时,我可以看到您的更改没有任何按钮可以接受它们.... 您应该会看到批准/拒绝编辑建议的按钮,也许this 和this 可以帮助您。您应该认为它不能解决您的问题,但可以帮助人们运行您的测试示例,如果它没有运行,如何尝试解决它?,我修复了它并建议进行更改以使不仅由我更容易找到解决方案。 【参考方案2】:我无法直接回答您的问题,但可能有另一种方法可以做您想做的事情,这可能会产生更一致的结果:
subset_a = D.objects.filter(one_attr=False, d_to_p__the_last_attr=10)
subset_b = D.objects.filter(one_attr=True, s_attr__p__the_last_attr=5)
union_set = subset_a | subset_b
union_set = union_set.distinct()
该 |两个查询集上的运算符执行联合,并且 distinct 将确保您不会得到重复行。我很想知道这里的安排是否也很重要。
【讨论】:
感谢您的回答!但我认为这行不通。虽然子集_a 和子集_b 给了我 25 和 0 个结果,但当我执行subset_a | subset_b
或 subset_b | subset_a
时,我得到相同的结果:0(零......空集)。除了这些结果之外,我在 django 项目页面上没有找到关于两个查询集之间该运算符的任何官方评论......你确定它像你说的那样工作吗?
我用过 | QuerySets 上的操作员多次,它对我有用。我还没有找到关于此的官方 python 文档,但其他 *** 答案证实了我的建议:***.com/questions/4411049/…。你能看到str(subset_a.query)
、str(subset_b.query)
和str(union_set.query)
的结果是什么吗?
文档 | docs.djangoproject.com/en/dev/topics/db/queries/…【参考方案3】:
Django 似乎有一个weak ORM implementation。我建议使用“过滤器”或其他方式来查询数据库,看看是否有相同的不一致。
或者也许您应该寻找替代的 ORM 实现,例如 peewee。
【讨论】:
以上是关于查询更改 Q 对象顺序的不同结果的主要内容,如果未能解决你的问题,请参考以下文章