test_response.py 14.1 KB
Newer Older
1 2
from __future__ import unicode_literals

3 4
import pickle
import time
5
from datetime import datetime
6

7
from django.conf import settings
8
from django.template import Context, engines
9 10 11 12 13 14
from django.template.response import (
    ContentNotRenderedError, SimpleTemplateResponse, TemplateResponse,
)
from django.test import (
    RequestFactory, SimpleTestCase, ignore_warnings, override_settings,
)
15
from django.test.utils import require_jinja2
16
from django.utils.deprecation import RemovedInDjango110Warning
17

18 19
from .utils import TEMPLATE_DIR

Jason Myers's avatar
Jason Myers committed
20

21 22
def test_processor(request):
    return {'processors': 'yes'}
23
test_processor_name = 'template_tests.test_response.test_processor'
24

25 26 27 28

# A test middleware that installs a temporary URLConf
class CustomURLConfMiddleware(object):
    def process_request(self, request):
29
        request.urlconf = 'template_tests.alternate_urls'
30 31


32
class SimpleTemplateResponseTest(SimpleTestCase):
33 34

    def _response(self, template='foo', *args, **kwargs):
35 36
        template = engines['django'].from_string(template)
        return SimpleTemplateResponse(template, *args, **kwargs)
37 38 39 40

    def test_template_resolving(self):
        response = SimpleTemplateResponse('first/test.html')
        response.render()
41
        self.assertEqual(response.content, b'First template\n')
42 43 44 45

        templates = ['foo.html', 'second/test.html', 'first/test.html']
        response = SimpleTemplateResponse(templates)
        response.render()
46
        self.assertEqual(response.content, b'Second template\n')
47 48 49

        response = self._response()
        response.render()
50
        self.assertEqual(response.content, b'foo')
51 52 53 54 55 56 57 58 59 60 61

    def test_explicit_baking(self):
        # explicit baking
        response = self._response()
        self.assertFalse(response.is_rendered)
        response.render()
        self.assertTrue(response.is_rendered)

    def test_render(self):
        # response is not re-rendered without the render call
        response = self._response().render()
62
        self.assertEqual(response.content, b'foo')
63 64

        # rebaking doesn't change the rendered content
65 66
        template = engines['django'].from_string('bar{{ baz }}')
        response.template_name = template
67
        response.render()
68
        self.assertEqual(response.content, b'foo')
69 70 71 72

        # but rendered content can be overridden by manually
        # setting content
        response.content = 'bar'
73
        self.assertEqual(response.content, b'bar')
74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89

    def test_iteration_unrendered(self):
        # unrendered response raises an exception on iteration
        response = self._response()
        self.assertFalse(response.is_rendered)

        def iteration():
            for x in response:
                pass
        self.assertRaises(ContentNotRenderedError, iteration)
        self.assertFalse(response.is_rendered)

    def test_iteration_rendered(self):
        # iteration works for rendered responses
        response = self._response().render()
        res = [x for x in response]
90
        self.assertEqual(res, [b'foo'])
91 92 93 94 95 96 97 98 99 100 101

    def test_content_access_unrendered(self):
        # unrendered response raises an exception when content is accessed
        response = self._response()
        self.assertFalse(response.is_rendered)
        self.assertRaises(ContentNotRenderedError, lambda: response.content)
        self.assertFalse(response.is_rendered)

    def test_content_access_rendered(self):
        # rendered response content can be accessed
        response = self._response().render()
102
        self.assertEqual(response.content, b'foo')
103 104

    def test_set_content(self):
105
        # content can be overridden
106 107 108 109
        response = self._response()
        self.assertFalse(response.is_rendered)
        response.content = 'spam'
        self.assertTrue(response.is_rendered)
110
        self.assertEqual(response.content, b'spam')
111
        response.content = 'baz'
112
        self.assertEqual(response.content, b'baz')
113 114 115 116 117 118

    def test_dict_context(self):
        response = self._response('{{ foo }}{{ processors }}',
                                  {'foo': 'bar'})
        self.assertEqual(response.context_data, {'foo': 'bar'})
        response.render()
119
        self.assertEqual(response.content, b'bar')
120

121
    @ignore_warnings(category=RemovedInDjango110Warning)
122 123 124 125 126
    def test_context_instance(self):
        response = self._response('{{ foo }}{{ processors }}',
                                  Context({'foo': 'bar'}))
        self.assertEqual(response.context_data.__class__, Context)
        response.render()
127
        self.assertEqual(response.content, b'bar')
128 129

    def test_kwargs(self):
Jason Myers's avatar
Jason Myers committed
130
        response = self._response(content_type='application/json', status=504)
131 132 133 134 135 136 137 138
        self.assertEqual(response['content-type'], 'application/json')
        self.assertEqual(response.status_code, 504)

    def test_args(self):
        response = SimpleTemplateResponse('', {}, 'application/json', 504)
        self.assertEqual(response['content-type'], 'application/json')
        self.assertEqual(response.status_code, 504)

139 140 141 142 143 144 145 146 147
    @require_jinja2
    def test_using(self):
        response = SimpleTemplateResponse('template_tests/using.html').render()
        self.assertEqual(response.content, b'DTL\n')
        response = SimpleTemplateResponse('template_tests/using.html', using='django').render()
        self.assertEqual(response.content, b'DTL\n')
        response = SimpleTemplateResponse('template_tests/using.html', using='jinja2').render()
        self.assertEqual(response.content, b'Jinja2\n')

148 149 150 151 152 153
    def test_post_callbacks(self):
        "Rendering a template response triggers the post-render callbacks"
        post = []

        def post1(obj):
            post.append('post1')
154

155 156 157 158 159 160 161 162 163
        def post2(obj):
            post.append('post2')

        response = SimpleTemplateResponse('first/test.html', {})
        response.add_post_render_callback(post1)
        response.add_post_render_callback(post2)

        # When the content is rendered, all the callbacks are invoked, too.
        response.render()
164
        self.assertEqual(response.content, b'First template\n')
Alex Gaynor's avatar
Alex Gaynor committed
165
        self.assertEqual(post, ['post1', 'post2'])
166 167 168 169 170

    def test_pickling(self):
        # Create a template response. The context is
        # known to be unpickleable (e.g., a function).
        response = SimpleTemplateResponse('first/test.html', {
171 172
            'value': 123,
            'fn': datetime.now,
173
        })
174 175 176 177 178 179 180 181
        self.assertRaises(ContentNotRenderedError,
                          pickle.dumps, response)

        # But if we render the response, we can pickle it.
        response.render()
        pickled_response = pickle.dumps(response)
        unpickled_response = pickle.loads(pickled_response)

182 183 184
        self.assertEqual(unpickled_response.content, response.content)
        self.assertEqual(unpickled_response['content-type'], response['content-type'])
        self.assertEqual(unpickled_response.status_code, response.status_code)
185

186
        # ...and the unpickled response doesn't have the
187
        # template-related attributes, so it can't be re-rendered
188 189 190 191 192 193
        template_attrs = ('template_name', 'context_data', '_post_render_callbacks')
        for attr in template_attrs:
            self.assertFalse(hasattr(unpickled_response, attr))

        # ...and requesting any of those attributes raises an exception
        for attr in template_attrs:
194
            with self.assertRaises(AttributeError):
195 196 197 198
                getattr(unpickled_response, attr)

    def test_repickling(self):
        response = SimpleTemplateResponse('first/test.html', {
199 200
            'value': 123,
            'fn': datetime.now,
201
        })
202 203 204 205 206 207
        self.assertRaises(ContentNotRenderedError,
                          pickle.dumps, response)

        response.render()
        pickled_response = pickle.dumps(response)
        unpickled_response = pickle.loads(pickled_response)
208
        pickle.dumps(unpickled_response)
209

210 211
    def test_pickling_cookie(self):
        response = SimpleTemplateResponse('first/test.html', {
212 213
            'value': 123,
            'fn': datetime.now,
214
        })
215 216 217 218 219 220 221 222 223 224

        response.cookies['key'] = 'value'

        response.render()
        pickled_response = pickle.dumps(response, pickle.HIGHEST_PROTOCOL)
        unpickled_response = pickle.loads(pickled_response)

        self.assertEqual(unpickled_response.cookies['key'].value, 'value')


225 226
@override_settings(TEMPLATES=[{
    'BACKEND': 'django.template.backends.django.DjangoTemplates',
227
    'DIRS': [TEMPLATE_DIR],
228 229 230 231
    'OPTIONS': {
        'context_processors': [test_processor_name],
    },
}])
232
class TemplateResponseTest(SimpleTestCase):
233 234 235

    def setUp(self):
        self.factory = RequestFactory()
236 237

    def _response(self, template='foo', *args, **kwargs):
238 239 240
        self._request = self.factory.get('/')
        template = engines['django'].from_string(template)
        return TemplateResponse(self._request, template, *args, **kwargs)
241 242 243

    def test_render(self):
        response = self._response('{{ foo }}{{ processors }}').render()
244
        self.assertEqual(response.content, b'yes')
245 246 247 248

    def test_render_with_requestcontext(self):
        response = self._response('{{ foo }}{{ processors }}',
                                  {'foo': 'bar'}).render()
249
        self.assertEqual(response.content, b'baryes')
250

251
    @ignore_warnings(category=RemovedInDjango110Warning)
252 253 254
    def test_render_with_context(self):
        response = self._response('{{ foo }}{{ processors }}',
                                  Context({'foo': 'bar'})).render()
255
        self.assertEqual(response.content, b'bar')
256

257 258 259 260 261 262
    def test_context_processor_priority(self):
        # context processors should be overridden by passed-in context
        response = self._response('{{ foo }}{{ processors }}',
                                  {'processors': 'no'}).render()
        self.assertEqual(response.content, b'no')

263
    def test_kwargs(self):
Jason Myers's avatar
Jason Myers committed
264
        response = self._response(content_type='application/json',
265 266 267 268 269 270 271 272 273
                                  status=504)
        self.assertEqual(response['content-type'], 'application/json')
        self.assertEqual(response.status_code, 504)

    def test_args(self):
        response = TemplateResponse(self.factory.get('/'), '', {},
                                    'application/json', 504)
        self.assertEqual(response['content-type'], 'application/json')
        self.assertEqual(response.status_code, 504)
274

275 276 277 278 279 280 281 282 283 284
    @require_jinja2
    def test_using(self):
        request = self.factory.get('/')
        response = TemplateResponse(request, 'template_tests/using.html').render()
        self.assertEqual(response.content, b'DTL\n')
        response = TemplateResponse(request, 'template_tests/using.html', using='django').render()
        self.assertEqual(response.content, b'DTL\n')
        response = TemplateResponse(request, 'template_tests/using.html', using='jinja2').render()
        self.assertEqual(response.content, b'Jinja2\n')

285
    @ignore_warnings(category=RemovedInDjango110Warning)
286
    def test_custom_app(self):
287 288
        self._response('{{ foo }}', current_app="foobar")
        self.assertEqual(self._request.current_app, 'foobar')
289

290 291 292 293 294 295 296
    def test_pickling(self):
        # Create a template response. The context is
        # known to be unpickleable (e.g., a function).
        response = TemplateResponse(self.factory.get('/'),
            'first/test.html', {
                'value': 123,
                'fn': datetime.now,
297 298
            }
        )
299 300 301 302 303 304 305 306
        self.assertRaises(ContentNotRenderedError,
                          pickle.dumps, response)

        # But if we render the response, we can pickle it.
        response.render()
        pickled_response = pickle.dumps(response)
        unpickled_response = pickle.loads(pickled_response)

307 308 309
        self.assertEqual(unpickled_response.content, response.content)
        self.assertEqual(unpickled_response['content-type'], response['content-type'])
        self.assertEqual(unpickled_response.status_code, response.status_code)
310

311
        # ...and the unpickled response doesn't have the
312
        # template-related attributes, so it can't be re-rendered
313 314 315 316 317 318 319
        template_attrs = ('template_name', 'context_data',
            '_post_render_callbacks', '_request', '_current_app')
        for attr in template_attrs:
            self.assertFalse(hasattr(unpickled_response, attr))

        # ...and requesting any of those attributes raises an exception
        for attr in template_attrs:
320
            with self.assertRaises(AttributeError):
321 322 323 324
                getattr(unpickled_response, attr)

    def test_repickling(self):
        response = SimpleTemplateResponse('first/test.html', {
325 326
            'value': 123,
            'fn': datetime.now,
327
        })
328 329 330 331 332 333
        self.assertRaises(ContentNotRenderedError,
                          pickle.dumps, response)

        response.render()
        pickled_response = pickle.dumps(response)
        unpickled_response = pickle.loads(pickled_response)
334
        pickle.dumps(unpickled_response)
335

336

337
@override_settings(
338
    MIDDLEWARE_CLASSES=settings.MIDDLEWARE_CLASSES + [
339
        'template_tests.test_response.CustomURLConfMiddleware'
340 341
    ],
    ROOT_URLCONF='template_tests.urls',
342
)
343
class CustomURLConfTest(SimpleTestCase):
344 345 346 347

    def test_custom_urlconf(self):
        response = self.client.get('/template_response_view/')
        self.assertEqual(response.status_code, 200)
348 349 350
        self.assertContains(response, 'This is where you can find the snark: /snark/')


351 352
@override_settings(
    CACHE_MIDDLEWARE_SECONDS=2.0,
353
    MIDDLEWARE_CLASSES=settings.MIDDLEWARE_CLASSES + [
354 355
        'django.middleware.cache.FetchFromCacheMiddleware',
        'django.middleware.cache.UpdateCacheMiddleware',
356 357
    ],
    ROOT_URLCONF='template_tests.alternate_urls',
358
)
359
class CacheMiddlewareTest(SimpleTestCase):
360 361 362 363 364 365 366 367 368 369 370 371 372

    def test_middleware_caching(self):
        response = self.client.get('/template_response_view/')
        self.assertEqual(response.status_code, 200)

        time.sleep(1.0)

        response2 = self.client.get('/template_response_view/')
        self.assertEqual(response2.status_code, 200)

        self.assertEqual(response.content, response2.content)

        time.sleep(2.0)
373

374 375 376
        # Let the cache expire and test again
        response2 = self.client.get('/template_response_view/')
        self.assertEqual(response2.status_code, 200)
377

378
        self.assertNotEqual(response.content, response2.content)