mirror of
				https://github.com/django/django.git
				synced 2025-10-30 17:16:10 +00:00 
			
		
		
		
	[1.11.x] Fixed #28399 -- Fixed QuerySet.count() for union(), difference(), and intersection() queries.
Backport of adab280cef from master
			
			
This commit is contained in:
		
				
					committed by
					
						 Tim Graham
						Tim Graham
					
				
			
			
				
	
			
			
			
						parent
						
							d9ef8ffb58
						
					
				
				
					commit
					9350f77c69
				
			| @@ -398,7 +398,7 @@ class SQLCompiler(object): | ||||
|                     continue | ||||
|                 raise | ||||
|         if not parts: | ||||
|             return [], [] | ||||
|             raise EmptyResultSet | ||||
|         combinator_sql = self.connection.ops.set_operators[combinator] | ||||
|         if all and combinator == 'union': | ||||
|             combinator_sql += ' ALL' | ||||
|   | ||||
| @@ -416,12 +416,12 @@ class Query(object): | ||||
|         # aren't smart enough to remove the existing annotations from the | ||||
|         # query, so those would force us to use GROUP BY. | ||||
|         # | ||||
|         # If the query has limit or distinct, then those operations must be | ||||
|         # done in a subquery so that we are aggregating on the limit and/or | ||||
|         # distinct results instead of applying the distinct and limit after the | ||||
|         # aggregation. | ||||
|         # If the query has limit or distinct, or uses set operations, then | ||||
|         # those operations must be done in a subquery so that the query | ||||
|         # aggregates on the limit and/or distinct results instead of applying | ||||
|         # the distinct and limit after the aggregation. | ||||
|         if (isinstance(self.group_by, list) or has_limit or has_existing_annotations or | ||||
|                 self.distinct): | ||||
|                 self.distinct or self.combinator): | ||||
|             from django.db.models.sql.subqueries import AggregateQuery | ||||
|             outer_query = AggregateQuery(self.model) | ||||
|             inner_query = self.clone() | ||||
|   | ||||
| @@ -822,11 +822,15 @@ of other models. Passing different models works as long as the ``SELECT`` list | ||||
| is the same in all ``QuerySet``\s (at least the types, the names don't matter | ||||
| as long as the types in the same order). | ||||
|  | ||||
| In addition, only ``LIMIT``, ``OFFSET``, and ``ORDER BY`` (i.e. slicing and | ||||
| :meth:`order_by`) are allowed on the resulting ``QuerySet``. Further, databases | ||||
| place restrictions on what operations are allowed in the combined queries. For | ||||
| example, most databases don't allow ``LIMIT`` or ``OFFSET`` in the combined | ||||
| queries. | ||||
| In addition, only ``LIMIT``, ``OFFSET``, ``COUNT(*)``, and ``ORDER BY`` (i.e. | ||||
| slicing, :meth:`count`, and :meth:`order_by`) are allowed on the resulting | ||||
| ``QuerySet``. Further, databases place restrictions on what operations are | ||||
| allowed in the combined queries. For example, most databases don't allow | ||||
| ``LIMIT`` or ``OFFSET`` in the combined queries. | ||||
|  | ||||
| .. versionchanged:: 1.11.4 | ||||
|  | ||||
|     ``COUNT(*)`` support was added. | ||||
|  | ||||
| ``intersection()`` | ||||
| ~~~~~~~~~~~~~~~~~~ | ||||
|   | ||||
| @@ -25,3 +25,6 @@ Bugfixes | ||||
| * Corrected ``Field.has_changed()`` to return ``False`` for disabled form | ||||
|   fields: ``BooleanField``, ``MultipleChoiceField``, ``MultiValueField``, | ||||
|   ``FileField``, ``ModelChoiceField``, and ``ModelMultipleChoiceField``. | ||||
|  | ||||
| * Fixed ``QuerySet.count()`` for ``union()``, ``difference()``, and | ||||
|   ``intersection()`` queries. (:ticket:`28399`). | ||||
|   | ||||
| @@ -98,6 +98,27 @@ class QuerySetSetOperationTests(TestCase): | ||||
|         qs2 = Number.objects.filter(num__gte=2, num__lte=3) | ||||
|         self.assertNumbersEqual(qs1.union(qs2).order_by('-num'), [3, 2, 1, 0]) | ||||
|  | ||||
|     def test_count_union(self): | ||||
|         qs1 = Number.objects.filter(num__lte=1).values('num') | ||||
|         qs2 = Number.objects.filter(num__gte=2, num__lte=3).values('num') | ||||
|         self.assertEqual(qs1.union(qs2).count(), 4) | ||||
|  | ||||
|     def test_count_union_empty_result(self): | ||||
|         qs = Number.objects.filter(pk__in=[]) | ||||
|         self.assertEqual(qs.union(qs).count(), 0) | ||||
|  | ||||
|     @skipUnlessDBFeature('supports_select_difference') | ||||
|     def test_count_difference(self): | ||||
|         qs1 = Number.objects.filter(num__lt=10) | ||||
|         qs2 = Number.objects.filter(num__lt=9) | ||||
|         self.assertEqual(qs1.difference(qs2).count(), 1) | ||||
|  | ||||
|     @skipUnlessDBFeature('supports_select_intersection') | ||||
|     def test_count_intersection(self): | ||||
|         qs1 = Number.objects.filter(num__gte=5) | ||||
|         qs2 = Number.objects.filter(num__lte=5) | ||||
|         self.assertEqual(qs1.intersection(qs2).count(), 1) | ||||
|  | ||||
|     @skipUnlessDBFeature('supports_slicing_ordering_in_compound') | ||||
|     def test_ordering_subqueries(self): | ||||
|         qs1 = Number.objects.order_by('num')[:2] | ||||
|   | ||||
		Reference in New Issue
	
	Block a user