Django получение FK через функцию селектора
У меня есть 3 модели:
class A:
start_date=DateField
end_date=DateField
...
@property
def is_valid():
if self.start_date is not None and self.end_date is not None:
return self.start_date <= get_now().date() <= self.end_date
elif (self.start_date is None or self.start_date <= get_now.date()) and self.end_date is None:
return True
class B:
a = ManyToMany(A)
class C:
b = ForeignKey(B)
и селекторная функция get_valid_a's
, которая возвращает все допустимые экземпляры class A
, заданные одним class B
Функция селектора:
def get_valid_a's(B):
return B.A.filter((
Q(start_date__lte=get_now().date()) &
Q(end_date__gte=get_now().date())) | (
Q(start_date__lte=get_now().date()) &
Q(end_date__isnull=True)) | (
Q(start_date__isnull=True) & Q(end_date__isnull=True))
)
Я ищу что-то вроде этого:
C_with_valid_A = C.objects.filter(
d_pk=d_pk,
b__a__isnull=False,
<here I am lookig to do something like: b__a__in=get_valid_a's(b=b)
)
Любая помощь будет оценена по достоинству!
Я бы добавил custom manager к A и добавил метод к этому менеджеру, который фильтрует по действительным результатам. Вы всегда можете просто сделать этот метод classmethod для быстрой победы
(я просто вставил запрос, так как его почти невозможно проверить/разобрать :D)
class A:
@classmethod
def get_valid(cls):
return cls.objects.filter((
Q(start_date__lte=get_now().date()) & Q(end_date__gte=get_now().date())) | (
Q(start_date__lte=get_now().date()) &
Q(end_date__isnull=True)) | (
Q(start_date__isnull=True) & Q(end_date__isnull=True)))
Затем вы можете использовать этот метод в других запросах
C_with_valid_A = C.objects.filter(
d_pk=d_pk,
b__a__in=A.get_valid()
)
Я прочитал немного документации Django и нашел действительно полезную функцию уровня DB, которую можно использовать в таких случаях - Exists
Запрос на выборку выглядит следующим образом
C_with_valid_A = C.objects.annotate(
has_valid_As=Exists(
A.objects.filter((
Q(start_date__lte=get_now().date()) &
Q(end_date__gte=get_now().date())) | (
Q(start_date__lte=get_now().date()) &
Q(end_date__isnull=True)) | (
Q(start_date__isnull=True) & Q(end_date__isnull=True)),
Bs=OuterRef('b')
)).filter(has_valid_As=True,d=d_pk)
Для Django >= 3.0 тот же запрос можно записать как
C_with_valid_A = C.objects.filter(
Exists(
A.objects.filter((
Q(start_date__lte=get_now().date()) &
Q(end_date__gte=get_now().date())) | (
Q(start_date__lte=get_now().date()) &
Q(end_date__isnull=True)) | (
Q(start_date__isnull=True) & Q(end_date__isnull=True)),
Bs=OuterRef('b'),
d=d_pk
))
По сути, он делает то же самое, что и первый запрос. В моем понимании, OuterRef
представляет отношение между моделью C и моделью B.