Skip to content

Commit cdbaf8e

Browse files
authored
Merge pull request piccolo-orm#60 from piccolo-orm/is_null
added is_null / is_not_null
2 parents 9091e2a + a97f279 commit cdbaf8e

File tree

6 files changed

+99
-3
lines changed

6 files changed

+99
-3
lines changed

CHANGES

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@ Changes
66
* Added ``WhereRaw``, so raw SQL can be used in where clauses.
77
* ``piccolo shell run`` now uses syntax highlighting - courtesy of Fingel.
88

9-
109
0.16.2
1110
------
1211
Reordering the dependencies in requirements.txt when using ``piccolo asgi new``

docs/src/piccolo/query_clauses/where.rst

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,44 @@ is_in / not_in
103103
104104
-------------------------------------------------------------------------------
105105

106+
is_null / is_not_null
107+
---------------------
108+
109+
These queries work, but some linters will complain about doing a comparison
110+
with None:
111+
112+
.. code-block:: python
113+
114+
b = Band
115+
116+
# Fetch all bands with a manager
117+
b.select().where(
118+
b.manager != None
119+
).run_sync()
120+
121+
# Fetch all bands without a manager
122+
b.select().where(
123+
b.manager == None
124+
).run_sync()
125+
126+
To avoid the linter errors, you can use `is_null` and `is_not_null` instead.
127+
128+
.. code-block:: python
129+
130+
b = Band
131+
132+
# Fetch all bands with a manager
133+
b.select().where(
134+
b.manager.is_not_null()
135+
).run_sync()
136+
137+
# Fetch all bands without a manager
138+
b.select().where(
139+
b.manager.is_null()
140+
).run_sync()
141+
142+
-------------------------------------------------------------------------------
143+
106144
Complex queries - and / or
107145
---------------------------
108146

piccolo/columns/base.py

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
GreaterThan,
1616
ILike,
1717
In,
18+
IsNotNull,
1819
IsNull,
1920
LessEqualThan,
2021
LessThan,
@@ -366,11 +367,28 @@ def __eq__(self, value) -> Where: # type: ignore
366367
return Where(column=self, value=value, operator=Equal)
367368

368369
def __ne__(self, value) -> Where: # type: ignore
369-
return Where(column=self, value=value, operator=NotEqual)
370+
if value is None:
371+
return Where(column=self, operator=IsNotNull)
372+
else:
373+
return Where(column=self, value=value, operator=NotEqual)
370374

371375
def __hash__(self):
372376
return hash(self._meta.name)
373377

378+
def is_null(self) -> Where:
379+
"""
380+
Can be used instead of `MyTable.column != None`, because some linters
381+
don't like a comparison to None.
382+
"""
383+
return Where(column=self, operator=IsNull)
384+
385+
def is_not_null(self) -> Where:
386+
"""
387+
Can be used instead of `MyTable.column == None`, because some linters
388+
don't like a comparison to None.
389+
"""
390+
return Where(column=self, operator=IsNotNull)
391+
374392
def as_alias(self, name: str) -> Column:
375393
"""
376394
Allows column names to be changed in the result of a select.

piccolo/columns/operators/comparison.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,10 @@ class IsNull(ComparisonOperator):
99
template = "{name} IS NULL"
1010

1111

12+
class IsNotNull(ComparisonOperator):
13+
template = "{name} IS NOT NULL"
14+
15+
1216
class Equal(ComparisonOperator):
1317
template = "{name} = {value}"
1418

tests/apps/migrations/commands/test_base.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,9 @@
1212

1313
class TestBaseMigrationManager(TestCase):
1414
def test_create_migration_table(self):
15-
self.assertTrue(BaseMigrationManager().create_migration_table())
15+
self.assertTrue(
16+
run_sync(BaseMigrationManager().create_migration_table())
17+
)
1618

1719
def tearDown(self):
1820
Migration.raw("DROP TABLE IF EXISTS migration").run_sync()

tests/table/test_select.py

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,41 @@ def test_where_greater_than(self):
7777

7878
self.assertEqual(response, [{"name": "Rustaceans"}])
7979

80+
def test_where_is_null(self):
81+
self.insert_rows()
82+
83+
Band(name="Managerless", popularity=0, manager=None).save().run_sync()
84+
85+
queries = (
86+
Band.select(Band.name).where(Band.manager == None), # noqa
87+
Band.select(Band.name).where(Band.manager.is_null()),
88+
)
89+
90+
for query in queries:
91+
response = query.run_sync()
92+
self.assertEqual(response, [{"name": "Managerless"}])
93+
94+
def test_where_is_not_null(self):
95+
self.insert_rows()
96+
97+
Band(name="Managerless", popularity=0, manager=None).save().run_sync()
98+
99+
queries = (
100+
Band.select(Band.name).where(Band.manager != None), # noqa
101+
Band.select(Band.name).where(Band.manager.is_not_null()),
102+
)
103+
104+
for query in queries:
105+
response = query.run_sync()
106+
self.assertEqual(
107+
response,
108+
[
109+
{"name": "Pythonistas"},
110+
{"name": "Rustaceans"},
111+
{"name": "CSharps"},
112+
],
113+
)
114+
80115
def test_where_greater_equal_than(self):
81116
self.insert_rows()
82117

0 commit comments

Comments
 (0)