Support multiple filters of the same type

Closes #288
This commit is contained in:
Simon Willison 2019-04-15 16:44:17 -07:00
commit 9c77e6e355
3 changed files with 63 additions and 43 deletions

View file

@ -219,13 +219,14 @@ class TableView(RowTableShared):
# it can still be queried using ?_col__exact=blah
special_args = {}
special_args_lists = {}
other_args = {}
other_args = []
for key, value in args.items():
if key.startswith("_") and "__" not in key:
special_args[key] = value[0]
special_args_lists[key] = value
else:
other_args[key] = value[0]
for v in value:
other_args.append((key, v))
# Handle ?_filter_column and redirect, if present
redirect_params = filters_should_redirect(special_args)
@ -253,7 +254,7 @@ class TableView(RowTableShared):
table_metadata = self.ds.table_metadata(database, table)
units = table_metadata.get("units", {})
filters = Filters(sorted(other_args.items()), units, ureg)
filters = Filters(sorted(other_args), units, ureg)
where_clauses, params = filters.build_where_clauses(table)
extra_wheres_for_ui = []
@ -521,7 +522,7 @@ class TableView(RowTableShared):
database, table, column, values
))
for row in facet_rows:
selected = str(other_args.get(column)) == str(row["value"])
selected = (column, str(row["value"])) in other_args
if selected:
toggle_path = path_with_removed_args(
request, {column: str(row["value"])}

View file

@ -903,6 +903,16 @@ def test_table_filter_queries(app_client, path, expected_rows):
assert expected_rows == response.json['rows']
def test_table_filter_queries_multiple_of_same_type(app_client):
response = app_client.get(
"/fixtures/simple_primary_key.json?content__not=world&content__not=hello"
)
assert [
['3', ''],
['4', 'RENDER_CELL_DEMO']
] == response.json['rows']
@pytest.mark.skipif(
not detect_json1(),
reason="Requires the SQLite json1 module"

View file

@ -4,88 +4,97 @@ import pytest
@pytest.mark.parametrize('args,expected_where,expected_params', [
(
{
'name_english__contains': 'foo',
},
(
('name_english__contains', 'foo'),
),
['"name_english" like :p0'],
['%foo%']
),
(
{
'foo': 'bar',
'bar__contains': 'baz',
},
(
('foo', 'bar'),
('bar__contains', 'baz'),
),
['"bar" like :p0', '"foo" = :p1'],
['%baz%', 'bar']
),
(
{
'foo__startswith': 'bar',
'bar__endswith': 'baz',
},
(
('foo__startswith', 'bar'),
('bar__endswith', 'baz'),
),
['"bar" like :p0', '"foo" like :p1'],
['%baz', 'bar%']
),
(
{
'foo__lt': '1',
'bar__gt': '2',
'baz__gte': '3',
'bax__lte': '4',
},
(
('foo__lt', '1'),
('bar__gt', '2'),
('baz__gte', '3'),
('bax__lte', '4'),
),
['"bar" > :p0', '"bax" <= :p1', '"baz" >= :p2', '"foo" < :p3'],
[2, 4, 3, 1]
),
(
{
'foo__like': '2%2',
'zax__glob': '3*',
},
(
('foo__like', '2%2'),
('zax__glob', '3*'),
),
['"foo" like :p0', '"zax" glob :p1'],
['2%2', '3*']
),
# Multiple like arguments:
(
{
'foo__isnull': '1',
'baz__isnull': '1',
'bar__gt': '10'
},
(
('foo__like', '2%2'),
('foo__like', '3%3'),
),
['"foo" like :p0', '"foo" like :p1'],
['2%2', '3%3']
),
(
(
('foo__isnull', '1'),
('baz__isnull', '1'),
('bar__gt', '10'),
),
['"bar" > :p0', '"baz" is null', '"foo" is null'],
[10]
),
(
{
'foo__in': '1,2,3',
},
(
('foo__in', '1,2,3'),
),
['foo in (:p0, :p1, :p2)'],
["1", "2", "3"]
),
# date
(
{
"foo__date": "1988-01-01",
},
(
("foo__date", "1988-01-01"),
),
["date(foo) = :p0"],
["1988-01-01"]
),
# JSON array variants of __in (useful for unexpected characters)
(
{
'foo__in': '[1,2,3]',
},
(
('foo__in', '[1,2,3]'),
),
['foo in (:p0, :p1, :p2)'],
[1, 2, 3]
),
(
{
'foo__in': '["dog,cat", "cat[dog]"]',
},
(
('foo__in', '["dog,cat", "cat[dog]"]'),
),
['foo in (:p0, :p1)'],
["dog,cat", "cat[dog]"]
),
])
def test_build_where(args, expected_where, expected_params):
f = Filters(sorted(args.items()))
f = Filters(sorted(args))
sql_bits, actual_params = f.build_where_clauses("table")
assert expected_where == sql_bits
assert {