Динамическое построение запросов Django на основе фильтра kendo UI
Я пытаюсь сделать запрос к базе данных на основе пользовательского фильтра. Я получил следующие данные из грида kendo UI.
{
"filter":{
"filters":[
{
"logic":"or",
"filters":[
{
"field":"aging",
"operator":"eq",
"value":24
},
{
"field":"aging",
"operator":"eq",
"value":13
}
]
},
{
"logic":"or",
"filters":[
{
"field":"follow_up_name",
"operator":"eq",
"value":"Call Insurance Provider"
}
]
},
{
"logic":"or",
"filters":[
{
"field":"patient_name",
"operator":"eq",
"value":"kartik"
}
]
},
{
"logic":"and",
"filters":[
{
"field":"created_date",
"operator":"eq",
"value":"2022-01-09T18:30:00.000Z"
},
{
"field":"created_date",
"operator":"gte",
"value":"2022-01-04T18:30:00.000Z"
}
]
},
{
"logic":"or",
"filters":[
{
"field":"follow_up_status",
"operator":"eq",
"value":"Open"
}
]
},
{
"logic":"or",
"filters":[
{
"field":"role_name",
"operator":"eq",
"value":"Pharmacist"
}
]
},
{
"logic":"or",
"filters":[
{
"field":"last_response",
"operator":"eq",
"value":"To-Patient"
}
]
}
],
"logic":"and"
},
"skip":0,
"take":10
}
На основе вышеприведенных данных мне нужно как 'and' & 'or' условие, чтобы построить запрос динамически. и передать его в базу данных. также фильтр может содержать несколько списков. также хочу сделать эти классы общими, которые могут принимать только UI аргументы построить запрос и вернуть. пожалуйста, предоставьте решение для них.
Q-объекты - ваш друг здесь.
from django.db.models import QuerySet, Q
from functools import reduce
def filter_rec(data: dict) -> Q:
logic = data.get('logic', None)
# Leaf node
if logic is None:
field = data['field']
operator = data['operator']
value = data['value']
return Q(**{f'{field}__{operator}': value})
# Node with children
children = list()
for item in data['filters']:
child = filter_rec(child)
children.append(child)
if logic == 'and':
query = reduce(lambda x, y: x & y, children)
elif logic == 'or':
query = reduce(lambda x, y: x | y, children)
return query
def kendo_filter(data: dict, qs: QuerySet) -> QuerySet:
query = filter_rec(data['filter'])
skip = data['skip']
take = data['take']
filtered_qs = qs.filter(query)
paginated_qs = filtered_qs[skip:skip+take]
return paginated_qs
Общая суть такова. Вы сами должны проверять данные и выполнять обработку ошибок.
filter_rec(data['filter'])
возвращает следующее для данных вашего примера:
<Q: (
AND:
(OR: ('aging__eq', 24), ('aging__eq', 13)),
('follow_up_name__eq', 'Call Insurance Provider'),
('patient_name__eq', 'kartik'),
('created_date__eq', '2022-01-09T18:30:00.000Z'),
('created_date__gte', '2022-01-04T18:30:00.000Z'),
('follow_up_status__eq', 'Open'),
('role_name__eq', 'Pharmacist'),
('last_response__eq', 'To-Patient')
)>
Поскольку одиночные OR будут подняты как есть, а вложенные AND будут показаны как один уровень, похоже, что это сработало.