【发布时间】:2018-06-13 18:05:06
【问题描述】:
我有一些对象的QuerySet。对于每一个,我希望用相关模型的最小值进行注释(在几个条件下加入,按日期排序)。我可以用 SQL 简洁地表达我想要的结果,但很好奇如何翻译成 Django 的 ORM。
背景
假设我有两个相关模型:Book 和 BlogPost,每个模型都有一个指向 Author 的外键:
class Book(models.Model):
title = models.CharField(max_length=255)
genre = models.CharField(max_length=63)
author = models.ForeignKey(Author)
date_published = models.DateField()
class BlogPost(models.Model):
author = models.ForeignKey(Author)
date_published = models.DateField()
我正试图找到给定作者在他们撰写的每篇博文之后发表的第一本神秘书。在 SQL 中,这可以通过窗口很好地实现。
PostgreSQL 9.6 中的工作解决方案
WITH ordered AS (
SELECT blog_post.id,
book.title,
ROW_NUMBER() OVER (
PARTITION BY blog_post.id ORDER BY book.date_published
) AS rn
FROM blog_post
LEFT JOIN book ON book.author_id = blog_post.author_id
AND book.genre = 'mystery'
AND book.date_published >= blog_post.date_published
)
SELECT id,
title
FROM ordered
WHERE rn = 1;
翻译成 Django 的 ORM
虽然上述 SQL 非常适合我的需求(如果需要,我可以使用原始 SQL),但我很好奇如何在 QuerySet 中执行此操作。我有一个现有的 QuerySet,我想进一步注释它
books = models.Book.objects.filter(...).select_related(...).prefetch_related(...)
annotated_books = books.annotate(
most_recent_title=...
)
我知道 Django 2.0 支持窗口函数,但我现在使用 Django 1.10。
尝试的解决方案
我首先构建了一个 Q 对象,以过滤在博文之后发布的神秘书籍。
published_after = Q(
author__book__date_published__gte=F('date_published'),
author__book__genre='mystery'
)
从这里开始,我尝试将 django.db.models.Min 和其他 F 对象拼凑在一起以达到我想要的结果,但没有成功。
注意:Django 2.0 引入了窗口表达式,但我目前使用的是 Django 1.10,并且很好奇如何使用那里提供的 QuerySet 功能来做到这一点。
【问题讨论】:
-
老实说(而且根本不是专家),我不认为您可以使用 Django 的 1.10 ORM 在单个查询中执行此操作... Django 1.11 和(如您所说)窗口表达式在 2.0 :-( 中引入
-
你试过
annotate(first=Min(<related_model__id>)).filter(<conditions with timestamps>)吗?
标签: python django postgresql django-queryset django-1.10