Python Django查询集-filter()、annotate()和values()

Python Django查询集-filter()、annotate()和values(),python,django,Python,Django,我正在编写一个复杂的django数据查询程序,为了加快返回速度,我将与filter()和aggregate一起使用,并且在重复结果方面遇到了一些问题 图片amodels.py如下所示: class Person(models.Model): name= CharField() class Question(models.Model): title = CharField() date_asked = DateField() asker = ForeignKey(

我正在编写一个复杂的django数据查询程序,为了加快返回速度,我将与filter()和aggregate一起使用,并且在重复结果方面遇到了一些问题

图片a
models.py
如下所示:

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

class Question(models.Model):
    title = CharField()
    date_asked = DateField()
    asker = ForeignKey(person)
我要做的是使用
person
queryset和
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
即使使用A也不能解决问题:

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语句之前,该语句应该限制返回行

关于如何执行此查询,有什么想法吗


更新:

Person | Title                    | most_recent
----------------------------------------------
Jack   | How to climb hill?       | 2012-02-05
Jill   | How to fix head injury?  | 2014-03-06
相关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"))
Question.objects.order_by('asker__name', '-date').distinct('asker__name')
djangos对
GROUP BY
语句的特异性过于敏感。如果我运行
/manage.py dbshell
并运行上面的查询,我会得到redundent结果,但是如果我将其限制为
按“示例人员”分组。“全名”
,没有其他分组,我会得到正确的结果


有没有办法限制django的
分组方式
或某种猴子补丁,只是稍微限制一下?

根据您的后端,您应该能够通过
命令和
不同的
来实现这一点,如下所示:

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"))
Question.objects.order_by('asker__name', '-date').distinct('asker__name')

这应该按照提问者的姓名和日期降序排列对象,然后为每个提问者回答第一个问题,这将是最新的问题。您没有提到您正在使用的后端,因此如果您使用的是像SQLite这样不支持distinct的东西,您可能需要用另一种方法来实现这一点。

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

Django不喜欢你玩
groupby
语句,它们被隐藏得很深。瓦艾深

不过,使用这个(仅适用于Django 1.7)monkey修补程序,您可以覆盖分组的方式。在下面的示例中,我们捕获django认为您应该拥有的分组,然后在且仅当此查询使用聚合时将其缩减(只有在存在聚合时才会填充
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

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

您是否尝试添加
distinct()
?在这里可以找到类似的问题,但这些答案可能不令人满意。这是一个难题。@RetoAebersold
distinct
不起作用,因为它返回不同的行。我看不到你的人和问题模型之间有任何关系。@Burnkhalid eek…修复了现在由于太复杂的原因,我需要使用t他
Person
queryset,而不是
问题
queryset