Django ORM вычисляет всех членов группы
Я пытаюсь аннотировать все мои группы по количеству пользователей, которые применимы к определенному условию. В данном случае я хочу получить количество пользователей, у которых модель предложения связана с задачей в группе.
Пользователи могут создавать Предложения к Задаче. Одна Задача может быть связана с одной Группой.
Итак, в результате аннотирования групп я хочу получить нечто подобное
| id | name | runners_num |
| -- | ------ | ----------- |
| 1 | name1 | 3 |
| 2 | name2 | 5 |
С помощью запроса ниже я могу получить этот граф в виде словаря
runners = User.objects.filter(
offer__tack__group_id=1
).distinct(
).values(
'id'
).aggregate(
count=Count('id')
)
output: {'count': 5}
Но я не могу понять, как это сделать с помощью выражения OuterRef
runners = User.objects.filter(
offer__task__group_id=OuterRef('id')
).distinct().values('id')
groups = Group.objects.annotate(
runners_num=Count(Subquery(runners))
)
В итоге получился вот такой неправильный запрос
SELECT
"groups"."id",
"groups"."name",
COUNT(
(
SELECT
DISTINCT U0."id"
FROM
"users" U0
INNER JOIN "offers" U1 ON (U0."id" = U1."runner_id")
INNER JOIN "tasks" U2 ON (U1."task_id" = U2."id")
WHERE
U2."group_id" = ("groups"."id")
)
) AS "runners_num"
FROM
"groups"
GROUP BY
"groups"."id"
LIMIT
21
Мои модели
class Task(models.Model):
tasker = models.ForeignKey(
"user.User", null=True, blank=True, on_delete=models.SET_NULL, related_name="tasker"
runner = models.ForeignKey(
"user.User", null=True, blank=True, on_delete=models.SET_NULL, related_name="runner",
)
group = models.ForeignKey(
"group.Group",
on_delete=models.CASCADE
)
class Offer(models.Model):
task = models.ForeignKey("task.Task", on_delete=models.CASCADE)
runner = models.ForeignKey("user.User", null=True, blank=True, on_delete=models.SET_NULL)
class Group(model.Model):
name = models.CharField(max_length=100)
class GroupMembers(models.Model):
group = models.ForeignKey("group.Group", on_delete=models.CASCADE)
member = models.ForeignKey("user.User", null=True, blank=True, on_delete=models.SET_NULL)
Укажите количество пользователей, у которых есть хотя бы одно предложение для задачи каждой группы, правильно? Я считаю, что это должно делать:
Group.objects.annotate(
runners_count=Count(
'task__offer__user', distinct=True
)
).values('id', 'runners_count')
Count() имеет несколько аккуратных kwargs, которые можно использовать подобным образом: https://docs.djangoproject.com/en/3.2/ref/models/querysets/#django.db.models.Count
Поэтому мое окончательное решение - выполнить подзапрос User и только потом агрегировать функцией Count.
users_runners = User.objects.filter(
offer__task__group_id=OuterRef(OuterRef('id'))
).annotate(
offer_num=Count(
'offer',
filter=
Q(
offer__task__completion_time__isnull=False,
offer__task__completion_time__gte=timezone.now() - timedelta(hours=24),
) |
Q(
offer__task__status__in=(
TaskStatus.ACTIVE,
TaskStatus.ACCEPTED,
TaskStatus.IN_PROGRESS,
TaskStatus.WAITING_REVIEW
),
offer__task__is_active=True,
)
)
).filter(
offer_num__gte=3
).values(
'id'
).distinct(
)
runners = User.objects.filter(
id__in=users_runners,
).annotate(
count=Func(
F('id'),
function='Count'
)
).values(
'count'
)
groups = Group.objects.annotate(
runners_num=runners
).order_by(
'id'
)