From eacf244506d8b7e6dd6483834ea122fec864da85 Mon Sep 17 00:00:00 2001 From: Bas Peschier Date: Wed, 27 Aug 2014 10:41:12 +0200 Subject: [PATCH] Converted sql_queries into a lazily evaluated list. Fixed #23364. Thanks Markush2010 for the report. --- django/core/context_processors.py | 8 +++-- docs/ref/templates/api.txt | 3 +- tests/context_processors/models.py | 5 ++++ .../templates/context_processors/debug.html | 15 ++++++++++ tests/context_processors/tests.py | 30 +++++++++++++++++++ tests/context_processors/urls.py | 1 + tests/context_processors/views.py | 13 +++++++- 7 files changed, 71 insertions(+), 4 deletions(-) create mode 100644 tests/context_processors/models.py create mode 100644 tests/context_processors/templates/context_processors/debug.html diff --git a/django/core/context_processors.py b/django/core/context_processors.py index ae67ec7211..e5d61fdb16 100644 --- a/django/core/context_processors.py +++ b/django/core/context_processors.py @@ -35,12 +35,16 @@ def csrf(request): def debug(request): - "Returns context variables helpful for debugging." + """ + Returns context variables helpful for debugging. + """ context_extras = {} if settings.DEBUG and request.META.get('REMOTE_ADDR') in settings.INTERNAL_IPS: context_extras['debug'] = True from django.db import connection - context_extras['sql_queries'] = connection.queries + # Return a lazy reference that computes connection.queries on access, + # to ensure it contains queries triggered after this function runs. + context_extras['sql_queries'] = lazy(lambda: connection.queries, list) return context_extras diff --git a/docs/ref/templates/api.txt b/docs/ref/templates/api.txt index 2c8abe22ae..a58b3d5060 100644 --- a/docs/ref/templates/api.txt +++ b/docs/ref/templates/api.txt @@ -555,7 +555,8 @@ If :setting:`TEMPLATE_CONTEXT_PROCESSORS` contains this processor, every you're in :setting:`DEBUG` mode. * ``sql_queries`` -- A list of ``{'sql': ..., 'time': ...}`` dictionaries, representing every SQL query that has happened so far during the request - and how long it took. The list is in order by query. + and how long it took. The list is in order by query and lazily generated + on access. django.core.context_processors.i18n ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/tests/context_processors/models.py b/tests/context_processors/models.py new file mode 100644 index 0000000000..cc1c8f766b --- /dev/null +++ b/tests/context_processors/models.py @@ -0,0 +1,5 @@ +from django.db import models + + +class DebugObject(models.Model): + pass diff --git a/tests/context_processors/templates/context_processors/debug.html b/tests/context_processors/templates/context_processors/debug.html new file mode 100644 index 0000000000..671ab81372 --- /dev/null +++ b/tests/context_processors/templates/context_processors/debug.html @@ -0,0 +1,15 @@ +{% if debug == True %} + +Have debug + +First query list: {{ sql_queries|length }} + +{% for obj in debug_objects.all %}{{ obj }}{% endfor %} + +Second query list: {{ sql_queries|length }} + +{% for obj in debug_objects.all %}{{ obj }}{% endfor %} + +Third query list: {{ sql_queries|length }} + +{% endif %} diff --git a/tests/context_processors/tests.py b/tests/context_processors/tests.py index 98342f28c2..44f36a4267 100644 --- a/tests/context_processors/tests.py +++ b/tests/context_processors/tests.py @@ -33,3 +33,33 @@ class RequestContextProcessorTests(TestCase): self.assertContains(response, url) response = self.client.post(url, {'path': '/blah/'}) self.assertContains(response, url) + + +@override_settings(ROOT_URLCONF='context_processors.urls', DEBUG=True, INTERNAL_IPS=('127.0.0.1',)) +class DebugContextProcessorTests(TestCase): + """ + Tests for the ``django.core.context_processors.debug`` processor. + """ + + def test_debug(self): + url = '/debug/' + # We should have the debug flag in the template. + response = self.client.get(url) + self.assertContains(response, 'Have debug') + + # And now we should not + with override_settings(DEBUG=False): + response = self.client.get(url) + self.assertNotContains(response, 'Have debug') + + def test_sql_queries(self): + """ + Test whether sql_queries represents the actual amount + of queries executed. (#23364) + """ + url = '/debug/' + response = self.client.get(url) + self.assertContains(response, 'First query list: 0') + self.assertContains(response, 'Second query list: 1') + # Check we have not actually memoized connection.queries + self.assertContains(response, 'Third query list: 2') diff --git a/tests/context_processors/urls.py b/tests/context_processors/urls.py index a7c0d1b4b0..f68720d581 100644 --- a/tests/context_processors/urls.py +++ b/tests/context_processors/urls.py @@ -5,4 +5,5 @@ from . import views urlpatterns = [ url(r'^request_attrs/$', views.request_processor), + url(r'^debug/$', views.debug_processor), ] diff --git a/tests/context_processors/views.py b/tests/context_processors/views.py index 66e7132c05..7a00edab0a 100644 --- a/tests/context_processors/views.py +++ b/tests/context_processors/views.py @@ -2,7 +2,18 @@ from django.core import context_processors from django.shortcuts import render_to_response from django.template.context import RequestContext +from .models import DebugObject + def request_processor(request): - return render_to_response('context_processors/request_attrs.html', + return render_to_response( + 'context_processors/request_attrs.html', RequestContext(request, {}, processors=[context_processors.request])) + + +def debug_processor(request): + return render_to_response( + 'context_processors/debug.html', + RequestContext(request, { + 'debug_objects': DebugObject.objects, + }, processors=[context_processors.debug]))