Django аннотирует кверисет одной модели методом кверисета другой модели

Я не уверен, что то, что я хочу сделать, можно сделать.

Я хочу фильтровать или исключать элементы из одного объекта QuerySet, используя аннотированные значения QuerySet другой модели, связанной с первой через FK.

модели

Ниже обзор моделей, о которых я говорю:
две модели сидят в моем main.models.py

  1. класс Member и его Менеджер
from django.db import models
from django.contrib.auth.models import UserManager

class MemberManager(UserManager):
  def get_queryset(self):
    return MemberQuerySet(self.model, using=self._db)

  def annotate_something(self):
    return self.get_queryset().annotate_something_important()

class Member(User):
  objects = MemberManager()
  field_1 = models.BooleanField(default = False)
  field_2 = models.CharField(max_length = 1, blank = True, null = True)
  ...
  1. класс Experience и его Менеджер
class ExperienceManager(models.Manager):
    def get_queryset(self):
        return ExperienceQuerySet(self.model, using=self._db)
    
    def annotate_duracion(self):
        return self.get_queryset().annotate_duracion()

class Experience(models.Model):
    objects = ExperienceManager()

    member = models.ForeignKey(Member, related_name='experiences', on_delete = models.CASCADE)
    start_date = models.DateField(blank = True, null = True)
    end_date = models.DateField(blank = True, null = True)

обратите внимание на FK в Experience на Member

классы MemberQuerySet и ExperienceQuerySet определяются в отдельном main.queryset.py файле

class MemberQuerySet(models.QuerySet):

    def annotate_something_important(self):
        return self.something_important
...

class ExperienceQuerySet(models.QuerySet):

    def annotate_duration(self):
        current_date = datetime.now().date()

        return self.annotate(
            end_date_obj = Case(
                When(end_date = None, then = Value(current_date)),
                default = F('end_date'),
                output_field = DateField(),
            )
        ).annotate(
            duration = ExpressionWrapper(
                F('end_date_obj') - F('start_date'),
                output_field = DurationField()
            )
        ).order_by("-end_date_obj")

запрос

начальной точкой является объект MemberQueryset, полученный из следующего запроса, который может состоять из ~400k элементов.

members = Member.objects.filter(
    field_1 = True
).annotate_something()

Основной целью функции является постепенная фильтрация данного набора QuerySet на основе критериев, считанных из views.py

препятствие

Теперь начинается самая мясная часть:
Я хочу исключить всех тех членов, чей последний 3 опыт длится менее 1 года

Для достижения этого я предусмотрел annotate на членах (экземпляр MemberQueryset), используя related_name='experiences'. однако, я должен сделать что-то вроде вызова метода annotate_duration в цепочке поиска, что не работает.

ориентировочное решение

Я прибегнул к следующему

bad_ids = [
    m.id for m in members if not all(
        m.experiences.all().annotate_duration()[:3].annotate(
            long_experience=Case(
                When(
                    duration__gte=year, 
                    then=Value(True, output_field=BooleanField())
                ),
                default=Value(False, output_field=BooleanField())
            )
        ).values_list('long_experience', flat=True)
    )
]

members = members.exclude(id__in = bad_ids)

Проблема такого подхода заключается в цикле for над членами, что резко увеличивает время вычислений скрипта, часто приводя к тому, что задача будет убита за чрезмерные затраты времени (я полагаю, основываясь на этой ошибке "Worker exited prematurely: signal 9 (SIGKILL).").

Кто-нибудь знает, как этого добиться, избегая громоздкого цикла for?

ресурсы

ниже список ресурсов, которые я прочитал
Как аннотировать queryset другим queryset
django model method to return queryset with annotate
Django - Query : аннотируйте кверисет с помощью связанных полей модели

Я чувствую, что последний может содержать элементы для достижения моей цели, но я не знаю, как их использовать.
Любая помощь будет высоко оценена. Будьте здоровы

Вернуться на верх