Название может немного сбивать с толку. Было бы неплохо иметь общий метод filter
который вызывается каждый раз, когда извлекается набор запросов и применяется фильтрация для полей. Да, я могу переопределить свойство qs
, сначала получить отфильтрованный набор запросов, а затем применить предварительную выборку. Но _не ясно, может ли это иметь какие-либо побочные эффекты, и данные, переданные в фильтр, безопасны для доступа ._
Вот мой вариант использования.
Я фильтрую набор запросов по отношению M2M (как в queryset.filter(items__field="foo")
). Однако я хочу, чтобы предварительно загруженные элементы также фильтровались по этому полю.
Другими словами, мой фильтр выглядит так:
# In my filterset
def filter_items_by_field(self, queryset, name, value):
queryset = queryset.all().filter(
items__field=value,
)
prefetched_items = Prefetch(
'items',
Item.objects..filter(
field=value,
),
)
return queryset.prefetch_related(prefetched_items)
# In my vieset
def get_queryset(self):
queryset = super().get_queryset()
# This should be applied everytime
prefetched_items = Prefetch(
'items',
Item.objects..filter(
another_field=some_value,
),
)
return queryset.prefetch_related(prefetched_items)
Такой единственный фильтр работает должным образом. Однако у меня может быть несколько фильтров filter_items_by_*
+ мне нужно применить ту же фильтрацию для набора запросов из предварительной выборки для всех фильтров. И несколько фильтров не проблема, потому что в моем случае они исключают друг друга. Но фильтр + универсальный вызовет исключение.
Дело в том, что я могу применить только одну предварительную выборку для одного и того же поиска. Итак, в моем случае я просто получу исключение.
У меня есть временное решение этой проблемы: все filter_items_by_*
выполняют обязательную фильтрацию при предварительной выборке.
И в filter_queryset
(после применения фильтрации), если items
не предварительно выбираются (что плохо, поскольку я проверяю атрибуты, защищенные набором запросов), я применяю предварительную выборку с обязательной фильтрацией. Но должен быть лучший способ справиться с подобными ситуациями.
Надеюсь, мой пример достаточно ясен.
Привет @ Alexx-G. Я думаю, что это возможно при некоторой координации между набором фильтров и соответствующими классами фильтров. По сути, фильтр добавит экземпляр Prefetch
к своему набору фильтров parent
. FilterSet.qs
тогда просто вызовет prefetch_related
когда это необходимо.
Тем не менее, каков вариант использования этого? Я бы сказал, что есть более подходящие места для обработки предварительной выборки.
Я думаю, что это возможно при некоторой координации между набором фильтров и соответствующими классами фильтров.
Я уже решил свою проблему, переопределив свойство qs
, но опять же, это неочевидно и не задокументировано. Другими словами, я не могу быть уверен, что некоторые внутренние изменения в базовом классе не повлияют на мой набор фильтров (хорошо, я закрепил версию, но в любом случае ...)
Тем не менее, каков вариант использования этого? Я бы сказал, что есть более подходящие места для обработки предварительной выборки.
Я полагаю, что описал выше свой вариант использования. Нет смысла разделять фильтрацию и предварительную выборку, потому что предварительная выборка зависит от фильтров. И если я их разделю, мне придется дублировать одинаковые проверки для фильтров.
Другими словами, если бы было описание публичного контракта свойства qs или задокументированный хук для него, это улучшило бы настройку набора фильтров.
Привет @ Alexx-G. Выпуск 2.0 все еще остается выходом, но в API будут внесены некоторые изменения, специально посвященные этому вопросу. Суммируя,
.filter_queryset()
. Атрибут .qs
основном отвечает за вызов .filter_queryset()
и кеширование его результата.self.form.cleaned_data
.Большое спасибо за ваши усилия!
Самый полезный комментарий
Привет @ Alexx-G. Выпуск 2.0 все еще остается выходом, но в API будут внесены некоторые изменения, специально посвященные этому вопросу. Суммируя,
.filter_queryset()
. Атрибут.qs
основном отвечает за вызов.filter_queryset()
и кеширование его результата.self.form.cleaned_data
.