Разбор строки, содержащей логику, для создания объектов Django Q - PullRequest
1 голос
/ 11 марта 2019

Я добавляю функции отчетности в веб-приложение django, которое включает возможность цепочки фильтров при определении логики, например, (1 OR 2 OR 4) AND 4

В настоящее время используется структура данных:

`{'logic': u'(1 OR 2 OR 4) AND 4',
 'query': [{u'field': u'first_name',
            u'id': 1,
            u'opertor': u'icontains',
            u'value': u'A'},
           {u'field': u'first_name',
            u'id': 2,
            u'opertor': u'icontains',
            u'value': u'b'},
           {u'field': u'show_tag',
            u'id': 3,
            u'opertor': u'includes',
            u'value': u'1955'},
           {u'field': u'organisation__organisation_name',
            u'id': 4,
            u'opertor': u'icontains',
            u'value': u'a'}]}`

Чтобы преобразовать логику из строки во вложенный список, я использую следующее:

from pyparsing import nestedExpr

def parse_brackets(val):
    val = '(' + val + ')'
    parsed = nestedExpr('(',')').parseString(val).asList()
    return parsed

Для приведенного выше примера это возвращает [[['1', 'OR', '2', 'OR', '4'], 'AND', '4']]

Мне сложно разобрать эту структуру данных, чтобы она могла быть преобразована в Q() цепочечные объекты. Я пытался использовать BFS для поиска по структуре, но не могу правильно сопоставить AND / OR с окружающими критериями

Любой совет с благодарностью!

1 Ответ

0 голосов
/ 11 марта 2019

Вы можете использовать функцию, которая ищет оператор OR или AND в данном списке токенов и нарезает список по индексу, где он найден, и рекурсивно обрабатывает два нарезанных списка с обеих сторон. оператора и примените соответствующие методы (Q.__or__ и Q.__and__) к возвращаемым Q объектам. Если оператор не найден, вернуть новый Q объект со ссылочными данными запроса:

def q(query, tokens):
    if isinstance(tokens, list):
        for operator in ('OR', 'AND'): # OR first since it has a lower precedence than AND
            try:
                index = tokens.index(operator)
                break
            except ValueError:
                pass
        else:
            return q(query, tokens[0])
        return (Q.__or__ if operator == 'OR' else Q.__and__)(
            q(query, tokens[:index]), q(query, tokens[index + 1:]))
    else:
        d = query[int(tokens)]
        return Q(**{'__'.join((d['field'], d['operator'])): d['value']})

так что дано:

tokens = [[['1', 'OR', '2', 'OR', '4'], 'AND', '4']]
query = {'logic': u'(1 OR 2 OR 4) AND 4',
         'query': [{u'field': u'first_name',
                    u'id': 1,
                    u'opertor': u'icontains',
                    u'value': u'A'},
                   {u'field': u'first_name',
                    u'id': 2,
                    u'opertor': u'icontains',
                    u'value': u'b'},
                   {u'field': u'show_tag',
                    u'id': 3,
                    u'opertor': u'includes',
                    u'value': u'1955'},
                   {u'field': u'organisation__organisation_name',
                    u'id': 4,
                    u'opertor': u'icontains',
                    u'value': u'a'}]}
query = {q['id']: q for q in query['query']} # transform the above to a dict indexed by id

q(query, tokens) должен вернуть Q нужный вам объект.

Отказ от ответственности: я не проверял этот код.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...