Ticket #12816: render_shortcut.4.diff

File render_shortcut.4.diff, 14.8 KB (added by kmike, 5 years ago)

More tests fixes. Sorry for patch spam.

  • django/shortcuts/__init__.py

    diff -r c67d03486d14 django/shortcuts/__init__.py
    a b  
    55"""
    66
    77from django.template import loader
     8from django.template.response import TemplateResponse
    89from django.http import HttpResponse, Http404
    910from django.http import HttpResponseRedirect, HttpResponsePermanentRedirect
    1011from django.db.models.manager import Manager
     
    2324    """
    2425    Returns an HttpResponseRedirect to the apropriate URL for the arguments
    2526    passed.
    26    
     27
    2728    The arguments could be:
    28    
     29
    2930        * A model: the model's `get_absolute_url()` function will be called.
    30    
     31
    3132        * A view name, possibly with arguments: `urlresolvers.reverse()` will
    3233          be used to reverse-resolve the name.
    33          
     34
    3435        * A URL, which will be used as-is for the redirect location.
    35        
     36
    3637    By default issues a temporary redirect; pass permanent=True to issue a
    3738    permanent redirect
    3839    """
     
    4041        redirect_class = HttpResponsePermanentRedirect
    4142    else:
    4243        redirect_class = HttpResponseRedirect
    43    
     44
    4445    # If it's a model, use get_absolute_url()
    4546    if hasattr(to, 'get_absolute_url'):
    4647        return redirect_class(to.get_absolute_url())
    47    
     48
    4849    # Next try a reverse URL resolution.
    4950    try:
    5051        return redirect_class(urlresolvers.reverse(to, args=args, kwargs=kwargs))
     
    5556        # If this doesn't "feel" like a URL, re-raise.
    5657        if '/' not in to and '.' not in to:
    5758            raise
    58        
     59
    5960    # Finally, fall back and assume it's a URL
    6061    return redirect_class(to)
    6162
     
    101102    obj_list = list(queryset.filter(*args, **kwargs))
    102103    if not obj_list:
    103104        raise Http404('No %s matches the given query.' % queryset.model._meta.object_name)
    104     return obj_list
    105  No newline at end of file
     105    return obj_list
     106
     107render = TemplateResponse
  • new file django/template/response.py

    diff -r c67d03486d14 django/template/response.py
    - +  
     1from django.http import HttpResponse
     2from django.template import loader, Context, RequestContext
     3
     4class SimpleTemplateResponse(HttpResponse):
     5
     6    def __init__(self, template, context=None, mimetype=None, status=None,
     7            content_type=None):
     8        # These two properties were originally called 'template' and 'context'
     9        # but django.test.client.Client was clobbering those leading to really
     10        # tricky-to-debug problems
     11        self.template_name = template
     12        self.template_context = context
     13        self.baked = False
     14
     15        # content argument doesn't make sense here because it will be replaced
     16        # with rendered template so we always pass empty string in order to
     17        # prevent errors and provide shorter signature.
     18        super(SimpleTemplateResponse, self).__init__('', mimetype, status,
     19                                                     content_type)
     20
     21    def resolve_template(self, template):
     22        "Accepts a template object, path-to-template or list of paths"
     23        if isinstance(template, (list, tuple)):
     24            return loader.select_template(template)
     25        elif isinstance(template, basestring):
     26            return loader.get_template(template)
     27        else:
     28            return template
     29
     30    def resolve_context(self, context):
     31        "context can be a dictionary or a context object"
     32        if isinstance(context, Context):
     33            return context
     34        else:
     35            return Context(context)
     36
     37    def render(self):
     38        template = self.resolve_template(self.template_name)
     39        context = self.resolve_context(self.template_context)
     40        content = template.render(context)
     41        return content
     42
     43    def bake(self):
     44        """
     45        The template is baked the first time you try to access
     46        response.content or iterate over it. This is a bit ugly, but is
     47        necessary because Django middleware sometimes expects to be able to
     48        over-write the content of a response.
     49        """
     50        if not self.baked:
     51            self.force_bake()
     52
     53    def force_bake(self):
     54        "Call this if you have modified the template or context but are "
     55        "unsure if the template has already been baked."
     56        self._set_content(self.render())
     57
     58    def __iter__(self):
     59        self.bake()
     60        return super(SimpleTemplateResponse, self).__iter__()
     61
     62    def _get_content(self):
     63        self.bake()
     64        return super(SimpleTemplateResponse, self)._get_content()
     65
     66    def _set_content(self, value):
     67        "Overrides rendered content, unless you later call force_bake()"
     68        super(SimpleTemplateResponse, self)._set_content(value)
     69        self.baked = True
     70
     71    content = property(_get_content, _set_content)
     72
     73class TemplateResponse(SimpleTemplateResponse):
     74
     75    def __init__(self, request, template, context=None, mimetype=None,
     76            status=None, content_type=None):
     77        # self.request gets over-written by django.test.client.Client - and
     78        # unlike template_context and template_name the _request should not
     79        # be considered part of the public API.
     80        self._request = request
     81        super(TemplateResponse, self).__init__(
     82            template, context, mimetype, status, content_type)
     83
     84    def resolve_context(self, context):
     85        if isinstance(context, Context):
     86            return context
     87        else:
     88            return RequestContext(self._request, context)
     89
  • new file tests/regressiontests/templates/response.py

    diff -r c67d03486d14 tests/regressiontests/templates/response.py
    - +  
     1import os
     2from django.utils import unittest
     3from django.test import RequestFactory
     4from django.conf import settings
     5from django.template import Template, Context, RequestContext
     6from django.template.response import TemplateResponse, SimpleTemplateResponse
     7import django.template.context
     8
     9def test_processor(request):
     10    return {'processors': 'yes'}
     11test_processor_name = 'regressiontests.templates.response.test_processor'
     12
     13class BaseTemplateResponseTest(unittest.TestCase):
     14    # tests rely on fact that global context
     15    # processors should only work when RequestContext is used.
     16
     17    def setUp(self):
     18        self.factory = RequestFactory()
     19
     20        cp = list(settings.TEMPLATE_CONTEXT_PROCESSORS)
     21        self._old_processors = cp[:]
     22        self._old_TEMPLATE_DIRS = settings.TEMPLATE_DIRS
     23        settings.TEMPLATE_CONTEXT_PROCESSORS = cp + [test_processor_name]
     24        settings.TEMPLATE_DIRS = (
     25            os.path.join(
     26                os.path.dirname(__file__),
     27                'templates'
     28            ),
     29        )
     30
     31    def tearDown(self):
     32        settings.TEMPLATE_DIRS = self._old_TEMPLATE_DIRS
     33        settings.TEMPLATE_CONTEXT_PROCESSORS = tuple(self._old_processors)
     34        # Force re-evaluation of the contex processor list
     35        django.template.context._standard_context_processors = None
     36
     37
     38class SimpleTemplateResponseTest(BaseTemplateResponseTest):
     39
     40    def _response(self, template='foo', *args, **kwargs):
     41        return SimpleTemplateResponse(Template(template), *args, **kwargs)
     42
     43    def test_template_resolving(self):
     44        response = SimpleTemplateResponse('first/test.html')
     45        self.assertEqual('First template\n', response.content)
     46
     47        templates = ['foo.html', 'second/test.html', 'first/test.html']
     48        response = SimpleTemplateResponse(templates)
     49        self.assertEqual('Second template\n', response.content)
     50
     51        response = self._response()
     52        self.assertEqual(response.content, 'foo')
     53
     54    def test_explicit_baking(self):
     55        # explicit baking
     56        response = self._response()
     57        self.assertFalse(response.baked)
     58        response.bake()
     59        self.assertTrue(response.baked)
     60
     61    def test_force_bake(self):
     62        # response is not re-baked without the force_bake call
     63        response = self._response()
     64        self.assertFalse(response.baked)
     65        self.assertEqual(response.content, 'foo')
     66
     67        response.template_name = Template('bar{{ baz }}')
     68        self.assertEqual(response.content, 'foo')
     69        response.bake()
     70        self.assertEqual(response.content, 'foo')
     71        response.force_bake()
     72        self.assertEqual(response.content, 'bar')
     73
     74        response.template_context = {'baz': 'baz'}
     75        self.assertEqual(response.content, 'bar')
     76        response.bake()
     77        self.assertEqual(response.content, 'bar')
     78        response.force_bake()
     79        self.assertEqual(response.content, 'barbaz')
     80
     81        response.template_context = {'baz': 'spam'}
     82        for x in response:
     83            pass
     84        self.assertEqual(response.content, 'barbaz')
     85
     86    def test_baking_on_iteration(self):
     87        # response becomes baked after first iteration
     88        response = self._response()
     89        self.assertFalse(response.baked)
     90        for x in response:
     91            pass
     92        self.assertTrue(response.baked)
     93
     94    def test_baking_on_access(self):
     95        # the response is baked when content is accessed
     96        response = self._response()
     97        self.assertFalse(response.baked)
     98        content = response.content
     99        self.assertTrue(response.baked)
     100        self.assertEqual(content, 'foo')
     101
     102    def test_set_content(self):
     103        # content can be overriden
     104        response = self._response()
     105        self.assertFalse(response.baked)
     106        response.content = 'spam'
     107        self.assertTrue(response.baked)
     108        self.assertEqual(response.content, 'spam')
     109        response.content = 'baz'
     110        self.assertEqual(response.content, 'baz')
     111
     112    def test_dict_context(self):
     113        response = self._response('{{ foo }}{{ processors }}',
     114                                  {'foo': 'bar'})
     115        self.assertEqual(response.template_context, {'foo': 'bar'})
     116        self.assertEqual(response.content, 'bar')
     117
     118    def test_context_instance(self):
     119        response = self._response('{{ foo }}{{ processors }}',
     120                                  Context({'foo': 'bar'}))
     121        self.assertEqual(response.template_context.__class__, Context)
     122        self.assertEqual(response.content, 'bar')
     123
     124    def test_kwargs(self):
     125        response = self._response(content_type = 'application/json', status=504)
     126        self.assertEqual(response['content-type'], 'application/json')
     127        self.assertEqual(response.status_code, 504)
     128
     129    def test_args(self):
     130        response = SimpleTemplateResponse('', {}, 'application/json', 504)
     131        self.assertEqual(response['content-type'], 'application/json')
     132        self.assertEqual(response.status_code, 504)
     133
     134
     135class TemplateResponseTest(BaseTemplateResponseTest):
     136
     137    def _response(self, template='foo', *args, **kwargs):
     138        return TemplateResponse(self.factory.get('/'), Template(template),
     139                                *args, **kwargs)
     140
     141    def test_render(self):
     142        response = self._response('{{ foo }}{{ processors }}')
     143        self.assertEqual(response.content, 'yes')
     144
     145    def test_render_with_requestcontext(self):
     146        response = self._response('{{ foo }}{{ processors }}',
     147                                  {'foo': 'bar'})
     148        self.assertEqual(response.content, 'baryes')
     149
     150    def test_render_with_context(self):
     151        response = self._response('{{ foo }}{{ processors }}',
     152                                  Context({'foo': 'bar'}))
     153        self.assertEqual(response.content, 'bar')
     154
     155    def test_kwargs(self):
     156        response = self._response(content_type = 'application/json', status=504)
     157        self.assertEqual(response['content-type'], 'application/json')
     158        self.assertEqual(response.status_code, 504)
     159
     160    def test_args(self):
     161        response = TemplateResponse(self.factory.get('/'), '', {},
     162                                    'application/json', 504)
     163        self.assertEqual(response['content-type'], 'application/json')
     164        self.assertEqual(response.status_code, 504)
  • tests/regressiontests/templates/tests.py

    diff -r c67d03486d14 tests/regressiontests/templates/tests.py
    a b  
    2727from unicode import unicode_tests
    2828from nodelist import NodelistTest
    2929from smartif import *
     30from response import *
    3031
    3132try:
    3233    from loaders import *
  • new file tests/regressiontests/views/templates/debug/render_test.html

    diff -r c67d03486d14 tests/regressiontests/views/templates/debug/render_test.html
    - +  
     1{{ foo }}.{{ bar }}.{{ baz }}.{{ processors }}
  • tests/regressiontests/views/tests/__init__.py

    diff -r c67d03486d14 tests/regressiontests/views/tests/__init__.py
    a b  
    77from i18n import *
    88from specials import *
    99from static import *
     10from shortcuts import *
  • new file tests/regressiontests/views/tests/shortcuts.py

    diff -r c67d03486d14 tests/regressiontests/views/tests/shortcuts.py
    - +  
     1from django.test import RequestFactory
     2from django.template import Template
     3from django.shortcuts import render
     4
     5from regressiontests.templates.response import BaseTemplateResponseTest
     6
     7def library_view(request):
     8    return render(request, 'debug/render_test.html',
     9                  {'foo': 'foo', 'baz': 'baz'})
     10
     11def customized_context_view(request):
     12    response = library_view(request)
     13    response.template_context.update({'bar': 'bar', 'baz': 'spam'})
     14    return response
     15
     16def customized_template_view(request):
     17    response = library_view(request)
     18    response.template_name = Template('context does not matter')
     19    return response
     20
     21
     22class RenderTest(BaseTemplateResponseTest):
     23
     24    def setUp(self):
     25        super(RenderTest, self).setUp()
     26        self.request = self.factory.get('/')
     27
     28    def test_library_view(self):
     29        response = library_view(self.request)
     30        self.assertEqual(response.content, 'foo..baz.yes\n')
     31
     32    def test_customized_context(self):
     33        response = customized_context_view(self.request)
     34        self.assertEqual(response.content, 'foo.bar.spam.yes\n')
     35
     36    def test_customized_template(self):
     37        response = customized_template_view(self.request)
     38        self.assertEqual(response.content, 'context does not matter')
     39
     40    def test_status_and_content_type(self):
     41        response = render(self.request, 'base.html', {'foo': 'bar'},
     42                          'application/json', 504)
     43        self.assertEqual(response.status_code, 504)
     44        self.assertEqual(response['content-type'], 'application/json')
     45
Back to Top