Code

Ticket #17681: emptyqs_removal.diff

File emptyqs_removal.diff, 12.8 KB (added by akaariai, 2 years ago)
Line 
1diff --git a/django/contrib/auth/models.py b/django/contrib/auth/models.py
2index eb39868..fb5ad10 100644
3--- a/django/contrib/auth/models.py
4+++ b/django/contrib/auth/models.py
5@@ -408,8 +408,8 @@ class AnonymousUser(object):
6     is_staff = False
7     is_active = False
8     is_superuser = False
9-    _groups = EmptyManager()
10-    _user_permissions = EmptyManager()
11+    _groups = EmptyManager(model=User)
12+    _user_permissions = EmptyManager(model=User)
13 
14     def __init__(self):
15         pass
16diff --git a/django/db/models/manager.py b/django/db/models/manager.py
17index e1bbf6e..28a320a 100644
18--- a/django/db/models/manager.py
19+++ b/django/db/models/manager.py
20@@ -1,6 +1,6 @@
21 import copy
22 from django.db import router
23-from django.db.models.query import QuerySet, EmptyQuerySet, insert_query, RawQuerySet
24+from django.db.models.query import QuerySet, insert_query, RawQuerySet
25 from django.db.models import signals
26 from django.db.models.fields import FieldDoesNotExist
27 
28@@ -46,10 +46,10 @@ class Manager(object):
29     # Tracks each time a Manager instance is created. Used to retain order.
30     creation_counter = 0
31 
32-    def __init__(self):
33+    def __init__(self, model=None):
34         super(Manager, self).__init__()
35         self._set_creation_counter()
36-        self.model = None
37+        self.model = model
38         self._inherited = False
39         self._db = None
40 
41@@ -101,7 +101,7 @@ class Manager(object):
42     #######################
43 
44     def get_empty_query_set(self):
45-        return EmptyQuerySet(self.model, using=self._db)
46+        return QuerySet(self.model, using=self.db).none()
47 
48     def get_query_set(self):
49         """Returns a new QuerySet object.  Subclasses can override this method
50diff --git a/django/db/models/query.py b/django/db/models/query.py
51index 41c24c7..0d91ce9 100644
52--- a/django/db/models/query.py
53+++ b/django/db/models/query.py
54@@ -31,7 +31,6 @@ class QuerySet(object):
55     """
56     def __init__(self, model=None, query=None, using=None):
57         self.model = model
58-        # EmptyQuerySet instantiates QuerySet with model as None
59         self._db = using
60         self.query = query or sql.Query(self.model)
61         self._result_cache = None
62@@ -210,8 +209,6 @@ class QuerySet(object):
63 
64     def __and__(self, other):
65         self._merge_sanity_check(other)
66-        if isinstance(other, EmptyQuerySet):
67-            return other._clone()
68         combined = self._clone()
69         combined.query.combine(other.query, sql.AND)
70         return combined
71@@ -219,8 +216,6 @@ class QuerySet(object):
72     def __or__(self, other):
73         self._merge_sanity_check(other)
74         combined = self._clone()
75-        if isinstance(other, EmptyQuerySet):
76-            return combined
77         combined.query.combine(other.query, sql.OR)
78         return combined
79 
80@@ -600,7 +595,9 @@ class QuerySet(object):
81         """
82         Returns an empty QuerySet.
83         """
84-        return self._clone(klass=EmptyQuerySet)
85+        c = self._clone()
86+        c.query.set_empty()
87+        return c
88 
89     ##################################################################
90     # PUBLIC METHODS THAT ALTER ATTRIBUTES AND RETURN A NEW QUERYSET #
91@@ -1116,127 +1113,6 @@ class DateQuerySet(QuerySet):
92             c._setup_query()
93         return c
94 
95-
96-class EmptyQuerySet(QuerySet):
97-    def __init__(self, model=None, query=None, using=None):
98-        super(EmptyQuerySet, self).__init__(model, query, using)
99-        self._result_cache = []
100-
101-    def __and__(self, other):
102-        return self._clone()
103-
104-    def __or__(self, other):
105-        return other._clone()
106-
107-    def count(self):
108-        return 0
109-
110-    def delete(self):
111-        pass
112-
113-    def _clone(self, klass=None, setup=False, **kwargs):
114-        c = super(EmptyQuerySet, self)._clone(klass, setup=setup, **kwargs)
115-        c._result_cache = []
116-        return c
117-
118-    def iterator(self):
119-        # This slightly odd construction is because we need an empty generator
120-        # (it raises StopIteration immediately).
121-        yield iter([]).next()
122-
123-    def all(self):
124-        """
125-        Always returns EmptyQuerySet.
126-        """
127-        return self
128-
129-    def filter(self, *args, **kwargs):
130-        """
131-        Always returns EmptyQuerySet.
132-        """
133-        return self
134-
135-    def exclude(self, *args, **kwargs):
136-        """
137-        Always returns EmptyQuerySet.
138-        """
139-        return self
140-
141-    def complex_filter(self, filter_obj):
142-        """
143-        Always returns EmptyQuerySet.
144-        """
145-        return self
146-
147-    def select_related(self, *fields, **kwargs):
148-        """
149-        Always returns EmptyQuerySet.
150-        """
151-        return self
152-
153-    def annotate(self, *args, **kwargs):
154-        """
155-        Always returns EmptyQuerySet.
156-        """
157-        return self
158-
159-    def order_by(self, *field_names):
160-        """
161-        Always returns EmptyQuerySet.
162-        """
163-        return self
164-
165-    def distinct(self, fields=None):
166-        """
167-        Always returns EmptyQuerySet.
168-        """
169-        return self
170-
171-    def extra(self, select=None, where=None, params=None, tables=None,
172-              order_by=None, select_params=None):
173-        """
174-        Always returns EmptyQuerySet.
175-        """
176-        assert self.query.can_filter(), \
177-                "Cannot change a query once a slice has been taken"
178-        return self
179-
180-    def reverse(self):
181-        """
182-        Always returns EmptyQuerySet.
183-        """
184-        return self
185-
186-    def defer(self, *fields):
187-        """
188-        Always returns EmptyQuerySet.
189-        """
190-        return self
191-
192-    def only(self, *fields):
193-        """
194-        Always returns EmptyQuerySet.
195-        """
196-        return self
197-
198-    def update(self, **kwargs):
199-        """
200-        Don't update anything.
201-        """
202-        return 0
203-
204-    def aggregate(self, *args, **kwargs):
205-        """
206-        Return a dict mapping the aggregate names to None
207-        """
208-        for arg in args:
209-            kwargs[arg.default_alias] = arg
210-        return dict([(key, None) for key in kwargs])
211-
212-    # EmptyQuerySet is always an empty result in where-clauses (and similar
213-    # situations).
214-    value_annotation = False
215-
216 def get_klass_info(klass, max_depth=0, cur_depth=0, requested=None,
217                    only_load=None, local_only=False):
218     """
219diff --git a/django/db/models/sql/compiler.py b/django/db/models/sql/compiler.py
220index 72948f9..6808b34 100644
221--- a/django/db/models/sql/compiler.py
222+++ b/django/db/models/sql/compiler.py
223@@ -60,7 +60,6 @@ class SQLCompiler(object):
224         """
225         if with_limits and self.query.low_mark == self.query.high_mark:
226             return '', ()
227-
228         self.pre_sql_setup()
229         # After executing the query, we must get rid of any joins the query
230         # setup created. So, take note of alias counts before the query ran.
231diff --git a/django/db/models/sql/query.py b/django/db/models/sql/query.py
232index a78df34..825647d 100644
233--- a/django/db/models/sql/query.py
234+++ b/django/db/models/sql/query.py
235@@ -22,7 +22,7 @@ from django.db.models.sql.constants import *
236 from django.db.models.sql.datastructures import EmptyResultSet, Empty, MultiJoin
237 from django.db.models.sql.expressions import SQLEvaluator
238 from django.db.models.sql.where import (WhereNode, Constraint, EverythingNode,
239-    ExtraWhere, AND, OR)
240+    ExtraWhere, AND, OR, NothingNode)
241 from django.core.exceptions import FieldError
242 
243 __all__ = ['Query', 'RawQuery']
244@@ -426,6 +426,13 @@ class Query(object):
245 
246         return number
247 
248+    def set_empty(self):
249+        """
250+        Turn this queryset into one which will return nothing when evaluated.
251+        """
252+        self.where = self.where_class()
253+        self.where.add(NothingNode(), AND)
254+
255     def has_results(self, using):
256         q = self.clone()
257         q.add_extra({'a': 1}, None, None, None, None, None)
258diff --git a/django/db/models/sql/where.py b/django/db/models/sql/where.py
259index 1455ba6..36cf82d 100644
260--- a/django/db/models/sql/where.py
261+++ b/django/db/models/sql/where.py
262@@ -36,6 +36,9 @@ class WhereNode(tree.Node):
263     """
264     default = AND
265 
266+    def __init__(self, *args, **kwargs):
267+        super(WhereNode, self).__init__(*args, **kwargs)
268+
269     def add(self, data, connector):
270         """
271         Add a node to the where-tree. If the data is a list or tuple, it is
272@@ -252,6 +255,7 @@ class WhereNode(tree.Node):
273                 if hasattr(child[3], 'relabel_aliases'):
274                     child[3].relabel_aliases(change_map)
275 
276+
277 class EverythingNode(object):
278     """
279     A node that matches everything.
280diff --git a/docs/ref/models/querysets.txt b/docs/ref/models/querysets.txt
281index 103cae1..886cbf6 100644
282--- a/docs/ref/models/querysets.txt
283+++ b/docs/ref/models/querysets.txt
284@@ -601,8 +601,8 @@ none
285 
286 .. method:: none()
287 
288-Returns an ``EmptyQuerySet`` — a ``QuerySet`` subclass that always evaluates to
289-an empty list. This can be used in cases where you know that you should return
290+Returns a ``QuerySet`` that always evaluates to an empty list. No query should
291+be executed. This can be used in cases where you know that you should return
292 an empty result set and your caller is expecting a ``QuerySet`` object (instead
293 of returning an empty list, for example.)
294 
295diff --git a/tests/modeltests/get_object_or_404/tests.py b/tests/modeltests/get_object_or_404/tests.py
296index 280720f..992a2f3 100644
297--- a/tests/modeltests/get_object_or_404/tests.py
298+++ b/tests/modeltests/get_object_or_404/tests.py
299@@ -53,7 +53,7 @@ class GetObjectOr404Tests(TestCase):
300             get_object_or_404, Author.objects.all()
301         )
302 
303-        # Using an EmptyQuerySet raises a Http404 error.
304+        # Using a empty queryset raises a Http404 error.
305         self.assertRaises(Http404,
306             get_object_or_404, Article.objects.none(), title__contains="Run"
307         )
308diff --git a/tests/modeltests/lookup/tests.py b/tests/modeltests/lookup/tests.py
309index 3571e21..5333402 100644
310--- a/tests/modeltests/lookup/tests.py
311+++ b/tests/modeltests/lookup/tests.py
312@@ -436,7 +436,7 @@ class LookupTests(TestCase):
313             ])
314 
315     def test_none(self):
316-       # none() returns an EmptyQuerySet that behaves like any other QuerySet object
317+       # none() returns an empty queryset that behaves like any other QuerySet object
318         self.assertQuerysetEqual(Article.objects.none(), [])
319         self.assertQuerysetEqual(
320             Article.objects.none().filter(headline__startswith='Article'), [])
321diff --git a/tests/regressiontests/queries/tests.py b/tests/regressiontests/queries/tests.py
322index ded3e8f..a706c82 100644
323--- a/tests/regressiontests/queries/tests.py
324+++ b/tests/regressiontests/queries/tests.py
325@@ -8,7 +8,7 @@ from django.conf import settings
326 from django.core.exceptions import FieldError
327 from django.db import DatabaseError, connection, connections, DEFAULT_DB_ALIAS
328 from django.db.models import Count
329-from django.db.models.query import Q, ITER_CHUNK_SIZE, EmptyQuerySet
330+from django.db.models.query import Q, ITER_CHUNK_SIZE
331 from django.test import TestCase, skipUnlessDBFeature
332 from django.utils import unittest
333 from django.utils.datastructures import SortedDict
334@@ -655,31 +655,6 @@ class Queries1Tests(BaseQuerysetTest):
335             ['<Item: one>', '<Item: two>']
336         )
337 
338-    def test_ticket7235(self):
339-        # An EmptyQuerySet should not raise exceptions if it is filtered.
340-        q = EmptyQuerySet()
341-        self.assertQuerysetEqual(q.all(), [])
342-        self.assertQuerysetEqual(q.filter(x=10), [])
343-        self.assertQuerysetEqual(q.exclude(y=3), [])
344-        self.assertQuerysetEqual(q.complex_filter({'pk': 1}), [])
345-        self.assertQuerysetEqual(q.select_related('spam', 'eggs'), [])
346-        self.assertQuerysetEqual(q.annotate(Count('eggs')), [])
347-        self.assertQuerysetEqual(q.order_by('-pub_date', 'headline'), [])
348-        self.assertQuerysetEqual(q.distinct(), [])
349-        self.assertQuerysetEqual(
350-            q.extra(select={'is_recent': "pub_date > '2006-01-01'"}),
351-            []
352-        )
353-        q.query.low_mark = 1
354-        self.assertRaisesMessage(
355-            AssertionError,
356-            'Cannot change a query once a slice has been taken',
357-            q.extra, select={'is_recent': "pub_date > '2006-01-01'"}
358-        )
359-        self.assertQuerysetEqual(q.reverse(), [])
360-        self.assertQuerysetEqual(q.defer('spam', 'eggs'), [])
361-        self.assertQuerysetEqual(q.only('spam', 'eggs'), [])
362-
363     def test_ticket7791(self):
364         # There were "issues" when ordering and distinct-ing on fields related
365         # via ForeignKeys.
366@@ -1613,7 +1588,7 @@ class CloneTests(TestCase):
367 
368 class EmptyQuerySetTests(TestCase):
369     def test_emptyqueryset_values(self):
370-        # #14366 -- Calling .values() on an EmptyQuerySet and then cloning that
371+        # #14366 -- Calling .values() on an empty queryset and then cloning that
372         # should not cause an error"
373         self.assertQuerysetEqual(
374             Number.objects.none().values('num').order_by('num'), []