#python #django #postgresql #django-models
#python #django #postgresql #django-модели
Вопрос:
Итак, настройка здесь такова, что у меня есть Post
таблица, содержащая кучу сообщений. Некоторые из этих строк представляют собой разные версии одного и того же сообщения, которые сгруппированы по post_version_group_id
, так что это выглядит примерно так:
pk | title | post_version_group_id
1 | a | 123
2 | b | 789
3 | c | 123
4 | d | 123
итак, есть две «группы» сообщений и всего 4 сообщения. Теперь у каждого сообщения есть внешний ключ, указывающий на PostDownloads
таблицу, которая выглядит как
post | user_downloaded
1 | user1
2 | user2
3 | user3
4 | user4
что я хотел бы иметь возможность сделать, так это аннотировать мой Post
набор запросов, чтобы он выглядел так:
pk | title | post_version_group_id | download_count
1 | a | 123 | 3
2 | b | 789 | 1
3 | c | 123 | 3
4 | d | 123 | 3
т.е. все сообщения с одинаковым post_version_group_id
количеством имеют одинаковое количество (являющееся суммой загрузок в разных версиях).
На данный момент я сейчас делаю:
Post.objects.all().annotate(download_count=models.Count("downloads__user_downloaded, distinct=True))
что не совсем работает, оно аннотирует a download_count
, который выглядит как:
pk | title | post_version_group_id | download_count
1 | a | 123 | 1
2 | b | 789 | 1
3 | c | 123 | 1
4 | d | 123 | 1
поскольку downloads__user_downloaded
, по-видимому, оно ограничено только набором строк внутри downloads
таблицы, который связан с текущей строкой post, которая аннотируется, что имеет смысл — на самом деле, но в данном конкретном случае работает против меня.
Одна вещь, которую я также пробовал, это
Post.objects.all().values("post_version_group_id").annotate(download_count=Count("downloads__user_downloaded", distinct=True))
какой тип работ, но .values()
бит разбивает набор запросов и экземпляров post на набор запросов dicts — и мне нужно, чтобы он оставался набором запросов экземпляров post.
Фактические модели выглядят примерно так:
class Post:
title = models.CharField()
post_version_group_id = models.UUIDField()
class PostDownloads:
post = models.ForeignKey(Post)
user_downloaded = models.ForeignKey(User)
Комментарии:
1. Можете ли вы добавить точные модели в OP?
2. Фактические модели довольно большие @ArakkalAbu — но псевдотаблицы, которые я записал, должны быть точным представлением соответствующих полей модели, позвольте мне посмотреть, смогу ли я немного расширить.
Ответ №1:
Итак, я закончил тем, что выяснил это, и подумал, что отправлю ответ для всех, кто застрял в той же колее. Ключевым моментом здесь было использование a Subquery
, но не просто any Subquery
— пользовательского, который возвращает количество строк, а Subquery
не тип по умолчанию, который возвращает одну строку данных.
Первым шагом является определение этого пользовательского типа подзапроса:
class SubqueryCount(models.Subquery):
template = "(SELECT count(*) FROM (%(subquery)s) _count)"
output_field = models.IntegerField()
Затем построение подзапроса:
downloads_subquery = PostDownloads
.objects
.filter(
post__post_version_group_id=models.OuterRef(
"post_version_group_id"
)
)
.distinct("user")
какие фильтры основаны на том идентификаторе версии группировки, который у меня был.
И, наконец, выполнение подзапроса в аннотации:
Post.objects.annotate(download_count=SubqueryCount(downloads_subquery))