Custom FiltersΒΆ

The basic requirements for a custom filter are to supply the operators, the query modifier for applying the filter, and a search expression for single-search. A few examples are here.

Simple custom filter:

class ActivityStatusFilter(FilterBase):
    # operators are declared as Operator(<key>, <display>, <field-type>)
    operators = (
        Operator('all', 'all', None),
        Operator('pend', 'pending', None),
        Operator('comp', 'completed', None),
    )

    def get_search_expr(self):
        status_col = sa.sql.case(
            [(Activity.flag_completed == sa.true(), 'completed')],
            else_='pending'
        )
        # Could use ilike here, depending on the target DBMS
        return lambda value: status_col.like('%{}%'.format(value))

    def apply(self, query):
        if self.op == 'all':
            return query
        if self.op == 'comp':
            return query.filter(Activity.flag_completed == sa.true())
        if self.op == 'pend':
            return query.filter(Activity.flag_completed == sa.false())
        return super().apply(self, query)

Options filter for INT foreign key lookup:

class VendorFilter(OptionsIntFilterBase):
    def options_from(self):
        # Expected to return a list of tuples (id, label).
        # In this case, we're retrieving options from the database.
        return db.session.query(Vendor.id, Vendor.label).select_from(
            Vendor
        ).filter(
            Vendor.active_flag == sa.true()
        ).order_by(
            Vendor.label
        ).all()

Aggregate filters, i.e. those using the HAVING clause instead of WHERE, must be marked with the is_aggregate flag. Single-search via expressions will only address aggregate filters if all search filters are aggregate. Using an aggregate filter will require a GROUP BY clause be set.

class AggregateTextFilter(TextFilter):

is_aggregate = True