Django prefetch_related, отфильтрованный по атрибуту внешнего запроса
Я работаю над простым Django-приложением, которое хранит и показывает программы для событий:
- Существует таблица
Participant
, которая также используется в системе аутентификации Django. - Каждый
Participant
может иметь несколькоAffiliation
с количествомInstitute
s. - Каждый
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
внешнего запроса, а только получить всеSlot
s'start
, поэтому возвращаются толькоAffiliations
, действительные на момент самого старогоSlot
.