【问题标题】:Django querysets - filter(), annotate() and values()Django 查询集 - filter()、annotate() 和 values()
【发布时间】:2015-10-17 01:49:14
【问题描述】:

我正在编写一个复杂的 django 数据查询器并加快返回速度,我将values() 与 filter() 和聚合一起使用,但我遇到了一些重复结果的问题。

像这样描绘models.py

class Person(models.Model):
    name= CharField()

class Question(models.Model):
    title = CharField()
    date_asked = DateField()
    asker = ForeignKey(person)

我想要做的是使用Person 查询集和values() 查询django 以获取一个人的姓名和他们最近的问题的标题。

如果我们有以下样本数据:

Person | Title                    | Date
----------------------------------------------
Jack   | Where can I get water?   | 2011-01-04
Jack   | How to climb hill?       | 2012-02-05
Jill   | How to fix head injury?  | 2014-03-06

我可以得到大部分的方法,像这样:

最近一个问题的人名和日期列表:

Person.objects.values('name','most_recent')\\
              .annotate('most_recent'=Max('question__date_asked'))

Person | most_recent
--------------------
Jack   | 2012-02-05
Jill   | 2014-03-06

人名列表以及他们所有的问题和他们的头衔:

Person.objects.values('name','question__title','question__date_asked')

Person | Title                    | Date
----------------------------------------------
Jack   | Where can I get water?   | 2011-01-04
Jack   | How to climb hill?       | 2012-02-05
Jill   | How to fix head injury?  | 2014-03-06

但是当我尝试将它们放在一起时:

Person.objects.values('name','question__title','most_recent')\\
              .annotate('most_recent'=Max('question__date_asked'))
              .filt

Person | Title                    | most_recent
----------------------------------------------
Jack   | Where can I get water?   | 2011-01-04
Jack   | How to climb hill?       | 2012-02-05
Jill   | How to fix head injury?  | 2014-03-06

即使使用F() expression 也无法解决问题:

Person.objects.values('name','question__title','most_recent')\\
              .annotate('most_recent'=Max('question__date_asked'))
              .filter('question__date_asked'=F('most_recent'))

Person | Title                    | most_recent
----------------------------------------------
Jack   | Where can I get water?   | 2011-01-04
Jack   | How to climb hill?       | 2012-02-05
Jill   | How to fix head injury?  | 2014-03-06

注意:在上表中,给出了每个关系的“最大”日期,而不是每个人。

我需要的是:

Person | Title                    | most_recent
----------------------------------------------
Jack   | How to climb hill?       | 2012-02-05
Jill   | How to fix head injury?  | 2014-03-06

语句的顺序和连接意味着当同时使用过滤器、聚合和值时,连接发生在 SQL USING 语句之前,应该限制返回行.

关于如何执行此查询的任何想法?


更新:

相关的 SQL 查询如下所示:

SELECT "example_person"."full_name", "example_question"."title",
       MAX("example_question"."date_asked") AS "max___example_question__date_asked"
FROM "example_person"
  LEFT OUTER JOIN
     "example_question" ON ( "example_person"."id" = "example_question"."person_id" )
  INNER JOIN
     "example_question" T3 ON ( "example_person"."id" = T3."person_id" )
GROUP BY
     "example_person"."full_name", T3."start_date",
     "example_person"."id", "example_question"."title"
HAVING
     T3."date_asked" = (MAX("example_person"."date_asked"))

这个问题与 djangos 的 GROUP BY 语句的特异性有关。如果我运行 ./manage.py dbshell 并运行上面的查询,我会得到多余的结果,但如果我将其限制为 GROUP BY "example_person"."full_name" 而没有其他分组,我会得到正确的结果。

有没有办法限制 django 的 GROUP BY 或某种猴子补丁来限制它一点点?

【问题讨论】:

  • 您是否尝试添加distinct()
  • 这里可以找到类似的问题,但是这些答案可能并不令人满意。这是一个棘手的问题。 stackoverflow.com/questions/31234880/…
  • @RetoAebersold distinct 不起作用,因为它返回不同的行
  • 我看不出你的 Person 和 Question 模型之间有任何关系。
  • @BurhanKhalid eek... 现在修复了

标签: python django


【解决方案1】:

这是我将更新的部分答案,但我找到了方法。

Django 不喜欢你玩GROUP BY 语句,它们被埋得很深。好深啊。

但是,使用这个(仅限 Django 1.7)猴子补丁,您可以覆盖分组的完成方式。在下面的示例中,我们捕获了您应该拥有的 django thinks 分组,然后当且仅当此查询使用聚合时才将其削减(仅当存在聚合时才会填充 having_group_by 参数。

_get_grouping  = SQLCompiler.get_grouping
def custom_get_grouping(compiler,having_group_by, ordering_group_by):
    fields,thing = _get_grouping(compiler,having_group_by, ordering_group_by)
    if having_group_by:
        fields = fields[0:1]+[".".join(f) for f in having_group_by]
    return fields,thing

SQLCompiler.get_grouping = custom_get_grouping

希望很快会有更好的方法......

【讨论】:

    【解决方案2】:

    根据您的后端,您应该可以使用 order_bydistinct 来完成此操作,如下所示:

    Question.objects.order_by('asker__name', '-date').distinct('asker__name')
    

    这应该按提问者的姓名和日期降序对您的对象进行排序,然后为每个提问者选择第一个问题,这将是最新的问题。您没有提及您正在使用的后端,因此如果您使用的是不支持 distinct 的 SQLite 之类的东西,您可能必须以另一种方式执行此操作。

    【讨论】:

    • 由于太复杂而无法进入,我需要使用Person查询集,而不是Question查询集
    猜你喜欢
    • 1970-01-01
    • 2017-10-10
    • 2014-07-14
    • 2018-10-23
    • 2020-10-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-10-04
    相关资源
    最近更新 更多