Django prefetch_related, отфильтрованный по атрибуту внешнего запроса

Я работаю над простым Django-приложением, которое хранит и показывает программы для событий:

  • Существует таблица Participant, которая также используется в системе аутентификации Django.
  • Каждый Participant может иметь несколько Affiliation с количеством Institutes.
  • Каждый Affiliation имеет (nullable) start и end временную метку.
  • Каждый Slot представляет собой одно мероприятие (беседа, обед, семинар) и имеет start временную метку. В каждый слот может быть назначено несколько человек.
class Participant(models.Model):
    name = models.CharField(max_length=256)
    institutes = models.ManyToManyField('Institute', through='Affiliation', related_name='people')
    
class Institute(models.Model):
    name = models.CharField(max_length=256)
    
class Affiliation(models.Model):
    person = models.ForeignKey('Participant', on_delete=models.CASCADE, related_name='affiliation')
    institute = models.ForeignKey('Institute', on_delete=models.CASCADE, related_name='affiliation')
    start = models.DateField(null=True, blank=True)
    end = models.DateField(null=True, blank=True)

class Slot(models.Model):
    title = models.CharField(blank=True, max_length=256)
    abstract = models.TextField(blank=True, max_length=4096)
    start = models.DateTimeField(null=True, blank=True)
    person = models.ManyToManyField('Participant', related_name='slots', blank=True)

Я хотел бы показать одно событие Slot и ответственных за него людей вместе с их Affiliations, но только тех, которые действительны на начало этого события (где start - это null или раньше Slot.start, а end - это null или позже Slot.start).

Я написал метод QuerySet для этого, и сам по себе он работает правильно, как я мог убедиться с помощью manage.py shell:

class ParticipantQuerySet(models.QuerySet):
    def with_current_affiliations(self, date: datetime.date = None):
        date = date or datetime.date.today()
        return self.prefetch_related(
            Prefetch(
                'affiliation',
                queryset=Affiliation.objects.with_institute().filter(
                    Q(start__lte=date) | Q(start__isnull=True),
                    Q(end__gte=date) | Q(end__isnull=True)
                ),
                to_attr='current_affiliations',
            ),
        )

Вызов его оттуда, где он мне нужен (DetailView из Slot), не делает того, что я хотел бы. Мне не хватает способа ссылаться на атрибут start в Slot.

class SlotQuerySet(models.QuerySet):
    def with_people(self):
        Participant = apps.get_model('core', 'Participant')

        return self.prefetch_related(
            Prefetch(
                'person',
                queryset=Participant.objects.with_current_affiliations(???)),
                to_attr='people',
            )
        )      

class SlotView(DetailView):
    model = Slot

    def get_queryset(self):
        return self.model.objects.with_people()          

Мой вопрос заключается в следующем: Что поставить вместо ??? здесь, чтобы оно ссылалось на исходное Slot в start и чтобы возвращались только те Affiliation, которые действительны на данный момент?

  • F('start') ссылается на start из Affiliation (который затем сравнивается __gte с самим собой, поэтому возвращает все Affiliation).
  • F('person__slots__start') возвращает список действительных связей для всех Slot из данного Participant, даже если они не связаны с данным Slot.
  • OuterRef('start') очевидно не может быть использован напрямую, так как Prefetch не является подзапросом.
  • Subquery(Slot.objects.filter(person__id=OuterRef('id')).values('start')) не решает проблему: я все еще не могу обратиться к start внешнего запроса, а только получить все Slots' start, поэтому возвращаются только Affiliations, действительные на момент самого старого Slot.
Вернуться на верх