1
0
mirror of https://github.com/django/django.git synced 2025-06-11 06:29:13 +00:00

[1.11.x] Fixed #28817 -- Made QuerySet.iterator() use server-side cursors after values() and values_list().

Backport of d97f026a7ab5212192426e45121f7a52751a2044 from master
This commit is contained in:
Dražen Odobašić 2017-11-19 10:13:10 -05:00 committed by Tim Graham
parent 90c120c2b7
commit 7f4e174511
4 changed files with 23 additions and 7 deletions

View File

@ -103,7 +103,7 @@ class ValuesIterable(BaseIterable):
# extra(select=...) cols are always at the start of the row. # extra(select=...) cols are always at the start of the row.
names = extra_names + field_names + annotation_names names = extra_names + field_names + annotation_names
for row in compiler.results_iter(): for row in compiler.results_iter(chunked_fetch=self.chunked_fetch):
yield dict(zip(names, row)) yield dict(zip(names, row))
@ -119,7 +119,7 @@ class ValuesListIterable(BaseIterable):
compiler = query.get_compiler(queryset.db) compiler = query.get_compiler(queryset.db)
if not query.extra_select and not query.annotation_select: if not query.extra_select and not query.annotation_select:
for row in compiler.results_iter(): for row in compiler.results_iter(chunked_fetch=self.chunked_fetch):
yield tuple(row) yield tuple(row)
else: else:
field_names = list(query.values_select) field_names = list(query.values_select)
@ -135,7 +135,7 @@ class ValuesListIterable(BaseIterable):
else: else:
fields = names fields = names
for row in compiler.results_iter(): for row in compiler.results_iter(chunked_fetch=self.chunked_fetch):
data = dict(zip(names, row)) data = dict(zip(names, row))
yield tuple(data[f] for f in fields) yield tuple(data[f] for f in fields)
@ -149,7 +149,7 @@ class FlatValuesListIterable(BaseIterable):
def __iter__(self): def __iter__(self):
queryset = self.queryset queryset = self.queryset
compiler = queryset.query.get_compiler(queryset.db) compiler = queryset.query.get_compiler(queryset.db)
for row in compiler.results_iter(): for row in compiler.results_iter(chunked_fetch=self.chunked_fetch):
yield row[0] yield row[0]

View File

@ -833,12 +833,12 @@ class SQLCompiler(object):
row[pos] = value row[pos] = value
return tuple(row) return tuple(row)
def results_iter(self, results=None): def results_iter(self, results=None, chunked_fetch=False):
""" """
Returns an iterator over the results from executing this query. Returns an iterator over the results from executing this query.
""" """
if results is None: if results is None:
results = self.execute_sql(MULTI) results = self.execute_sql(MULTI, chunked_fetch=chunked_fetch)
fields = [s[0] for s in self.select[0:self.col_count]] fields = [s[0] for s in self.select[0:self.col_count]]
converters = self.get_converters(fields) converters = self.get_converters(fields)
for rows in results: for rows in results:

View File

@ -18,3 +18,6 @@ Bugfixes
* Fixed incorrect index name truncation when using a namespaced ``db_table`` * Fixed incorrect index name truncation when using a namespaced ``db_table``
(:ticket:`28792`). (:ticket:`28792`).
* Made ``QuerySet.iterator()`` use server-side cursors on PostgreSQL after
``values()`` and ``values_list()`` (:ticket:`28817`).

View File

@ -3,7 +3,7 @@ import unittest
from collections import namedtuple from collections import namedtuple
from contextlib import contextmanager from contextlib import contextmanager
from django.db import connection from django.db import connection, models
from django.test import TestCase from django.test import TestCase
from .models import Person from .models import Person
@ -53,6 +53,19 @@ class ServerSideCursorsPostgres(TestCase):
def test_server_side_cursor(self): def test_server_side_cursor(self):
self.assertUsesCursor(Person.objects.iterator()) self.assertUsesCursor(Person.objects.iterator())
def test_values(self):
self.assertUsesCursor(Person.objects.values('first_name').iterator())
def test_values_list(self):
self.assertUsesCursor(Person.objects.values_list('first_name').iterator())
def test_values_list_flat(self):
self.assertUsesCursor(Person.objects.values_list('first_name', flat=True).iterator())
def test_values_list_fields_not_equal_to_names(self):
expr = models.Count('id')
self.assertUsesCursor(Person.objects.annotate(id__count1=expr).values_list(expr, 'id__count1').iterator())
def test_server_side_cursor_many_cursors(self): def test_server_side_cursor_many_cursors(self):
persons = Person.objects.iterator() persons = Person.objects.iterator()
persons2 = Person.objects.iterator() persons2 = Person.objects.iterator()