Сложная фильтрация в 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='')
  • Вопрос задан
  • 343 просмотра
Решения вопроса 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)

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

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

Войти через центр авторизации
Похожие вопросы
Omnic Москва
от 100 000 до 160 000 руб.
CRON Махачкала
от 60 000 до 140 000 руб.
23 окт. 2019, в 12:53
2000 руб./за проект
23 окт. 2019, в 12:26
3000 руб./за проект
23 окт. 2019, в 12:12
10000 руб./за проект