import typing from flask import request from sqlalchemy.orm import Query from sqlalchemy.sql.schema import Column class QueryFiltering: """ Filtering query by provided filter string. Filter string example: `field1:field1_filter_value,fieldN:fieldN_filter_value` """ def __init__( self, query: Query, allowed_fields: typing.Optional[typing.Iterable[Column]] = None, filter_string: str = "", fields_mapping: typing.Optional[typing.Mapping] = None, ): self._query = query self._allowed_fields = allowed_fields or [] self._fields_mapping = fields_mapping or {} self._filter_string = filter_string or request.args.get( "filter", type=str, default="" ) self._update_query() @property def query(self): return self._query @property def filter_string(self): return self._filter_string def _update_query(self): filter_params = self._parse_filter_string() for key, val in filter_params.items(): column = self._get_column_by_name(key) if column is None: continue self._query = self._query.filter(column.in_(val)) def _get_column_by_name(self, name: str): for c in self._allowed_fields: if c.key == name: return c def _parse_filter_string(self): allowed_params = [f.name for f in self._allowed_fields] filter_params = {} for param_string in self.filter_string.split(";"): try: key, value = param_string.split(":") except Exception: # nosec continue key = key.strip() key = self._fields_mapping.get(key) or key if key not in allowed_params: continue filter_params[key] = value.strip().split(",") return filter_params