Add MasterView.use_byte_string_filters flag for encoding search values

This commit is contained in:
Lance Edgar 2018-05-24 15:09:00 -05:00
parent 54bfafdbfe
commit ccdf821583
3 changed files with 30 additions and 12 deletions

View file

@ -69,7 +69,7 @@ class Grid(object):
def __init__(self, key, data, columns=None, width='auto', request=None, mobile=False, model_class=None,
enums={}, labels={}, renderers={}, extra_row_class=None, linked_columns=[], url='#',
joiners={}, filterable=False, filters={},
joiners={}, filterable=False, filters={}, use_byte_string_filters=False,
sortable=False, sorters={}, default_sortkey=None, default_sortdir='asc',
pageable=False, default_pagesize=20, default_page=1,
checkboxes=False, checked=None, main_actions=[], more_actions=[],
@ -94,6 +94,7 @@ class Grid(object):
self.joiners = joiners or {}
self.filterable = filterable
self.use_byte_string_filters = use_byte_string_filters
self.filters = self.make_filters(filters)
self.sortable = sortable
@ -426,6 +427,7 @@ class Grid(object):
factory = gridfilters.AlchemyDateTimeFilter
elif isinstance(column.type, GPCType):
factory = gridfilters.AlchemyGPCFilter
kwargs.setdefault('encode_values', self.use_byte_string_filters)
return factory(key, column=column, config=self.request.rattail_config, **kwargs)
def iter_filters(self):

View file

@ -140,7 +140,8 @@ class GridFilter(object):
value_renderer_factory = DefaultValueRenderer
def __init__(self, key, label=None, verbs=None, value_enum=None, value_renderer=None,
default_active=False, default_verb=None, default_value=None, **kwargs):
default_active=False, default_verb=None, default_value=None,
encode_values=False, value_encoding='utf-8', **kwargs):
self.key = key
self.label = label or prettify(key)
self.verbs = verbs or self.get_default_verbs()
@ -153,6 +154,10 @@ class GridFilter(object):
self.default_active = default_active
self.default_verb = default_verb
self.default_value = default_value
self.encode_values = encode_values
self.value_encoding = value_encoding
for key, value in kwargs.items():
setattr(self, key, value)
@ -196,6 +201,11 @@ class GridFilter(object):
def get_value(self, value=UNSPECIFIED):
return value if value is not UNSPECIFIED else self.value
def encode_value(self, value):
if self.encode_values and isinstance(value, six.string_types):
return value.encode('utf-8')
return value
def filter_is_any(self, data, value):
"""
Special no-op filter which does no actual filtering. Useful in some
@ -241,7 +251,7 @@ class AlchemyGridFilter(GridFilter):
"""
if value is None or value == '':
return query
return query.filter(self.column == value)
return query.filter(self.column == self.encode_value(value))
def filter_not_equal(self, query, value):
"""
@ -254,7 +264,7 @@ class AlchemyGridFilter(GridFilter):
# include things which are nothing at all, in our result set.
return query.filter(sa.or_(
self.column == None,
self.column != value,
self.column != self.encode_value(value),
))
def filter_is_null(self, query, value):
@ -277,7 +287,7 @@ class AlchemyGridFilter(GridFilter):
"""
if value is None or value == '':
return query
return query.filter(self.column > value)
return query.filter(self.column > self.encode_value(value))
def filter_greater_equal(self, query, value):
"""
@ -285,7 +295,7 @@ class AlchemyGridFilter(GridFilter):
"""
if value is None or value == '':
return query
return query.filter(self.column >= value)
return query.filter(self.column >= self.encode_value(value))
def filter_less_than(self, query, value):
"""
@ -293,7 +303,7 @@ class AlchemyGridFilter(GridFilter):
"""
if value is None or value == '':
return query
return query.filter(self.column < value)
return query.filter(self.column < self.encode_value(value))
def filter_less_equal(self, query, value):
"""
@ -301,7 +311,7 @@ class AlchemyGridFilter(GridFilter):
"""
if value is None or value == '':
return query
return query.filter(self.column <= value)
return query.filter(self.column <= self.encode_value(value))
class AlchemyStringFilter(AlchemyGridFilter):
@ -323,7 +333,8 @@ class AlchemyStringFilter(AlchemyGridFilter):
if value is None or value == '':
return query
return query.filter(sa.and_(
*[self.column.ilike('%{}%'.format(v)) for v in value.split()]))
*[self.column.ilike(self.encode_value('%{}%'.format(v)))
for v in value.split()]))
def filter_does_not_contain(self, query, value):
"""
@ -337,7 +348,8 @@ class AlchemyStringFilter(AlchemyGridFilter):
return query.filter(sa.or_(
self.column == None,
sa.and_(
*[~self.column.ilike('%{0}%'.format(v)) for v in value.split()]),
*[~self.column.ilike(self.encode_value('%{}%'.format(v)))
for v in value.split()]),
))
@ -350,13 +362,13 @@ class AlchemyEmptyStringFilter(AlchemyStringFilter):
return query.filter(
sa.or_(
self.column == None,
sa.func.trim(self.column) == ''))
sa.func.trim(self.column) == self.encode_value('')))
def filter_is_not_null(self, query, value):
return query.filter(
sa.and_(
self.column != None,
sa.func.trim(self.column) != ''))
sa.func.trim(self.column) != self.encode_value('')))
class AlchemyByteStringFilter(AlchemyStringFilter):

View file

@ -67,6 +67,9 @@ class MasterView(View):
pageable = True
checkboxes = False
# set to True in order to encode search values as utf-8
use_byte_string_filters = False
listable = True
sortable = True
results_downloadable_csv = False
@ -276,6 +279,7 @@ class MasterView(View):
'model_class': getattr(self, 'model_class', None),
'width': 'full',
'filterable': self.filterable,
'use_byte_string_filters': self.use_byte_string_filters,
'sortable': self.sortable,
'pageable': self.pageable,
'extra_row_class': self.grid_extra_class,