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, но это не основная проблема, и в целом этот подход может быть очень удобным.

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