Получение количества столбцов, различающихся по двум столбцам
Вот упрощенное представление моих моделей:
class Post(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE)
template_id = models.IntegerField(null=True)
...
Я хочу показать количество раз, когда шаблон был использован пользователями. Поэтому, когда я перечисляю шаблоны, я хочу иметь возможность сказать Used by X users
. Основная проблема заключается в том, что я не хочу учитывать пользователя только один раз (поэтому если пользователь использует шаблон дважды, он все равно считается "одним случаем использования"). Все посты на stackoverflow говорят о том, чтобы сделать что-то вроде этого:
counts = Post.objects.all().values("template_id").order_by().annotate(count=Count("template_id"))
Но это явно удваивает количество пользователей, которые используют один и тот же шаблон дважды. Я смог сделать отчетливый подсчет пар template_id
и user
следующим образом:
Post.objects.all().values("template_id", "user__id").distinct()
# Printing this out, I get 2 distinct entries in the QuerySet:
# <QuerySet [{'template_id': 1, 'user__id': 1}, {'template_id': 1, 'user__id': 2}]>
Однако, когда я пытаюсь получить подсчеты template_id
(код ниже), кажется, что он игнорирует distinct
и по-прежнему дважды подсчитывает пользователей.
Post.objects.all().values("template_id", "user__id").distinct().values("template_id").annotate(count=Count("template_id"))
# Printing this out I get `count` = 3, which double counts a user.
# <QuerySet [{'template_id': 1, 'count': 3}]>
Для того, что это стоит, я написал быстрый тестовый пример, который и дает сбой.
user1 = baker.make("User")
user2 = baker.make("User")
# Populate posts
quest1 = baker.make("post.Post", user=user1, template_id=1)
quest2 = baker.make("post.Post", user=user1, template_id=1) # Duplicate shouldn't count
quest3 = baker.make("post.Post", user=user2, template_id=1)
Почему вы просто не используете:
counts = Post.objects.all().values("template_id", "user__id").distinct().values("template_id").count()
Заставил его работать, используя встроенный в Django ORM, сделав следующее:
template_ids = [] # My templates
# Get the number of times each template_id was used.
top_template_counts = (
Post.objects.filter(template_id__in=template_ids)
.values("template_id") # groups by template ids
.annotate(user_count=Count("user", distinct=True)) # Gets the number of users using each template
.order_by("-user_count")
)
# Accessing `top_template_counts`
for template_id_count in top_template_counts[:10]:
template_id = template_id_count["template_id"]
count = template_id_count["parent_count"]