summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAdnan Umer <u.adnan@outlook.com>2018-04-18 22:25:59 +0500
committerTim Graham <timograham@gmail.com>2018-04-19 11:35:49 -0400
commitec0319ff821492783f555917b61bceacddb77571 (patch)
treec6622fee55ea142838285db8b185e4ea079c7cd8
parentc1c163b42717ed5e051098ebf0e2f5c77810f20e (diff)
Fixed #29339 -- Added result caching to RawQuerySet.
-rw-r--r--django/db/models/query.py9
-rw-r--r--docs/releases/2.1.txt3
-rw-r--r--docs/topics/db/sql.txt5
-rw-r--r--tests/raw_query/tests.py12
4 files changed, 25 insertions, 4 deletions
diff --git a/django/db/models/query.py b/django/db/models/query.py
index 1c6c3aae35..e1f3ce8adf 100644
--- a/django/db/models/query.py
+++ b/django/db/models/query.py
@@ -1277,6 +1277,7 @@ class RawQuerySet:
self.query = query or sql.RawQuery(sql=raw_query, using=self.db, params=params)
self.params = params or ()
self.translations = translations or {}
+ self._result_cache = None
def resolve_model_init_order(self):
"""Resolve the init field names and value positions."""
@@ -1288,7 +1289,15 @@ class RawQuerySet:
model_init_names = [f.attname for f in model_init_fields]
return model_init_names, model_init_order, annotation_fields
+ def _fetch_all(self):
+ if self._result_cache is None:
+ self._result_cache = list(self.iterator())
+
def __iter__(self):
+ self._fetch_all()
+ return iter(self._result_cache)
+
+ def iterator(self):
# Cache some things for performance reasons outside the loop.
db = self.db
compiler = connections[db].ops.compiler('SQLCompiler')(
diff --git a/docs/releases/2.1.txt b/docs/releases/2.1.txt
index 7b65b90b9c..f3a99e5fc0 100644
--- a/docs/releases/2.1.txt
+++ b/docs/releases/2.1.txt
@@ -401,6 +401,9 @@ Miscellaneous
* The admin CSS class ``field-box`` is renamed to ``fieldBox`` to prevent
conflicts with the class given to model fields named "box".
+* ``QuerySet.raw()`` now caches its results like regular querysets. Use
+ ``iterator()`` if you don't want caching.
+
.. _deprecated-features-2.1:
Features deprecated in 2.1
diff --git a/docs/topics/db/sql.txt b/docs/topics/db/sql.txt
index 96d79a999c..cff628011e 100644
--- a/docs/topics/db/sql.txt
+++ b/docs/topics/db/sql.txt
@@ -91,10 +91,7 @@ options that make it very powerful.
:class:`~django.db.models.query.QuerySet`, ``RawQuerySet`` doesn't
implement all methods you can use with ``QuerySet``. For example,
``__bool__()`` and ``__len__()`` are not defined in ``RawQuerySet``, and
- thus all ``RawQuerySet`` instances are considered ``True``. The reason
- these methods are not implemented in ``RawQuerySet`` is that implementing
- them without internal caching would be a performance drawback and adding
- such caching would be backward incompatible.
+ thus all ``RawQuerySet`` instances are considered ``True``.
Mapping query fields to model fields
------------------------------------
diff --git a/tests/raw_query/tests.py b/tests/raw_query/tests.py
index bcc075ea35..1f0d0c363f 100644
--- a/tests/raw_query/tests.py
+++ b/tests/raw_query/tests.py
@@ -318,3 +318,15 @@ class RawQueryTests(TestCase):
c = Coffee.objects.create(brand='starbucks', price=20.5)
qs = Coffee.objects.raw("SELECT * FROM raw_query_coffee WHERE price >= %s", params=[Decimal(20)])
self.assertEqual(list(qs), [c])
+
+ def test_result_caching(self):
+ with self.assertNumQueries(1):
+ books = Book.objects.raw('SELECT * FROM raw_query_book')
+ list(books)
+ list(books)
+
+ def test_iterator(self):
+ with self.assertNumQueries(2):
+ books = Book.objects.raw('SELECT * FROM raw_query_book')
+ list(books.iterator())
+ list(books.iterator())