Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse code

Fixed #12226 -- Deprecated test client Response.template attribute in…

… favor of templates attribute, which is always a list. Thanks Russell for patch review.

git-svn-id: http://code.djangoproject.com/svn/django/trunk@14106 bcc190cf-cafb-0310-a4f2-bffc1f526a37
  • Loading branch information...
commit 501546df6f4cbb56ea94da073e8206804fef3040 1 parent d084439
Carl Meyer authored October 10, 2010
32  django/test/client.py
@@ -4,6 +4,7 @@
4 4
 import os
5 5
 import re
6 6
 import mimetypes
  7
+import warnings
7 8
 try:
8 9
     from cStringIO import StringIO
9 10
 except ImportError:
@@ -93,7 +94,7 @@ def store_rendered_templates(store, signal, sender, template, context, **kwargs)
93 94
     """
94 95
     Stores templates and contexts that are rendered.
95 96
     """
96  
-    store.setdefault('template', []).append(template)
  97
+    store.setdefault('templates', []).append(template)
97 98
     store.setdefault('context', ContextList()).append(context)
98 99
 
99 100
 def encode_multipart(boundary, data):
@@ -260,16 +261,25 @@ def request(self, **request):
260 261
             response.request = request
261 262
 
262 263
             # Add any rendered template detail to the response.
263  
-            # If there was only one template rendered (the most likely case),
264  
-            # flatten the list to a single element.
265  
-            for detail in ('template', 'context'):
266  
-                if data.get(detail):
267  
-                    if len(data[detail]) == 1:
268  
-                        setattr(response, detail, data[detail][0]);
269  
-                    else:
270  
-                        setattr(response, detail, data[detail])
271  
-                else:
272  
-                    setattr(response, detail, None)
  264
+            response.templates = data.get("templates", [])
  265
+            response.context = data.get("context")
  266
+
  267
+            # Flatten a single context. Not really necessary anymore thanks to
  268
+            # the __getattr__ flattening in ContextList, but has some edge-case
  269
+            # backwards-compatibility implications.
  270
+            if response.context and len(response.context) == 1:
  271
+                response.context = response.context[0]
  272
+
  273
+            # Provide a backwards-compatible (but pending deprecation) response.template
  274
+            def _get_template(self):
  275
+                warnings.warn("response.template is deprecated; use response.templates instead (which is always a list)",
  276
+                              PendingDeprecationWarning)
  277
+                if not self.templates:
  278
+                    return None
  279
+                elif len(self.templates) == 1:
  280
+                    return self.templates[0]
  281
+                return self.templates
  282
+            response.__class__.template = property(_get_template)
273 283
 
274 284
             # Update persistent cookie data.
275 285
             if response.cookies:
4  django/test/testcases.py
@@ -443,7 +443,7 @@ def assertTemplateUsed(self, response, template_name, msg_prefix=''):
443 443
         if msg_prefix:
444 444
             msg_prefix += ": "
445 445
 
446  
-        template_names = [t.name for t in to_list(response.template)]
  446
+        template_names = [t.name for t in response.templates]
447 447
         if not template_names:
448 448
             self.fail(msg_prefix + "No templates used to render the response")
449 449
         self.failUnless(template_name in template_names,
@@ -459,7 +459,7 @@ def assertTemplateNotUsed(self, response, template_name, msg_prefix=''):
459 459
         if msg_prefix:
460 460
             msg_prefix += ": "
461 461
 
462  
-        template_names = [t.name for t in to_list(response.template)]
  462
+        template_names = [t.name for t in response.templates]
463 463
         self.failIf(template_name in template_names,
464 464
             msg_prefix + "Template '%s' was used unexpectedly in rendering"
465 465
             " the response" % template_name)
6  docs/internals/deprecation.txt
@@ -102,6 +102,12 @@ their deprecation, as per the :ref:`Django deprecation policy
102 102
         * The ``mod_python`` request handler has been deprecated since the 1.3
103 103
           release. The ``mod_wsgi`` handler should be used instead.
104 104
 
  105
+        * The ``template`` attribute on :class:`~django.test.client.Response`
  106
+          objects returned by the :ref:`test client <test-client>` has been
  107
+          deprecated since the 1.3 release. The
  108
+          :attr:`~django.test.client.Response.templates` attribute should be
  109
+          used instead.
  110
+
105 111
     * 2.0
106 112
         * ``django.views.defaults.shortcut()``. This function has been moved
107 113
           to ``django.contrib.contenttypes.views.shortcut()`` as part of the
17  docs/releases/1.3.txt
@@ -106,6 +106,23 @@ If you are currently using the ``mod_python`` request handler, it is strongly
106 106
 encouraged you redeploy your Django instances using :doc:`mod_wsgi
107 107
 </howto/deployment/modwsgi>`.
108 108
 
  109
+Test client response ``template`` attribute
  110
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  111
+
  112
+Django's :ref:`test client <test-client>` returns
  113
+:class:`~django.test.client.Response` objects annotated with extra testing
  114
+information. In Django versions prior to 1.3, this included a
  115
+:attr:`~django.test.client.Response.template` attribute containing information
  116
+about templates rendered in generating the response: either None, a single
  117
+:class:`~django.template.Template` object, or a list of
  118
+:class:`~django.template.Template` objects. This inconsistency in return values
  119
+(sometimes a list, sometimes not) made the attribute difficult to work with.
  120
+
  121
+In Django 1.3 the :attr:`~django.test.client.Response.template` attribute is
  122
+deprecated in favor of a new :attr:`~django.test.client.Response.templates`
  123
+attribute, which is always a list, even if it has only a single element or no
  124
+elements.
  125
+
109 126
 What's new in Django 1.3
110 127
 ========================
111 128
 
16  docs/topics/testing.txt
@@ -494,6 +494,8 @@ Testing tools
494 494
 
495 495
 Django provides a small set of tools that come in handy when writing tests.
496 496
 
  497
+.. _test-client:
  498
+
497 499
 The test client
498 500
 ---------------
499 501
 
@@ -894,15 +896,15 @@ Specifically, a ``Response`` object has the following attributes:
894 896
         The HTTP status of the response, as an integer. See RFC2616_ for a full
895 897
         list of HTTP status codes.
896 898
 
897  
-    .. attribute:: template
  899
+    .. versionadded:: 1.3
  900
+
  901
+    .. attribute:: templates
898 902
 
899  
-        The ``Template`` instance that was used to render the final content. Use
  903
+        A list of ``Template`` instances used to render the final content, in
  904
+        the order they were rendered. For each template in the list, use
900 905
         ``template.name`` to get the template's file name, if the template was
901  
-        loaded from a file. (The name is a string such as ``'admin/index.html'``.)
902  
-
903  
-        If the rendered page used multiple templates -- e.g., using :ref:`template
904  
-        inheritance<template-inheritance>` -- then ``template`` will be a list of
905  
-        ``Template`` instances, in the order in which they were rendered.
  906
+        loaded from a file. (The name is a string such as
  907
+        ``'admin/index.html'``.)
906 908
 
907 909
 You can also use dictionary syntax on the response object to query the value
908 910
 of any settings in the HTTP headers. For example, you could determine the
10  tests/modeltests/test_client/models.py
@@ -37,7 +37,7 @@ def test_get_view(self):
37 37
         # Check some response details
38 38
         self.assertContains(response, 'This is a test')
39 39
         self.assertEqual(response.context['var'], u'\xf2')
40  
-        self.assertEqual(response.template.name, 'GET Template')
  40
+        self.assertEqual(response.templates[0].name, 'GET Template')
41 41
 
42 42
     def test_get_post_view(self):
43 43
         "GET a view that normally expects POSTs"
@@ -45,7 +45,7 @@ def test_get_post_view(self):
45 45
 
46 46
         # Check some response details
47 47
         self.assertEqual(response.status_code, 200)
48  
-        self.assertEqual(response.template.name, 'Empty GET Template')
  48
+        self.assertEqual(response.templates[0].name, 'Empty GET Template')
49 49
         self.assertTemplateUsed(response, 'Empty GET Template')
50 50
         self.assertTemplateNotUsed(response, 'Empty POST Template')
51 51
 
@@ -55,7 +55,7 @@ def test_empty_post(self):
55 55
 
56 56
         # Check some response details
57 57
         self.assertEqual(response.status_code, 200)
58  
-        self.assertEqual(response.template.name, 'Empty POST Template')
  58
+        self.assertEqual(response.templates[0].name, 'Empty POST Template')
59 59
         self.assertTemplateNotUsed(response, 'Empty GET Template')
60 60
         self.assertTemplateUsed(response, 'Empty POST Template')
61 61
 
@@ -69,7 +69,7 @@ def test_post(self):
69 69
         # Check some response details
70 70
         self.assertEqual(response.status_code, 200)
71 71
         self.assertEqual(response.context['data'], '37')
72  
-        self.assertEqual(response.template.name, 'POST Template')
  72
+        self.assertEqual(response.templates[0].name, 'POST Template')
73 73
         self.failUnless('Data received' in response.content)
74 74
 
75 75
     def test_response_headers(self):
@@ -84,7 +84,7 @@ def test_raw_post(self):
84 84
         response = self.client.post("/test_client/raw_post_view/", test_doc,
85 85
                                     content_type="text/xml")
86 86
         self.assertEqual(response.status_code, 200)
87  
-        self.assertEqual(response.template.name, "Book template")
  87
+        self.assertEqual(response.templates[0].name, "Book template")
88 88
         self.assertEqual(response.content, "Blink - Malcolm Gladwell")
89 89
 
90 90
     def test_redirect(self):
17  tests/regressiontests/test_client_regress/models.py
@@ -9,7 +9,7 @@
9 9
 from django.test.utils import ContextList
10 10
 from django.core.urlresolvers import reverse
11 11
 from django.core.exceptions import SuspiciousOperation
12  
-from django.template import TemplateDoesNotExist, TemplateSyntaxError, Context
  12
+from django.template import TemplateDoesNotExist, TemplateSyntaxError, Context, Template
13 13
 from django.template import loader
14 14
 from django.test.client import encode_file
15 15
 
@@ -861,3 +861,18 @@ def test_client_headers_redirect(self):
861 861
         self.assertEquals(response.content, "HTTP_X_ARG_CHECK: Testing 123")
862 862
         self.assertRedirects(response, '/test_client_regress/check_headers/',
863 863
             status_code=301, target_status_code=200)
  864
+
  865
+class ResponseTemplateDeprecationTests(TestCase):
  866
+    """
  867
+    Response.template still works backwards-compatibly, but with pending deprecation warning. Refs #12226.
  868
+
  869
+    """
  870
+    def test_response_template_data(self):
  871
+        response = self.client.get("/test_client_regress/request_data/", data={'foo':'whiz'})
  872
+        self.assertEqual(response.template.__class__, Template)
  873
+        self.assertEqual(response.template.name, 'base.html')
  874
+
  875
+    def test_response_no_template(self):
  876
+        response = self.client.get("/test_client_regress/request_methods/")
  877
+        self.assertEqual(response.template, None)
  878
+

0 notes on commit 501546d

Please sign in to comment.
Something went wrong with that request. Please try again.