Django: Изменение Queryset уже определенного объекта Prefetch
Context
Я очень хочу, но не понимаю, как я могу ограничить уже существующий объект Prefetch
Модели
class MyUser(AbstractUser):
pass
class Absence(Model):
employee = ForeignKey(MyUser, related_name='absences', on_delete=PROTECT)
start_date = DateField()
end_date = DateField()
View
class UserAbsencesListAPIView(ListAPIView):
queryset = MyUser.objects.order_by('first_name')
serializer_class = serializers.UserWithAbsencesSerializer
filterset_class = filters.UserAbsencesFilterSet
Filter
class UserAbsencesFilterSet(FilterSet):
first_name = CharFilter(lookup_expr='icontains', field_name='first_name')
from_ = DateFilter(method='filter_from', distinct=True)
to = DateFilter(method='filter_to', distinct=True)
Что мне нужно
В запросе есть два аргумента from_
и to
. Я должен вернуть Пользователей с их Отсутствиями, которые (Отсутствия) ограничены интервалами from_
и/или to
. Это очень просто для одного аргумента, я могу ограничить набор, используя объект Prefetch
:
def filter_from(self, queryset, name, value):
return queryset.prefetch_related(
Prefetch(
'absences',
Absence.objects.filter(Q(start_date__gte=value) | Q(start_date__lte=value, end_date__gte=value)),
)
)
Аналогично для to
.
Но что если я хочу получить ограничение сразу по двум аргументам?
Когда запрашивается атрибут from_
- выполняется метод 'filter_from'; для аргумента to
выполняется другой метод filter_to
.
Я не могу использовать prefetch_related
дважды, я получаю исключение ValueError: 'absences' lookup was already seen with a different queryset. You may need to adjust the ordering of your lookups.
.
Я пробовал использовать to_attr
, но похоже, что я не могу получить доступ к нему в неоцененном наборе запросов.
Я знаю, что могу найти первый определенный Prefetch
в _prefetch_related_lookups
атрибуте queryset, но есть ли способ применить к нему дополнительный фильтр или заменить его другим Prefetch
объектом, чтобы в итоге получился запрос, подобный:
queryset.prefetch_related(
Prefetch(
'absences',
Absence.objects.filter(
Q(Q(start_date__gte=from_) | Q(start_date__lte=from_, end_date__gte=from_))
& Q(Q(end_date__lte=to) | Q(start_date__lte=to, end_date__gte=to))
),
)
)
django-filter
, похоже, имеет свой собственный встроенный фильтр для запросов диапазона:
Дополнительная информация здесь и здесь
Так что, вероятно, проще использовать это вместо:
def filter_date_range(self, queryset, name, value):
if self.lookup_expr = "range":
#return queryset with specific prefetch
if self.lookup_expr = "lte":
#return queryset with specific prefetch
if self.lookup_expr = "gte":
#return queryset with specific prefetch
Я не тестировал это, и вам, возможно, придется повозиться с распаковкой value, но это должно помочь вам пройти большую часть пути.