Skip to content

Commit eb2e744

Browse files
committed
enable multiple AND filters on the same column
- specify as { 'column__<op>`: value }
1 parent 7e92150 commit eb2e744

File tree

3 files changed

+33
-3
lines changed

3 files changed

+33
-3
lines changed

dataset/table.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -424,7 +424,11 @@ def _args_to_clause(self, args, clauses=()):
424424
clauses = list(clauses)
425425
for column, value in args.items():
426426
column = self._get_column_name(column)
427-
if not self.has_column(column):
427+
if '__' in column:
428+
# enable Django-style column__<op>=value syntax
429+
column, op = column.split('__', 1)
430+
clauses.append(self._generate_clause(column, op, value))
431+
elif not self.has_column(column):
428432
clauses.append(false())
429433
elif isinstance(value, (list, tuple, set)):
430434
clauses.append(self._generate_clause(column, "in", value))

docs/queries.rst

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ Advanced filters
55
================
66

77
``dataset`` provides two methods for running queries: :py:meth:`table.find() <dataset.Table.find>`
8-
and :py:meth:`db.query() <dataset.Database.query>`. The table find helper method provides
8+
and :py:meth:`db.query() <dataset.Database.query>`. The table find helper method provides
99
limited, but simple filtering options::
1010

1111
results = table.find(column={operator: value})
@@ -23,6 +23,12 @@ A special form is using keyword searches on specific columns::
2323
# equal to:
2424
results = table.find(value={'in': ('foo', 'bar')})
2525

26+
To AND-combine multiple operators on the same column, you may specify
27+
the operator using the `column__operator=value` format::
28+
29+
# height >= 20 AND height <= 50
30+
table.find({'height__gte': 20, 'height__lte: 50'}
31+
2632
The following comparison operators are supported:
2733

2834
============== ============================================================
@@ -69,4 +75,4 @@ Finally, you should consider falling back to SQLAlchemy_ core to construct
6975
queries if you are looking for a programmatic, composable method of generating
7076
SQL in Python.
7177

72-
.. _SQLALchemy: https://docs.sqlalchemy.org/
78+
.. _SQLALchemy: https://docs.sqlalchemy.org/

test/test_dataset.py

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -353,6 +353,26 @@ def test_find_dsl(self):
353353
ds = list(self.tbl.find(place={"ilike": "%LwAy"}))
354354
assert len(ds) == 3, ds
355355

356+
def test_find_dsl_dunder_op(self):
357+
ds = list(self.tbl.find(place__like="%lw%"))
358+
assert len(ds) == 3, ds
359+
ds = list(self.tbl.find(temperature__gt=5))
360+
assert len(ds) == 2, ds
361+
ds = list(self.tbl.find(temperature__gte=5))
362+
assert len(ds) == 3, ds
363+
ds = list(self.tbl.find(temperature__lt=0))
364+
assert len(ds) == 1, ds
365+
ds = list(self.tbl.find(temperature__lte=0))
366+
assert len(ds) == 2, ds
367+
ds = list(self.tbl.find(temperature__not=-1))
368+
assert len(ds) == 5, ds
369+
ds = list(self.tbl.find(temperature__between=[5, 8]))
370+
assert len(ds) == 3, ds
371+
ds = list(self.tbl.find(place="G€lway"))
372+
assert len(ds) == 3, ds
373+
ds = list(self.tbl.find(place__ilike="%LwAy"))
374+
assert len(ds) == 3, ds
375+
356376
def test_offset(self):
357377
ds = list(self.tbl.find(place=TEST_CITY_1, _offset=1))
358378
assert len(ds) == 2, ds

0 commit comments

Comments
 (0)