2

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 语句时,我得到两个不同的语句:一个生成 aLEFT OUTER JOIN和很多INNER JOINs,第二个生成所有INNER JOINs。真正返回我想要的东西的是制作LEFT OUTER JOIN. 这让我觉得我的所有查询都可能返回糟糕的结果,具体取决于我如何安排它的条件。这是一个错误还是我做错了什么(或一切)?

4

3 回答 3

3

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

不过,我测试了您的代码(使用我在问题代码中所做的更正),但无法生成您描述的错误。也许您在简化代码时引入了其他错误并错过了,您可以发布您使用的示例数据吗?(见下面我的数据)。

首先,您的示例代码有问题我已经编辑了您的问题,建议进行以下更正以解决问题,简化和改进测试,但我没有看到更改更新,所以我在这里重复:

更正 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. 添加您的模型并调整项目设置(数据库设置,添加testsoINSTALLED_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)>]
    

结果一样!...正如预期的那样。

于 2013-11-16T16:11:18.390 回答
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 将确保您不会得到重复的行。我很想知道这里的安排是否也很重要。

于 2013-11-13T18:35:58.807 回答
0

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

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

于 2013-11-13T18:09:32.843 回答