From 073847e58fd7927e3bb920cb437fdedb9749a858 Mon Sep 17 00:00:00 2001 From: Julio Trigo Date: Wed, 17 May 2017 15:09:26 +0100 Subject: [PATCH 1/8] Make operator filter attribute not mandatory --- sqlalchemy_filters/filters.py | 10 ++++----- test/interface/test_filters.py | 38 ++++++++++++++++++---------------- 2 files changed, 25 insertions(+), 23 deletions(-) diff --git a/sqlalchemy_filters/filters.py b/sqlalchemy_filters/filters.py index 7915abc..ee40b0f 100644 --- a/sqlalchemy_filters/filters.py +++ b/sqlalchemy_filters/filters.py @@ -29,6 +29,9 @@ class Operator(object): } def __init__(self, operator): + if operator is None: + operator = '==' + if operator not in self.OPERATORS: raise BadFilterFormat('Operator `{}` not valid.'.format(operator)) @@ -42,18 +45,15 @@ class Filter(object): def __init__(self, filter_, models): try: field_name = filter_['field'] - op = filter_['op'] except KeyError: - raise BadFilterFormat( - '`field` and `op` are mandatory filter attributes.' - ) + raise BadFilterFormat('`field` is a mandatory filter attribute.') except TypeError: raise BadFilterFormat( 'Filter `{}` should be a dictionary.'.format(filter_) ) self.field = Field(models, field_name) - self.operator = Operator(op) + self.operator = Operator(filter_.get('op')) self.value = filter_.get('value') self.value_present = True if 'value' in filter_ else False diff --git a/test/interface/test_filters.py b/test/interface/test_filters.py index 1203d6a..6eba54a 100644 --- a/test/interface/test_filters.py +++ b/test/interface/test_filters.py @@ -35,7 +35,19 @@ def test_multiple_models(self, session): assert expected_error == err.value.args[0] -class TestProvidedFilters(object): +class TestFiltersMixin(object): + + @pytest.fixture + def multiple_bars_inserted(self, session): + bar_1 = Bar(id=1, name='name_1', count=5) + bar_2 = Bar(id=2, name='name_2', count=10) + bar_3 = Bar(id=3, name='name_1', count=None) + bar_4 = Bar(id=4, name='name_4', count=15) + session.add_all([bar_1, bar_2, bar_3, bar_4]) + session.commit() + + +class TestProvidedFilters(TestFiltersMixin): def test_no_filters_provided(self, session): query = session.query(Bar) @@ -67,15 +79,17 @@ def test_invalid_operator(self, session): assert 'Operator `op_not_valid` not valid.' == err.value.args[0] + @pytest.mark.usefixtures('multiple_bars_inserted') def test_no_operator_provided(self, session): query = session.query(Bar) filters = [{'field': 'name', 'value': 'name_1'}] - with pytest.raises(BadFilterFormat) as err: - apply_filters(query, filters) + filtered_query = apply_filters(query, filters) + result = filtered_query.all() - expected_error = '`field` and `op` are mandatory filter attributes.' - assert expected_error == err.value.args[0] + assert len(result) == 2 + assert result[0].id == 1 + assert result[1].id == 3 def test_no_field_provided(self, session): query = session.query(Bar) @@ -84,7 +98,7 @@ def test_no_field_provided(self, session): with pytest.raises(BadFilterFormat) as err: apply_filters(query, filters) - expected_error = '`field` and `op` are mandatory filter attributes.' + expected_error = '`field` is a mandatory filter attribute.' assert expected_error == err.value.args[0] # TODO: replace this test once we add the option to compare against @@ -129,18 +143,6 @@ def test_invalid_field_but_valid_model_attribute(self, session, attr_name): assert expected_error == err.value.args[0] -class TestFiltersMixin(object): - - @pytest.fixture - def multiple_bars_inserted(self, session): - bar_1 = Bar(id=1, name='name_1', count=5) - bar_2 = Bar(id=2, name='name_2', count=10) - bar_3 = Bar(id=3, name='name_1', count=None) - bar_4 = Bar(id=4, name='name_4', count=15) - session.add_all([bar_1, bar_2, bar_3, bar_4]) - session.commit() - - class TestApplyIsNullFilter(TestFiltersMixin): @pytest.mark.usefixtures('multiple_bars_inserted') From fc9b75ccb7d88430e170d0892038422aaca09a78 Mon Sep 17 00:00:00 2001 From: Julio Trigo Date: Wed, 17 May 2017 21:54:58 +0100 Subject: [PATCH 2/8] Update documentation --- README.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.rst b/README.rst index 0a7bd28..d026491 100644 --- a/README.rst +++ b/README.rst @@ -104,8 +104,8 @@ following format: ] Where ``field`` is the name of the field that will be filtered using the -operator provided in ``op`` and (optionally, depending on the operator) -the provided ``value``. +operator provided in ``op`` (optional, defaults to `==`) and the +provided ``value`` (optional depending on the operator). This is the list of operators that can be used: From 7a60e356b49796ae13ea9c4051dce134e06f10f7 Mon Sep 17 00:00:00 2001 From: Julio Trigo Date: Sat, 20 May 2017 09:57:09 +0100 Subject: [PATCH 3/8] Improve documentation --- README.rst | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/README.rst b/README.rst index 0a2c383..1ffbb92 100644 --- a/README.rst +++ b/README.rst @@ -102,7 +102,7 @@ following format: {'field': 'field_2_name', 'op': '!=', 'value': 'field_2_value'}, # ... ] - + Optionally, if there is only one filter, the containing list may be omitted: .. code-block:: python @@ -111,7 +111,7 @@ Optionally, if there is only one filter, the containing list may be omitted: Where ``field`` is the name of the field that will be filtered using the operator provided in ``op`` (optional, defaults to `==`) and the -provided ``value`` (optional depending on the operator). +provided ``value`` (optional, depending on the operator). This is the list of operators that can be used: @@ -151,9 +151,9 @@ Boolean Functions } ] - + Note: ``or`` and ``and`` must reference a list of at least one element. ``not`` must reference a list of exactly one element. - + Sort format ----------- From 297ede502dd12e104c6a9aec6aa9aa6569392eb0 Mon Sep 17 00:00:00 2001 From: Julio Trigo Date: Sat, 20 May 2017 10:09:07 +0100 Subject: [PATCH 4/8] Close parentheses in next line for consistency --- sqlalchemy_filters/filters.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/sqlalchemy_filters/filters.py b/sqlalchemy_filters/filters.py index 53aa929..440c7c3 100644 --- a/sqlalchemy_filters/filters.py +++ b/sqlalchemy_filters/filters.py @@ -113,12 +113,14 @@ def _build_sqlalchemy_filters(filterdef, models): if boolean_function.only_one_arg and len(fn_args) != 1: raise BadFilterFormat( '`{}` must have one argument'.format( - boolean_function.key) + boolean_function.key + ) ) if not boolean_function.only_one_arg and len(fn_args) < 1: raise BadFilterFormat( '`{}` must have one or more arguments'.format( - boolean_function.key) + boolean_function.key + ) ) return [ boolean_function.sqlalchemy_fn( From 9d2a93482e8a793179a267aa778598824d747494 Mon Sep 17 00:00:00 2001 From: Julio Trigo Date: Sat, 20 May 2017 10:10:26 +0100 Subject: [PATCH 5/8] Add default operator --- sqlalchemy_filters/filters.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sqlalchemy_filters/filters.py b/sqlalchemy_filters/filters.py index 440c7c3..9146542 100644 --- a/sqlalchemy_filters/filters.py +++ b/sqlalchemy_filters/filters.py @@ -45,7 +45,7 @@ class Operator(object): 'not_in': lambda f, a: ~f.in_(a), } - def __init__(self, operator): + def __init__(self, operator='=='): if operator is None: operator = '==' From b8d5fadde2a2e72a2ece7becf7a958d16bc02434 Mon Sep 17 00:00:00 2001 From: Julio Trigo Date: Sat, 20 May 2017 10:11:47 +0100 Subject: [PATCH 6/8] Simplify operator check --- sqlalchemy_filters/filters.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sqlalchemy_filters/filters.py b/sqlalchemy_filters/filters.py index 9146542..830a7be 100644 --- a/sqlalchemy_filters/filters.py +++ b/sqlalchemy_filters/filters.py @@ -46,7 +46,7 @@ class Operator(object): } def __init__(self, operator='=='): - if operator is None: + if not operator: operator = '==' if operator not in self.OPERATORS: From 03470d836c3f9063550b1b7771d0659b2cc373b9 Mon Sep 17 00:00:00 2001 From: Julio Trigo Date: Sat, 20 May 2017 10:26:13 +0100 Subject: [PATCH 7/8] Add these changes to the next release --- CHANGELOG.rst | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index e17d187..6a4f3ba 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -9,9 +9,12 @@ Backwards-compatible changes increment the minor version number only. Version 0.3.0 ------------- -Released 2017-05-16 +Released 2017-05-22 * Adds support for boolean functions within filters +* Adds the possibility of supplying a single dictionary as filters when +only one filter is provided +* Makes the `op` filter attribute optional: `==` is the default operator Version 0.2.0 ------------- From 323b5babd99aa3901aec4bdada25fd8bbd9b8f1b Mon Sep 17 00:00:00 2001 From: Julio Trigo Date: Thu, 25 May 2017 11:02:08 +0100 Subject: [PATCH 8/8] Add a better default value for the operator argument --- sqlalchemy_filters/filters.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sqlalchemy_filters/filters.py b/sqlalchemy_filters/filters.py index 830a7be..a1b3997 100644 --- a/sqlalchemy_filters/filters.py +++ b/sqlalchemy_filters/filters.py @@ -45,7 +45,7 @@ class Operator(object): 'not_in': lambda f, a: ~f.in_(a), } - def __init__(self, operator='=='): + def __init__(self, operator=None): if not operator: operator = '=='