mirror of
				https://github.com/django/django.git
				synced 2025-10-31 09:41:08 +00:00 
			
		
		
		
	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. git-svn-id: http://code.djangoproject.com/svn/django/trunk@2997 bcc190cf-cafb-0310-a4f2-bffc1f526a37
This commit is contained in:
		| @@ -291,22 +291,26 @@ class QuerySet(object): | ||||
|  | ||||
|     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 @@ class QuerySet(object): | ||||
|         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 Q(object): | ||||
|  | ||||
| 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 | ||||
|  | ||||
|   | ||||
| @@ -89,6 +89,10 @@ Hello and goodbye | ||||
| >>> 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. | ||||
|   | ||||
		Reference in New Issue
	
	Block a user