From eb2e744c8a887d890d2f148182e67504856187f9 Mon Sep 17 00:00:00 2001 From: miraculixx Date: Tue, 17 May 2022 15:58:26 +0200 Subject: [PATCH] enable multiple AND filters on the same column - specify as { 'column__`: value } --- dataset/table.py | 6 +++++- docs/queries.rst | 10 ++++++++-- test/test_dataset.py | 20 ++++++++++++++++++++ 3 files changed, 33 insertions(+), 3 deletions(-) diff --git a/dataset/table.py b/dataset/table.py index 75e05b4..76bbb4b 100644 --- a/dataset/table.py +++ b/dataset/table.py @@ -424,7 +424,11 @@ def _args_to_clause(self, args, clauses=()): clauses = list(clauses) for column, value in args.items(): column = self._get_column_name(column) - if not self.has_column(column): + if '__' in column: + # enable Django-style column__=value syntax + column, op = column.split('__', 1) + clauses.append(self._generate_clause(column, op, value)) + elif not self.has_column(column): clauses.append(false()) elif isinstance(value, (list, tuple, set)): clauses.append(self._generate_clause(column, "in", value)) diff --git a/docs/queries.rst b/docs/queries.rst index 2d25e93..b0b48cc 100644 --- a/docs/queries.rst +++ b/docs/queries.rst @@ -5,7 +5,7 @@ Advanced filters ================ ``dataset`` provides two methods for running queries: :py:meth:`table.find() ` -and :py:meth:`db.query() `. The table find helper method provides +and :py:meth:`db.query() `. The table find helper method provides limited, but simple filtering options:: results = table.find(column={operator: value}) @@ -23,6 +23,12 @@ A special form is using keyword searches on specific columns:: # equal to: results = table.find(value={'in': ('foo', 'bar')}) +To AND-combine multiple operators on the same column, you may specify +the operator using the `column__operator=value` format:: + + # height >= 20 AND height <= 50 + table.find({'height__gte': 20, 'height__lte: 50'} + The following comparison operators are supported: ============== ============================================================ @@ -69,4 +75,4 @@ Finally, you should consider falling back to SQLAlchemy_ core to construct queries if you are looking for a programmatic, composable method of generating SQL in Python. -.. _SQLALchemy: https://docs.sqlalchemy.org/ \ No newline at end of file +.. _SQLALchemy: https://docs.sqlalchemy.org/ diff --git a/test/test_dataset.py b/test/test_dataset.py index f7c94eb..c909a92 100644 --- a/test/test_dataset.py +++ b/test/test_dataset.py @@ -353,6 +353,26 @@ def test_find_dsl(self): ds = list(self.tbl.find(place={"ilike": "%LwAy"})) assert len(ds) == 3, ds + def test_find_dsl_dunder_op(self): + ds = list(self.tbl.find(place__like="%lw%")) + assert len(ds) == 3, ds + ds = list(self.tbl.find(temperature__gt=5)) + assert len(ds) == 2, ds + ds = list(self.tbl.find(temperature__gte=5)) + assert len(ds) == 3, ds + ds = list(self.tbl.find(temperature__lt=0)) + assert len(ds) == 1, ds + ds = list(self.tbl.find(temperature__lte=0)) + assert len(ds) == 2, ds + ds = list(self.tbl.find(temperature__not=-1)) + assert len(ds) == 5, ds + ds = list(self.tbl.find(temperature__between=[5, 8])) + assert len(ds) == 3, ds + ds = list(self.tbl.find(place="G€lway")) + assert len(ds) == 3, ds + ds = list(self.tbl.find(place__ilike="%LwAy")) + assert len(ds) == 3, ds + def test_offset(self): ds = list(self.tbl.find(place=TEST_CITY_1, _offset=1)) assert len(ds) == 2, ds