From cb77bb6b69bb80c79c97d2f0792d173fc2f443d4 Mon Sep 17 00:00:00 2001 From: Filip Kucharczyk Date: Thu, 5 Dec 2019 00:21:03 +0100 Subject: [PATCH 1/6] Implement __bool__ on Q and QCombination --- mongoengine/queryset/base.py | 2 +- mongoengine/queryset/visitor.py | 20 ++++++++++++++++---- tests/queryset/test_visitor.py | 11 +++++++++++ 3 files changed, 28 insertions(+), 5 deletions(-) diff --git a/mongoengine/queryset/base.py b/mongoengine/queryset/base.py index a648391e..c6f467cc 100644 --- a/mongoengine/queryset/base.py +++ b/mongoengine/queryset/base.py @@ -686,7 +686,7 @@ class BaseQuerySet(object): .. versionchanged:: 0.6 Raises InvalidQueryError if filter has been set """ queryset = self.clone() - if not queryset._query_obj.empty: + if queryset._query_obj: msg = "Cannot use a filter whilst using `with_id`" raise InvalidQueryError(msg) return queryset.filter(pk=object_id).first() diff --git a/mongoengine/queryset/visitor.py b/mongoengine/queryset/visitor.py index 058c722a..a7295ae5 100644 --- a/mongoengine/queryset/visitor.py +++ b/mongoengine/queryset/visitor.py @@ -2,6 +2,8 @@ import copy from mongoengine.errors import InvalidQueryError from mongoengine.queryset import transform +import warnings + __all__ = ("Q", "QNode") @@ -101,13 +103,15 @@ class QNode(object): return self # Or if this Q is empty, ignore it and just use `other`. - if self.empty: + if not self: return other return QCombination(operation, [self, other]) @property def empty(self): + msg = "'empty' property is deprecated in favour of using 'not bool(filter)" + warnings.warn(msg, DeprecationWarning) return False def __or__(self, other): @@ -137,6 +141,9 @@ class QCombination(QNode): op = " & " if self.operation is self.AND else " | " return "(%s)" % op.join([repr(node) for node in self.children]) + def __bool__(self): + return bool(self.children) + def accept(self, visitor): for i in range(len(self.children)): if isinstance(self.children[i], QNode): @@ -146,6 +153,8 @@ class QCombination(QNode): @property def empty(self): + msg = "'empty' property is deprecated in favour of using 'not bool(filter)" + warnings.warn(msg, DeprecationWarning) return not bool(self.children) def __eq__(self, other): @@ -167,12 +176,15 @@ class Q(QNode): def __repr__(self): return "Q(**%s)" % repr(self.query) + def __bool__(self): + return bool(self.query) + + def __eq__(self, other): + return self.__class__ == other.__class__ and self.query == other.query + def accept(self, visitor): return visitor.visit_query(self) @property def empty(self): return not bool(self.query) - - def __eq__(self, other): - return self.__class__ == other.__class__ and self.query == other.query diff --git a/tests/queryset/test_visitor.py b/tests/queryset/test_visitor.py index afa00839..81e0f253 100644 --- a/tests/queryset/test_visitor.py +++ b/tests/queryset/test_visitor.py @@ -407,6 +407,17 @@ class TestQ(unittest.TestCase): def test_combine_or_both_empty(self): assert Q() | Q() == Q() + def test_q_bool(self): + assert Q(name="John") + assert not Q() + + def test_combine_bool(self): + assert not Q() & Q() + assert Q() & Q(name="John") + assert Q(name="John") & Q() + assert Q() | Q(name="John") + assert Q(name="John") | Q() + if __name__ == "__main__": unittest.main() From bd6c52e025fbe60473a6f009b100eb4d8edbfe83 Mon Sep 17 00:00:00 2001 From: Filip Kucharczyk Date: Thu, 5 Dec 2019 00:30:03 +0100 Subject: [PATCH 2/6] Changelog --- docs/changelog.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/changelog.rst b/docs/changelog.rst index 99081957..e2ffa41e 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -25,6 +25,7 @@ Development - In bulk write insert, the detailed error message would raise in exception. - Added ability to compare Q and Q operations #2204 - Added ability to use a db alias on query_counter #2194 +- Added ability to check if Q or Q operations is empty by parsing them to bool #2210 Changes in 0.18.2 ================= From 5f14d958ac32925df18e757f77f729f3bfb79c5a Mon Sep 17 00:00:00 2001 From: Filip Kucharczyk Date: Thu, 5 Dec 2019 00:46:57 +0100 Subject: [PATCH 3/6] Sort imports --- mongoengine/queryset/visitor.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/mongoengine/queryset/visitor.py b/mongoengine/queryset/visitor.py index a7295ae5..8038d23f 100644 --- a/mongoengine/queryset/visitor.py +++ b/mongoengine/queryset/visitor.py @@ -1,9 +1,8 @@ import copy +import warnings from mongoengine.errors import InvalidQueryError from mongoengine.queryset import transform -import warnings - __all__ = ("Q", "QNode") From 6e8196d475953f88bd70207e81234bc07e1526d0 Mon Sep 17 00:00:00 2001 From: Filip Kucharczyk Date: Thu, 5 Dec 2019 01:31:37 +0100 Subject: [PATCH 4/6] Python 2.x compatibility --- mongoengine/queryset/visitor.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/mongoengine/queryset/visitor.py b/mongoengine/queryset/visitor.py index 8038d23f..7faed897 100644 --- a/mongoengine/queryset/visitor.py +++ b/mongoengine/queryset/visitor.py @@ -143,6 +143,8 @@ class QCombination(QNode): def __bool__(self): return bool(self.children) + __nonzero__ = __bool__ # For Py2 support + def accept(self, visitor): for i in range(len(self.children)): if isinstance(self.children[i], QNode): @@ -178,6 +180,8 @@ class Q(QNode): def __bool__(self): return bool(self.query) + __nonzero__ = __bool__ # For Py2 support + def __eq__(self, other): return self.__class__ == other.__class__ and self.query == other.query From 1b38309d70efc122720d9c5d3fcc6d362436ed62 Mon Sep 17 00:00:00 2001 From: Filip Kucharczyk Date: Fri, 6 Dec 2019 10:14:22 +0100 Subject: [PATCH 5/6] Revert 'empty' usage to it's previous state --- mongoengine/queryset/base.py | 2 +- mongoengine/queryset/visitor.py | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/mongoengine/queryset/base.py b/mongoengine/queryset/base.py index c6f467cc..a648391e 100644 --- a/mongoengine/queryset/base.py +++ b/mongoengine/queryset/base.py @@ -686,7 +686,7 @@ class BaseQuerySet(object): .. versionchanged:: 0.6 Raises InvalidQueryError if filter has been set """ queryset = self.clone() - if queryset._query_obj: + if not queryset._query_obj.empty: msg = "Cannot use a filter whilst using `with_id`" raise InvalidQueryError(msg) return queryset.filter(pk=object_id).first() diff --git a/mongoengine/queryset/visitor.py b/mongoengine/queryset/visitor.py index 7faed897..470839c1 100644 --- a/mongoengine/queryset/visitor.py +++ b/mongoengine/queryset/visitor.py @@ -102,14 +102,14 @@ class QNode(object): return self # Or if this Q is empty, ignore it and just use `other`. - if not self: + if self.empty: return other return QCombination(operation, [self, other]) @property def empty(self): - msg = "'empty' property is deprecated in favour of using 'not bool(filter)" + msg = "'empty' property is deprecated in favour of using 'not bool(filter)'" warnings.warn(msg, DeprecationWarning) return False @@ -154,7 +154,7 @@ class QCombination(QNode): @property def empty(self): - msg = "'empty' property is deprecated in favour of using 'not bool(filter)" + msg = "'empty' property is deprecated in favour of using 'not bool(filter)'" warnings.warn(msg, DeprecationWarning) return not bool(self.children) From e83132f32c254f04cb505d1ede1d90f0dac84b18 Mon Sep 17 00:00:00 2001 From: Filip Kucharczyk Date: Tue, 10 Dec 2019 11:51:33 +0100 Subject: [PATCH 6/6] Note deprecation of 'empty' in changelog --- docs/changelog.rst | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/docs/changelog.rst b/docs/changelog.rst index e2ffa41e..bc01a403 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -17,6 +17,9 @@ Development - If you catch/use ``MongoEngineConnectionError`` in your code, you'll have to rename it. - BREAKING CHANGE: Positional arguments when instantiating a document are no longer supported. #2103 - From now on keyword arguments (e.g. ``Doc(field_name=value)``) are required. +- DEPRECATION: ``Q.empty`` & ``QNode.empty`` are marked as deprecated and will be removed in a next version of MongoEngine. #2210 + - Added ability to check if Q or QNode are empty by parsing them to bool. + - Instead of ``Q(name="John").empty`` use ``not Q(name="John")``. - Improve error message related to InvalidDocumentError #2180 - Fix updating/modifying/deleting/reloading a document that's sharded by a field with ``db_field`` specified. #2125 - ``ListField`` now accepts an optional ``max_length`` parameter. #2110 @@ -25,7 +28,6 @@ Development - In bulk write insert, the detailed error message would raise in exception. - Added ability to compare Q and Q operations #2204 - Added ability to use a db alias on query_counter #2194 -- Added ability to check if Q or Q operations is empty by parsing them to bool #2210 Changes in 0.18.2 =================