Аннотирование количества надмножеств полей с помощью Django

#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))