У меня есть 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.