Сложная фильтрация в Django, как отфильтровать элементы по вложенным крайним элементам?

Есть три модели: Job, WorkItem и DocumentPackage.

У Job есть ForeignKey на WorkItem, а у WorkItem есть ForeignKey на DocumentPackage.
Задача состоит в том, чтобы отфильтровать только те DocumentPackage, в которых у всех WorkItem'ов, Job с самым большим id имеет state=Job.STATE_COMPLETE

def finished(qs):
    active_jobs = Job.objects.filter(item=OuterRef('pk')).order_by('-id')

    active_items = (
        WorkItem.objects.annotate(
            active_job_state=Subquery(active_jobs.values('state')[:1])
        ).filter(
            active_job_state=Job.STATE_COMPLETE
        )
    )

    qs = qs.prefetch_related(
        Prefetch(
            'work_items',
            queryset=active_items,
            to_attr='active_items',
        )
    ).annotate(
        _active=Count('active_items'),
    ).annotate(
        _total=Count('work_items')
    )

    return qs.filter(_total=F('_active'))


Ошибка возникает при вычислении Count('active_items'):
FieldError: Cannot resolve keyword 'active_items' into field.


Модели (лишние поля убрал):
class Job(models.Model):
    STATE_PENDING = 0
    STATE_TODO = 1
    STATE_PAUSED = 2
    STATE_REQUESTED = 3
    STATE_IN_PROGRESS = 4
    STATE_READY = 5
    STATE_COMPLETE = 6
    STATE_RETURNED = 7
    STATE_ABORTED = 8
    STATE_HISTORY = 9

    STATE_CHOICES = Choices(
        (STATE_PENDING, 'Pending'),
        (STATE_TODO, 'ToDo'),
        (STATE_PAUSED, 'Paused'),
        (STATE_REQUESTED, 'Requested'),
        (STATE_IN_PROGRESS, 'InProgress'),
        (STATE_READY, 'Ready'),
        (STATE_COMPLETE, 'Complete'),
        (STATE_RETURNED, 'Returned'),
        (STATE_ABORTED, 'Aborted'),
        (STATE_HISTORY, 'History'),
    )
    # Идентификатор документа
    item = models.ForeignKey('WorkItem', on_delete=models.CASCADE, related_name='jobs')
    # Состояние:
    state = models.IntegerField(default=STATE_IN_PROGRESS, choices=STATE_CHOICES)


class WorkItem(ModelDiffMixin, models.Model):
    # Идентификатор пакета, в который входит данный документ
    package = models.ForeignKey('DocumentPackage', related_name='work_items', on_delete=models.CASCADE)


class DocumentPackage(models.Model):
    # Наименование пакета
    name = models.TextField(null=True, blank=True, default='')
  • Вопрос задан
  • 632 просмотра
Решения вопроса 1
@tumbler
бекенд-разработчик на python
Вам нужно сгруппировать WorkItemJob по DocumentPackage, а потом получить значение функции argmax(id) - строку с максимальным ID. Это возможно сделать через оконные функции, в DjangoORM поддерживаются.

queryset.annotate(
            last_job_status=Window(
                expression=LastValue("workitem__job__status"),
                partition_by=F('workitem__id'),
                order_by=F('workitem__job__id')
            )

).filter(last_job_status=Job.STATE_COMPLETED)

Как-то так примерно.
Ответ написан
Пригласить эксперта
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Войти через центр авторизации
Похожие вопросы