diff --git a/django/db/models/query_utils.py b/django/db/models/query_utils.py index 274a2b7835..9782a4bd7a 100644 --- a/django/db/models/query_utils.py +++ b/django/db/models/query_utils.py @@ -40,6 +40,9 @@ class Q(tree.Node): def __and__(self, other): return self._combine(other, self.AND) + def __invert__(self): + return QNot(self) + class QNot(Q): """ Encapsulates the negation of a Q object. @@ -50,3 +53,6 @@ class QNot(Q): self.add(q, self.AND) self.negate() + def __invert__(self): + return self.children[0] + diff --git a/docs/db-api.txt b/docs/db-api.txt index 3906376f47..49c28d5d5a 100644 --- a/docs/db-api.txt +++ b/docs/db-api.txt @@ -211,11 +211,11 @@ Saving ForeignKey and ManyToManyField fields -------------------------------------------- Updating ``ForeignKey`` fields works exactly the same way as saving a normal -field; simply assign an object of the right type to the field in question:: +field; simply assign an object of the right type to the field in question:: - cheese_blog = Blog.objects.get(name="Cheddar Talk") - entry.blog = cheese_blog - entry.save() + cheese_blog = Blog.objects.get(name="Cheddar Talk") + entry.blog = cheese_blog + entry.save() Updating a ``ManyToManyField`` works a little differently; use the ``add()`` method on the field to add a record to the relation:: @@ -1563,6 +1563,12 @@ This is equivalent to the following SQL ``WHERE`` clause:: You can compose statements of arbitrary complexity by combining ``Q`` objects with the ``&`` and ``|`` operators. You can also use parenthetical grouping. +``Q`` objects can also be negated using the ``~`` operator, allowing for +combined lookups that combine both a normal query and a negated (``NOT``) +query:: + + Q(question__startswith='Who') | ~Q(pub_date__year=2005) + Each lookup function that takes keyword-arguments (e.g. ``filter()``, ``exclude()``, ``get()``) can also be passed one or more ``Q`` objects as positional (not-named) arguments. If you provide multiple ``Q`` object diff --git a/tests/modeltests/or_lookups/models.py b/tests/modeltests/or_lookups/models.py index 5dc8351054..b3500f27e7 100644 --- a/tests/modeltests/or_lookups/models.py +++ b/tests/modeltests/or_lookups/models.py @@ -90,6 +90,17 @@ __test__ = {'API_TESTS':""" >>> Article.objects.filter(Q(headline__contains='bye'), headline__startswith='Hello') [] +# Q objects can be negated +>>> Article.objects.filter(Q(pk=1) | ~Q(pk=2)) +[, ] +>>> Article.objects.filter(~Q(pk=1) & ~Q(pk=2)) +[] + +# This allows for more complex queries than filter() and exclude() alone would +# allow +>>> Article.objects.filter(Q(pk=1) & (~Q(pk=2) | Q(pk=3))) +[] + # Try some arg queries with operations other than get_list >>> Article.objects.get(Q(headline__startswith='Hello'), Q(headline__contains='bye'))