diff --git a/tailbone/grids/search.py b/tailbone/grids/search.py index c3051f31..de519628 100644 --- a/tailbone/grids/search.py +++ b/tailbone/grids/search.py @@ -26,6 +26,8 @@ Grid Search Filters from __future__ import unicode_literals +import re + from sqlalchemy import func, or_ from webhelpers.html import tags @@ -181,6 +183,23 @@ def filter_ilike(field): return {'lk': ilike, 'nl': not_ilike} +def filter_int(field): + """ + Returns a filter map entry for an integer field. This provides exact + matching but also strips out non-numeric characters to avoid type errors. + """ + + def filter_is(q, v): + v = re.sub(r'\D', '', v or '') + return q.filter(field == int(v)) if v else q + + def filter_nt(q, v): + v = re.sub(r'\D', '', v or '') + return q.filter(field != int(v)) if v else q + + return {'is': filter_is, 'nt': filter_nt} + + def filter_soundex(field): """ Returns a filter map entry which leverages the `soundex()` SQL function. @@ -256,7 +275,7 @@ def get_filter_config(prefix, request, filter_map, **kwargs): return config -def get_filter_map(cls, exact=[], ilike=[], **kwargs): +def get_filter_map(cls, exact=[], ilike=[], int_=[], **kwargs): """ Convenience function which returns a "filter map" for ``cls``. @@ -266,6 +285,9 @@ def get_filter_map(cls, exact=[], ilike=[], **kwargs): ``ilike``, if provided, should be a list of field names for which "ILIKE" filtering is to be allowed. + ``int_``, if provided, should be a list of field names for which "integer" + filtering is to be allowed. + Any remaining ``kwargs`` are assumed to be filter map entries themselves, and are added directly to the map. """ @@ -275,6 +297,8 @@ def get_filter_map(cls, exact=[], ilike=[], **kwargs): fmap[name] = filter_exact(getattr(cls, name)) for name in ilike: fmap[name] = filter_ilike(getattr(cls, name)) + for name in int_: + fmap[name] = filter_int(getattr(cls, name)) fmap.update(kwargs) return fmap diff --git a/tailbone/views/grids/alchemy.py b/tailbone/views/grids/alchemy.py index 3cfc8a89..922683a5 100644 --- a/tailbone/views/grids/alchemy.py +++ b/tailbone/views/grids/alchemy.py @@ -138,6 +138,9 @@ class SearchableAlchemyGridView(PagedAlchemyGridView): def filter_ilike(self, field): return grids.search.filter_ilike(field) + def filter_int(self, field): + return grids.search.filter_int(field) + def filter_soundex(self, field): return grids.search.filter_soundex(field)