【问题标题】:Different results on query changing Q object order查询更改 Q 对象顺序的不同结果
【发布时间】: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 JOINs,第二个生成所有 INNER JOINs。真正返回我想要的东西是制作LEFT OUTER JOIN 的那个。这让我觉得我的所有查询都可能返回糟糕的结果,具体取决于我如何安排它的条件。这是一个错误还是我做错了什么(或一切)?

【问题讨论】:

标签: django django-queryset django-q


【解决方案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:您在查询中的查找字段错误(已修复)。

以下是我所做的:

  1. 创建名为testso的项目和应用程序:

    django-admin.py startproject marianobianchi
    cd marianobianchi
    python manage.py startapp testso
    
  2. 添加您的模型并调整项目设置(数据库设置,将testso 添加到INSTALLED_APPS

  3. 添加示例数据:

    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}}
    ]
    
  4. python manage.py syncdb

  5. python manage.py shell
  6. 在外壳中:

    >>> 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 ...我会再次提出更正,我认为你不需要接受此提案的声誉;)
  • 提案更改完成,在我看到您的问题更新后,我将从我的答案中删除
  • 好的...我明白您的意思,但您知道这不是问题...关于您的提议,我看到了有关提议的版本的通知,但是当我点击那里时,我可以看到您的更改没有任何按钮可以接受它们....
  • 您应该会看到批准/拒绝编辑建议的按钮,也许thisthis 可以帮助您。您应该认为它不能解决您的问题,但可以帮助人们运行您的测试示例,如果它没有运行,如何尝试解决它?,我修复了它并建议进行更改以使不仅由我更容易找到解决方案。
【解决方案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_bsubset_b | subset_a 时,我得到相同的结果:0(零......空集)。除了这些结果之外,我在 django 项目页面上没有找到关于两个查询集之间该运算符的任何官方评论......你确定它像你说的那样工作吗?
  • 我用过 | QuerySets 上的操作员多次,它对我有用。我还没有找到关于此的官方 python 文档,但其他 StackOverflow 答案证实了我的建议:stackoverflow.com/questions/4411049/…。你能看到str(subset_a.query)str(subset_b.query)str(union_set.query)的结果是什么吗?
【解决方案3】:

Django 似乎有一个weak ORM implementation。我建议使用“过滤器”或其他方式来查询数据库,看看是否有相同的不一致。

或者也许您应该寻找替代的 ORM 实现,例如 peewee

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2013-05-23
    • 1970-01-01
    • 2012-01-02
    • 1970-01-01
    • 2015-06-24
    • 1970-01-01
    • 2018-01-14
    相关资源
    最近更新 更多