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.