Django ORM:是否可以注入子查询?

时间:2015-01-29 12:31:27

标签: django postgresql orm

我有一个看起来像这样的Django模型:

class Result(models.Model):
    date = DateTimeField()
    subject = models.ForeignKey('myapp.Subject')
    test_type = models.ForeignKey('myapp.TestType')
    summary = models.PositiveSmallIntegerField()
    # more fields about the result like its location, tester ID and so on

有时我们想要检索所有测试结果,有时我们只想要每个主题的特定测试类型的最新结果。 This answer有一些很好的SQL选项可以找到最新的结果。

此外,我们有时希望将结果分成不同的时间块,以便我们可以绘制每天/每周/每月的结果数。

我们还希望对各个字段进行过滤,为了优雅,我想要一个QuerySet,然后我可以对所有的filter()调用进行调用,并对计数进行注释,而不是进行原始的SQL调用。

我到目前为止:

qs = Result.objects.extra(select = {
         'date_range': "date_trunc('{0}', time)".format("day"), # Chunking into time buckets
         'rn' : "ROW_NUMBER() OVER(PARTITION BY subject_id, test_type_id ORDER BY time DESC)"})
qs = qs.values('date_range', 'result_summary', 'rn')
qs = qs.order_by('-date_range')

导致以下SQL:

SELECT (ROW_NUMBER() OVER(PARTITION BY subject_id, test_type_id ORDER BY time DESC)) AS "rn", (date_trunc('day', time)) AS "date_range", "myapp_result"."result_summary" FROM "myapp_result" ORDER BY "date_range" DESC

这是接近我想要的东西,但现在我需要以某种方式过滤以仅获取rn = 1的行。我尝试使用extra()中的'where'字段,这给了我以下SQL和错误:

SELECT (ROW_NUMBER() OVER(PARTITION BY subject_id, test_type_id ORDER BY time DESC)) AS "rn", (date_trunc('day', time)) AS "date_range", "myapp_result"."result_summary" FROM "myapp_result" WHERE "rn"=1 ORDER BY "date_range" DESC                                                                                                                            ;
ERROR:  column "rn" does not exist

所以我认为找到“rn”的查询需要是一个子查询 - 但有可能以某种方式这样做,也许使用extra()?

我知道我可以用原始SQL做到这一点,但它看起来很难看!我很想找到一个很好的方法,我有一个可过滤的QuerySet。

我想另一种选择是在模型中有一个字段,表明它是否实际上是该主题的测试类型的最新结果......

1 个答案:

答案 0 :(得分:2)

我找到了办法!

qs = Result.objects.extra(where = ["NOT EXISTS(SELECT * FROM myapp_result as T2 WHERE (T2.test_type_id = myapp_result.test_type_id AND T2.subject_id = myapp_result.subject ID AND T2.time > myapp_result.time))"])

这是基于the answer I referenced earlier的其他选项。我可以用我想要的任何东西来过滤或注释q。


顺便说一句,在通往此解决方案的路上,我尝试了这个:

qq = Result.objects.extra(where = ["NOT EXISTS(SELECT * FROM myapp_result as T2 WHERE (T2.test_type_id = myapp_result.test_type_id AND T2.subject_id = myapp_result.subject ID AND T2.time > myapp_result.time))"])
qs = Result.objects.filter(id__in=qq)

Django按照您的意愿嵌入子查询:

SELECT ...some fields... FROM "myapp_result" 
WHERE ("myapp_result"."id" IN (SELECT "myapp_result"."id" FROM "myapp_result" 
WHERE (NOT EXISTS(SELECT * FROM myapp_result as T2 
WHERE (T2.subject_id = myapp_result.subject_id AND T2.test_type_id = myapp_result.test_type_id AND T2.time > myapp_result.time))))) 

我意识到这有比我需要的更多的子查询,但是我在这里注意到它,因为我可以想象知道你可以用另一个查询集过滤一个查询集是有用的,而Django完全符合你的希望。嵌入子查询(而不是执行它并嵌入返回的值,这将是可怕的。)