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, но это должно помочь вам пройти большую часть пути.

Вернуться на верх