динамически генерировать фильтры в sqlalchemy со смесью и / или и не / как - PullRequest
0 голосов
/ 01 ноября 2018

У меня есть словарь, который определяет некоторую конфигурацию, где я не буду знать заранее точные фильтры, которые мой пользователь хочет построить

указано так:

{"col1": ["val1", "~val2"],  
 "col2": ["val3", "val4"], 
 "col3": ["val5", "val6", "~val7", "~val8"]}

где это должно переводиться в:

WHERE ( 
  col1 LIKE val1 
  AND 
  col1 NOT LIKE val2 
) 
OR ( 
  col2 LIKE val3 
    OR 
  col2 LIKE val4 
) 
OR 
( 
  ( 
    col3 LIKE val5 
    OR 
    col3 LIKE val6 
  ) 
  AND 
    col3 NOT LIKE val7 
  AND 
    col3 NOT LIKE val8 
)

критерием на верхнем уровне всегда является 'или' (вы либо соответствуете одному столбцу, как указано, либо соответствует другому столбцу, как указано), никогда 'и'

однако в списке вы можете сопоставить любой из положительных критериев, но ни один из отрицательных критериев (критериями здесь являются строки)

Моя попытка построить это динамически выглядит примерно так:

def make_filter(definition):
    f = {}
    for variable in definition:
        v = getattr(Target, variable)
        f[v] = []
        for col in definition[variable]:
            f[v].append(col)
    return f


def make_query(def_dict):
    q = destination.query(Target)
    filter = make_filter(def_dict)
    for col in filter:
        for val in filter[col]:
            if '~' in val:
                q = q.filter(sa.not_(col.like(val)))
            else:
                q = q.filter(col.like(val))
    for record in q.all():
        # do stuff

но очевидно, что это объединяет все критерии с помощью 'и' - я не вижу способа динамически комбинировать с 'или' для неизвестного числа условий ...

Я могу изменить спецификации, если это лучший способ решить эту проблему, при условии, что один и тот же набор критериев может быть задан однозначно. Я специально не требовал от пользователей ставить все положительные критерии перед всеми отрицательными, но это разумно, например.

1 Ответ

0 голосов
/ 02 ноября 2018

Не уверен, следует ли это пометить как обман, учитывая приведенные выше связанные ответы - возможно, достаточно разные, чтобы все еще быть полезными, так что на всякий случай я публикую свое решение на основе связанных ответов для полноты:

def make_filter(definition):
    f = {}
    for variable in definition:
        v = getattr(Target, variable)
        f[v] = {"like": [], "not": []}
        for col in definition[variable]:
            if "~" in col:
                f[v]["not"].append(re.sub('~', '', col))
            else:
                f[v]["like"].append(col)
    return f

def make_query(def_dict):
    q = destination.query(Target)
    filter = make_filter(def_dict)
    for item in filter:
        like_this = filter[item]["like"]
        not_this = filter[item]["not"]
        if len(not_this):
            for nt in not_this:
                q = q.filter(sa.not_(item.like(nt)))
        clauses = []
        for lt in like_this:
            clauses.append(item.like(lt))
        q = q.filter(sa.or_(*clauses))
        for record in q.all():
            # do stuff
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...