From 1ae8ea0f03cfb9f25b1e25f3194c456c3649789c Mon Sep 17 00:00:00 2001 From: Simon Willison Date: Tue, 24 Oct 2017 17:06:23 -0700 Subject: [PATCH] Started implementing ?name__contains=X filters So far we support __contains=, __startswith=, __endswith= and __exact= Refs #23 --- app.py | 41 ++++++++++++++++++++++++++++++++++++++++- test_helpers.py | 19 +++++++++++++++++++ 2 files changed, 59 insertions(+), 1 deletion(-) diff --git a/app.py b/app.py index dc0711c1..0eb2dd98 100644 --- a/app.py +++ b/app.py @@ -183,7 +183,15 @@ class TableView(BaseView): def data(self, request, name, hash, table): conn = get_conn(name) - rows = conn.execute('select * from {} limit 20'.format(table)) + if request.args: + where_clause, params = build_where_clause(request.args) + sql = 'select * from "{}" where {} limit 50'.format( + table, where_clause + ) + else: + sql = 'select * from "{}" limit 50'.format(table) + params = [] + rows = conn.execute(sql, params) columns = [r[0] for r in rows.description] pks = pks_for_table(conn, table) rows = list(rows) @@ -294,6 +302,37 @@ def path_from_row_pks(row, pks): return ','.join(bits) +def build_where_clause(args): + sql_bits = [] + for key, values in args.items(): + if '__' in key: + column, lookup = key.rsplit('__', 1) + else: + column = key + lookup = 'exact' + template = { + 'exact': '"{}" = ?', + 'contains': '"{}" like ?', + 'endswith': '"{}" like ?', + 'startswith': '"{}" like ?', + }[lookup] + value = values[0] + value_convert = { + 'exact': lambda s: s, + 'contains': lambda s: '%{}%'.format(s), + 'endswith': lambda s: '%{}'.format(s), + 'startswith': lambda s: '{}%'.format(s), + }[lookup] + converted = value_convert(value) + sql_bits.append( + (template.format(column), converted) + ) + sql_bits.sort(key=lambda p: p[0]) + where_clause = ' and '.join(p[0] for p in sql_bits) + params = [p[1] for p in sql_bits] + return where_clause, params + + class CustomJSONEncoder(json.JSONEncoder): def default(self, obj): if isinstance(obj, sqlite3.Row): diff --git a/test_helpers.py b/test_helpers.py index 42e90166..75779d71 100644 --- a/test_helpers.py +++ b/test_helpers.py @@ -64,3 +64,22 @@ def test_custom_json_encoder(obj, expected): sort_keys=True ) assert expected == actual + + +@pytest.mark.parametrize('args,expected_where,expected_params', [ + ({ + 'name_english__contains': ['foo'], + }, '"name_english" like ?', ['%foo%']), + ({ + 'foo': ['bar'], + 'bar__contains': ['baz'], + }, '"bar" like ? and "foo" = ?', ['%baz%', 'bar']), + ({ + 'foo__startswith': ['bar'], + 'bar__endswith': ['baz'], + }, '"bar" like ? and "foo" like ?', ['%baz', 'bar%']), +]) +def test_build_where(args, expected_where, expected_params): + actual_where, actual_params = app.build_where_clause(args) + assert expected_where == actual_where + assert expected_params == actual_params