查询更改Q对象顺序的结果不同

时间:2013-11-07 15:57:39

标签: django django-queryset django-q

使用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秒。实际返回我想要的是LEFT OUTER JOIN的那个。这让我觉得我的所有查询都可以返回不良结果,具体取决于我如何安排其条件。这是一个错误,或者我做错了什么(或一切)?

3 个答案:

答案 0 :(得分:3)

不同的顺序应该在您的示例中返回相同的结果。

尽管如此,我测试了您的代码(使用我在问题代码中进行的更正),但无法生成您描述的错误。也许你已经介绍了其他错误并且在简化代码时错过了,你能发布你使用的样本数据吗? (见下面的数据)。

首先你的示例代码是错误的我编辑了你的问题以建议修正问题后进行修正,简化并改进它以进行测试,但我没有看到更改的更新,所以我在此重复:

更正1:差异格式的模型更改:

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. 添加你的模特&amp;调整项目设置(数据库设置,将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. 在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)>]
    
  7. 结果相同! ......正如所料。

答案 1 :(得分: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将确保您不会获得重复行。我很想知道这里的安排是否也很重要。

答案 2 :(得分:0)

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

或许你应该寻找替代的ORM实现,比如peewee