DRF/django-filter программный вызов класса Filterset без использования Viewset/request
Я создаю функциональность, позволяющую пользователям сохранять набор фильтров из компонента построения запросов на моем фронтенде Angular. Я также использую пакет django-filter для обеспечения большей части возможностей поиска в моем приложении.
Допустим, у меня есть модель MyModel (с полем FK 'category') и набор ViewsSet, в котором я указал filterset_class = MyModeFilter
from django_filters import rest_framework as filters
class MyModelFilter(filters.Filterset):
category_in = filters.BaseCSVFilter(field_name='category', method='filter_category_in')
def filter_category_in(self, qs, name, value):
# filters happen
return qs
Это хорошо работает, когда мой фронтенд вызывает https://api-url/mymodel/?category_in=1,2,3
Теперь я хочу программно вызвать этот фильтр из приложения django.
Пользовательский случай: Мне нужно запрограммировать задание CRON для создания и отправки по электронной почте отчета в формате excel на основе набора запросов, который будет соответствовать параметру фильтра ?category_in=1,2,3. Это очень просто с помощью ViewSets при использовании DRF-запросов, но я не знаю, с чего начать поиск того, как использовать этот фильтр без использования запросов.
Фильтр сохраняется в JSONField (saved_query_statement) в модели под названием SavedQuery. Это будет выглядеть примерно так:
sqs = {
'name': 'category',
'lookup_expr': 'in',
'value': '1,2,3'
}
Итак, функция задания CRON при выполнении выполнит вызов SavedQuery.objects.get() и получит запрос, который имеет к нему отношение, затем ей нужно получить набор запросов MyModel, где (category__in=[1,2,3]). Как я могу получить JSON/строковое представление этого фильтра, не делая много чего-то подобного (у меня более 50 возможных фильтров и еще больше впереди...)
qs = MyModel.objects.all()
if 'category' in sqs.get('name'):
if 'in' in sqs.get('lookup_expr'):
ids = [int(v) for v in sqs.get('value').split(',')]
qs.filter(category__in=ids)
...
TLDR, я хотел бы использовать класс django-filter Filterset без прохождения через Viewset... есть идеи?
Вы можете использовать **, чтобы распаковать ключевые/значимые аргументы фильтра и сделать его в некоторой степени общим:
qs.filter(**kv)
В вашем случае это будет выглядеть примерно так:
qs = MyModel.objects.all()
# Somehow create kwargs from your JSON
filter_key = sqs.get('name')
lookup_expr = sqs.get('lookup_expr')
if lookup_expr:
filter_key += '__{}'.format(lookup_expr)
filter_value = parse_filter_value(sqs.get('value')) # This is a bit tricky
kwargs = {filter_key: filter_value}
# Now filter
qs.filter(**kwargs)
Вам придется выяснить, как реализовать parse_filter_value, но это не основная проблема, и в целом этот подход может быть очень удобным.