Author: lukeplant
Date: 2006-05-26 18:41:43 -0500 (Fri, 26 May 2006)
New Revision: 2997

Modified:
   django/trunk/django/db/models/query.py
   django/trunk/tests/modeltests/or_lookups/models.py
Log:
Fixed bug with QuerySet.exclude() failing to do negations on Q objects, and
at the same time generalised exclude/QNot so that they work for 'external'
Q objects i.e. ones that simply have 'get_sql' defined.


Modified: django/trunk/django/db/models/query.py
===================================================================
--- django/trunk/django/db/models/query.py      2006-05-26 21:28:12 UTC (rev 
2996)
+++ django/trunk/django/db/models/query.py      2006-05-26 23:41:43 UTC (rev 
2997)
@@ -291,22 +291,26 @@
 
     def filter(self, *args, **kwargs):
         "Returns a new QuerySet instance with the args ANDed to the existing 
set."
-        return self._filter_or_exclude(Q, *args, **kwargs)
+        return self._filter_or_exclude(None, *args, **kwargs)
 
     def exclude(self, *args, **kwargs):
         "Returns a new QuerySet instance with NOT (args) ANDed to the existing 
set."
         return self._filter_or_exclude(QNot, *args, **kwargs)
 
-    def _filter_or_exclude(self, qtype, *args, **kwargs):
+    def _filter_or_exclude(self, mapper, *args, **kwargs):
+        # mapper is a callable used to transform Q objects,
+        # or None for identity transform
+        if mapper is None:
+            mapper = lambda x: x
         if len(args) > 0 or len(kwargs) > 0:
             assert self._limit is None and self._offset is None, \
                 "Cannot filter a query once a slice has been taken."
 
         clone = self._clone()
         if len(kwargs) > 0:
-            clone._filters = clone._filters & qtype(**kwargs)
+            clone._filters = clone._filters & mapper(Q(**kwargs))
         if len(args) > 0:
-            clone._filters = clone._filters & reduce(operator.and_, args)
+            clone._filters = clone._filters & reduce(operator.and_, 
map(mapper, args))
         return clone
 
     def complex_filter(self, filter_obj):
@@ -318,7 +322,7 @@
         if hasattr(filter_obj, 'get_sql'):
             return self._filter_or_exclude(None, filter_obj)
         else:
-            return self._filter_or_exclude(Q, **filter_obj)
+            return self._filter_or_exclude(None, **filter_obj)
 
     def select_related(self, true_or_false=True):
         "Returns a new QuerySet instance with '_select_related' modified."
@@ -582,9 +586,12 @@
 
 class QNot(Q):
     "Encapsulates NOT (...) queries as objects"
+    def __init__(self, q):
+        "Creates a negation of the q object passed in."
+        self.q = q
 
     def get_sql(self, opts):
-        tables, joins, where, params = super(QNot, self).get_sql(opts)
+        tables, joins, where, params = self.q.get_sql(opts)
         where2 = ['(NOT (%s))' % " AND ".join(where)]
         return tables, joins, where2, params
 

Modified: django/trunk/tests/modeltests/or_lookups/models.py
===================================================================
--- django/trunk/tests/modeltests/or_lookups/models.py  2006-05-26 21:28:12 UTC 
(rev 2996)
+++ django/trunk/tests/modeltests/or_lookups/models.py  2006-05-26 23:41:43 UTC 
(rev 2997)
@@ -89,6 +89,10 @@
 >>> Article.objects.filter(Q(headline__startswith='Hello')).in_bulk([1,2])
 {1: Hello}
 
+# Demonstrating exclude with a Q object
+>>> Article.objects.exclude(Q(headline__startswith='Hello'))
+[Goodbye]
+
 # The 'complex_filter' method supports framework features such as 
 # 'limit_choices_to' which normally take a single dictionary of lookup 
arguments
 # but need to support arbitrary queries via Q objects too.


--~--~---------~--~----~------------~-------~--~----~
You received this message because you are subscribed to the Google Groups 
"Django updates" group.
To post to this group, send email to django-updates@googlegroups.com
To unsubscribe from this group, send email to [EMAIL PROTECTED]
For more options, visit this group at 
http://groups.google.com/group/django-updates
-~----------~----~----~----~------~----~------~--~---

Reply via email to