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'
)
Вернуться на верх