Джанго и условные агрегаты
У меня есть две модели, авторы и статьи:
class Author(models.Model):
name = models.CharField('name', max_length=100)
class Article(models.Model)
title = models.CharField('title', max_length=100)
pubdate = models.DateTimeField('publication date')
authors = models.ManyToManyField(Author)
Теперь я хочу выбрать всех авторов и аннотировать их с соответствующим количеством статей. Это кусок пирога с агрегатами Джанго. Проблема в том, что следует учитывать только те статьи, которые уже опубликованы. Согласно билету 11305 в трекере Django, это пока невозможно. Я пытался использовать CountIf
аннотация, упомянутая в этом билете, но она не заключает в кавычки строку даты и времени и не делает все соединения, которые ей понадобятся.
Итак, что является лучшим решением, кроме написания собственного SQL?
3 ответа
Django 1.8+ решение
Начиная с Django 1.8, условные выражения доступны для построения наборов запросов.
Для более подробной информации обратитесь к документации, но быстрое решение для вашего вопроса будет выглядеть примерно так:
today = datetime.date.today()
authors = Author.objects.all().annotate(article_count=Sum(
Case(When(articles__pubdate__lt=today, then=1),
output_field=IntegerField())
))
Я не проверял это хотя, но это должно работать.
Вы можете использовать приложение django-aggregate-if, вдохновленное билетом 11305. Или вы можете просто использовать extra
метод для набора запросов (предположим, что ваше приложение называется "article"):
Author.objects.all().extra(
select={'article_count': 'SELECT COUNT(*) FROM "articles_article" '
'INNER JOIN "articles_article_authors" '
'ON "articles_article"."id" = '
' "articles_article_authors"."article_id" '
'WHERE "articles_article_authors"."author_id" = '
' "articles_author"."id" '
'AND "articles_article"."pubdate" IS NOT NULL'})
Я решил свою проблему, создав SQL-представление с необходимыми GROUP BY
заявление и модель для указанного вида с managed = False
и OneToOneField
к Author
Таблица. Это не самое эффективное или элегантное решение, но оно работает хорошо.