Dynamically constructing filters in SQLAlchemy

后端 未结 5 951
挽巷
挽巷 2020-12-04 09:11

I am looking for a way to dynamically construct filters using SQLAlchemy. That is, given the column, the operator name and the comparing value, construct the corresponding f

5条回答
  •  难免孤独
    2020-12-04 10:09

    improving https://stackoverflow.com/a/14876320/12562701, you can get more complex filters

    Dict to map op

    dict_filtros_op = {
        '==':'eq',
        '!=':'ne',
        '>':'gt',
        '<':'lt',
        '>=':'ge',
        '<=':'le',
        'like':'like',
        'ilike':'ilike',
        'in':'in'
    }
    

    Class dao:

    class BaseDao():
        @classmethod
        @init_db_connection
        def create_query_select(cls, model, filters=None, columns=None):
            return cls.db_session.query(*cls.create_query_columns(model=model, columns=columns))\
                .filter(*cls.create_query_filter(model=model, filters=filters))
    
        @classmethod
        def create_query_filter(cls, model, filters):
            '''
            return sqlalchemy filter list
            Args:
                model:sqlalchemy  model (classe das tabelas)
                filters: filter dict
                         ex:
                            filters = {
                                'or_1':{
                                    'and_1':[('id', '>', 5),('id', '!=', 3)],
                                    'and_2':[('fase', '==', 'arquivado')]
                                },
                                'and':[('test', '==', 'test')]
                            }
            Returns:
                filt: sqlalchemy filter list
             '''
            if not filters:
                return []
    
            filt = []
            for condition in filters:
                if type(filters[condition]) == dict:
                    if 'and' in condition:
                        filt.append(and_(*cls.create_query_filter(model, filters[condition])))
                    elif 'or' in condition:
                        filt.append(or_(*cls.create_query_filter(model, filters[condition])))
                    else:
                        raise Exception('Invalid filter condition: %s' % condition)
                    continue
                filt_aux = []
                for t_filter in filters[condition]:
                    try:
                        column_name, op, value = t_filter
                    except ValueError:
                        raise Exception('Invalid filter: %s' % t_filter)
                    if not op in dict_filtros_op:
                        raise Exception('Invalid filter operation: %s' % op)
                    column = getattr(model, column_name, None)
                    if not column:
                        raise Exception('Invalid filter column: %s' % column_name)
                    if dict_filtros_op[op] == 'in':
                        filt.append(column.in_(value))
                    else:
                        try:
                            attr = list(filter(lambda e: hasattr(column, e % dict_filtros_op[op]), ['%s', '%s_', '__%s__']))[0] % dict_filtros_op[op]
                        except IndexError:
                            raise Exception('Invalid filter operator: %s' % dict_filtros_op[op])
                        if value == 'null':
                            value = None
                        filt_aux.append(getattr(column, attr)(value))
                if 'and' in condition:
                    filt.append(and_(*filt_aux))
                elif 'or' in condition:
                    filt.append(or_(*filt_aux))
                else:
                    raise Exception('Invalid filter condition: %s' % condition)
            return filt
    
        @classmethod
        def create_query_columns(cls, model, columns):
            '''
            Return a list of attributes (columns) from the class model
            Args:
                model: sqlalchemy model
                columns: string list
                         ex: ['id', 'cnj']
            Returns:
                cols: list of attributes from the class model
             '''
            if not columns:
                return [model]
    
            cols = []
            for column in columns:
                attr = getattr(model, column, None)
                if not attr:
                    raise Exception('Invalid column name %s' % column)
                cols.append(attr)
            return cols
    

提交回复
热议问题