diff --git a/AUTHORS b/AUTHORS index ac878164650..a0c66aa43dc 100644 --- a/AUTHORS +++ b/AUTHORS @@ -273,6 +273,7 @@ answer newbie questions, and generally made Django that much better: Igor Kolar Tomáš Kopeček Gasper Koren + Mikhail Korobov Martin Kosír Arthur Koziel Meir Kriheli diff --git a/django/contrib/messages/tests/base.py b/django/contrib/messages/tests/base.py index 9a0f7fb36ea..61938acc579 100644 --- a/django/contrib/messages/tests/base.py +++ b/django/contrib/messages/tests/base.py @@ -103,7 +103,7 @@ class BaseTest(TestCase): storage = self.get_storage() self.assertFalse(storage.added_new) storage.add(constants.INFO, 'Test message 1') - self.assert_(storage.added_new) + self.assertTrue(storage.added_new) storage.add(constants.INFO, 'Test message 2', extra_tags='tag') self.assertEqual(len(storage), 2) @@ -180,6 +180,26 @@ class BaseTest(TestCase): for msg in data['messages']: self.assertContains(response, msg) + def test_with_template_response(self): + settings.MESSAGE_LEVEL = constants.DEBUG + data = { + 'messages': ['Test message %d' % x for x in xrange(10)], + } + show_url = reverse('django.contrib.messages.tests.urls.show_template_response') + for level in self.levels.keys(): + add_url = reverse('django.contrib.messages.tests.urls.add_template_response', + args=(level,)) + response = self.client.post(add_url, data, follow=True) + self.assertRedirects(response, show_url) + self.assertTrue('messages' in response.context) + for msg in data['messages']: + self.assertContains(response, msg) + + # there shouldn't be any messages on second GET request + response = self.client.get(show_url) + for msg in data['messages']: + self.assertNotContains(response, msg) + def test_multiple_posts(self): """ Tests that messages persist properly when multiple POSTs are made diff --git a/django/contrib/messages/tests/urls.py b/django/contrib/messages/tests/urls.py index 6252adcfb19..7557f9af4c4 100644 --- a/django/contrib/messages/tests/urls.py +++ b/django/contrib/messages/tests/urls.py @@ -2,9 +2,20 @@ from django.conf.urls.defaults import * from django.contrib import messages from django.core.urlresolvers import reverse from django.http import HttpResponseRedirect, HttpResponse -from django.shortcuts import render_to_response +from django.shortcuts import render_to_response, redirect from django.template import RequestContext, Template +from django.template.response import TemplateResponse +TEMPLATE = """{% if messages %} +
    + {% for message in messages %} + + {{ message }} + + {% endfor %} +
+{% endif %} +""" def add(request, message_type): # don't default to False here, because we want to test that it defaults @@ -16,24 +27,27 @@ def add(request, message_type): fail_silently=fail_silently) else: getattr(messages, message_type)(request, msg) + show_url = reverse('django.contrib.messages.tests.urls.show') return HttpResponseRedirect(show_url) +def add_template_response(request, message_type): + for msg in request.POST.getlist('messages'): + getattr(messages, message_type)(request, msg) + + show_url = reverse('django.contrib.messages.tests.urls.show_template_response') + return HttpResponseRedirect(show_url) def show(request): - t = Template("""{% if messages %} -
    - {% for message in messages %} - - {{ message }} - - {% endfor %} -
-{% endif %}""") + t = Template(TEMPLATE) return HttpResponse(t.render(RequestContext(request))) +def show_template_response(request): + return TemplateResponse(request, Template(TEMPLATE)) urlpatterns = patterns('', ('^add/(debug|info|success|warning|error)/$', add), ('^show/$', show), + ('^template_response/add/(debug|info|success|warning|error)/$', add_template_response), + ('^template_response/show/$', show_template_response), ) diff --git a/django/core/handlers/base.py b/django/core/handlers/base.py index 81f0a9ef3ac..9468b1dab96 100644 --- a/django/core/handlers/base.py +++ b/django/core/handlers/base.py @@ -21,6 +21,7 @@ class BaseHandler(object): def __init__(self): self._request_middleware = self._view_middleware = self._response_middleware = self._exception_middleware = None + def load_middleware(self): """ Populate middleware lists from settings.MIDDLEWARE_CLASSES. @@ -30,16 +31,16 @@ class BaseHandler(object): from django.conf import settings from django.core import exceptions self._view_middleware = [] + self._template_response_middleware = [] self._response_middleware = [] self._exception_middleware = [] request_middleware = [] for middleware_path in settings.MIDDLEWARE_CLASSES: try: - dot = middleware_path.rindex('.') + mw_module, mw_classname = middleware_path.rsplit('.', 1) except ValueError: raise exceptions.ImproperlyConfigured('%s isn\'t a middleware module' % middleware_path) - mw_module, mw_classname = middleware_path[:dot], middleware_path[dot+1:] try: mod = import_module(mw_module) except ImportError, e: @@ -48,7 +49,6 @@ class BaseHandler(object): mw_class = getattr(mod, mw_classname) except AttributeError: raise exceptions.ImproperlyConfigured('Middleware module "%s" does not define a "%s" class' % (mw_module, mw_classname)) - try: mw_instance = mw_class() except exceptions.MiddlewareNotUsed: @@ -58,6 +58,8 @@ class BaseHandler(object): request_middleware.append(mw_instance.process_request) if hasattr(mw_instance, 'process_view'): self._view_middleware.append(mw_instance.process_view) + if hasattr(mw_instance, 'process_template_response'): + self._template_response_middleware.insert(0, mw_instance.process_template_response) if hasattr(mw_instance, 'process_response'): self._response_middleware.insert(0, mw_instance.process_response) if hasattr(mw_instance, 'process_exception'): @@ -164,6 +166,13 @@ class BaseHandler(object): urlresolvers.set_urlconf(None) try: + # If the response supports deferred rendering, apply template + # response middleware and the render the response + if hasattr(response, 'render') and callable(response.render): + for middleware_method in self._template_response_middleware: + response = middleware_method(request, response) + response.render() + # Apply response middleware, regardless of the response for middleware_method in self._response_middleware: response = middleware_method(request, response) diff --git a/django/template/response.py b/django/template/response.py new file mode 100644 index 00000000000..d89fee0aab7 --- /dev/null +++ b/django/template/response.py @@ -0,0 +1,108 @@ +from django.http import HttpResponse +from django.template import loader, Context, RequestContext + +class ContentNotRenderedError(Exception): + pass + +class SimpleTemplateResponse(HttpResponse): + + def __init__(self, template, context=None, mimetype=None, status=None, + content_type=None): + # It would seem obvious to call these next two members 'template' and + # 'context', but those names are reserved as part of the test Client API. + # To avoid the name collision, we use + # tricky-to-debug problems + self.template_name = template + self.context_data = context + + # _is_rendered tracks whether the template and context has been baked into + # a final response. + self._is_rendered = False + + # content argument doesn't make sense here because it will be replaced + # with rendered template so we always pass empty string in order to + # prevent errors and provide shorter signature. + super(SimpleTemplateResponse, self).__init__('', mimetype, status, + content_type) + + def resolve_template(self, template): + "Accepts a template object, path-to-template or list of paths" + if isinstance(template, (list, tuple)): + return loader.select_template(template) + elif isinstance(template, basestring): + return loader.get_template(template) + else: + return template + + def resolve_context(self, context): + """Convert context data into a full Context object + (assuming it isn't already a Context object). + """ + if isinstance(context, Context): + return context + else: + return Context(context) + + @property + def rendered_content(self): + """Returns the freshly rendered content for the template and context + described by the TemplateResponse. + + This *does not* set the final content of the response. To set the + response content, you must either call render(), or set the + content explicitly using the value of this property. + """ + template = self.resolve_template(self.template_name) + context = self.resolve_context(self.context_data) + content = template.render(context) + return content + + def render(self): + """Render (thereby finalizing) the content of the response. + + If the content has already been rendered, this is a no-op. + + Returns the baked response instance. + """ + if not self._is_rendered: + self._set_content(self.rendered_content) + return self + + is_rendered = property(lambda self: self._is_rendered) + + def __iter__(self): + if not self._is_rendered: + raise ContentNotRenderedError('The response content must be rendered before it can be iterated over.') + return super(SimpleTemplateResponse, self).__iter__() + + def _get_content(self): + if not self._is_rendered: + raise ContentNotRenderedError('The response content must be rendered before it can be accessed.') + return super(SimpleTemplateResponse, self)._get_content() + + def _set_content(self, value): + "Overrides rendered content, unless you later call render()" + super(SimpleTemplateResponse, self)._set_content(value) + self._is_rendered = True + + content = property(_get_content, _set_content) + + +class TemplateResponse(SimpleTemplateResponse): + def __init__(self, request, template, context=None, mimetype=None, + status=None, content_type=None): + # self.request gets over-written by django.test.client.Client - and + # unlike context_data and template_name the _request should not + # be considered part of the public API. + self._request = request + super(TemplateResponse, self).__init__( + template, context, mimetype, status, content_type) + + def resolve_context(self, context): + """Convert context data into a full RequestContext object + (assuming it isn't already a Context object). + """ + if isinstance(context, Context): + return context + else: + return RequestContext(self._request, context) diff --git a/django/views/generic/base.py b/django/views/generic/base.py index 0c9cd7971f4..5e69a8dd304 100644 --- a/django/views/generic/base.py +++ b/django/views/generic/base.py @@ -1,6 +1,7 @@ from django import http from django.core.exceptions import ImproperlyConfigured from django.template import RequestContext, loader +from django.template.response import TemplateResponse from django.utils.functional import update_wrapper from django.utils.log import getLogger from django.utils.decorators import classonlymethod @@ -81,59 +82,29 @@ class TemplateResponseMixin(object): A mixin that can be used to render a template. """ template_name = None + response_class = TemplateResponse - def render_to_response(self, context): + def render_to_response(self, context, **response_kwargs): """ Returns a response with a template rendered with the given context. """ - return self.get_response(self.render_template(context)) - - def get_response(self, content, **httpresponse_kwargs): - """ - Construct an `HttpResponse` object. - """ - return http.HttpResponse(content, **httpresponse_kwargs) - - def render_template(self, context): - """ - Render the template with a given context. - """ - context_instance = self.get_context_instance(context) - return self.get_template().render(context_instance) - - def get_context_instance(self, context): - """ - Get the template context instance. Must return a Context (or subclass) - instance. - """ - return RequestContext(self.request, context) - - def get_template(self): - """ - Get a ``Template`` object for the given request. - """ - names = self.get_template_names() - if not names: - raise ImproperlyConfigured(u"'%s' must provide template_name." - % self.__class__.__name__) - return self.load_template(names) + return self.response_class( + request = self.request, + template = self.get_template_names(), + context = context, + **response_kwargs + ) def get_template_names(self): """ - Return a list of template names to be used for the request. Must return - a list. May not be called if get_template is overridden. + Returns a list of template names to be used for the request. Must return + a list. May not be called if render_to_response is overridden. """ if self.template_name is None: return [] else: return [self.template_name] - def load_template(self, names): - """ - Load a list of templates using the default template loader. - """ - return loader.select_template(names) - class TemplateView(TemplateResponseMixin, View): """ diff --git a/docs/index.txt b/docs/index.txt index 55e6cda983e..632adc8da3f 100644 --- a/docs/index.txt +++ b/docs/index.txt @@ -93,7 +93,9 @@ The view layer :doc:`View functions ` | :doc:`Shortcuts ` - * **Reference:** :doc:`Request/response objects ` + * **Reference:** + :doc:`Request/response objects ` | + :doc:`TemplateResponse objects ` * **File uploads:** :doc:`Overview ` | diff --git a/docs/ref/class-based-views.txt b/docs/ref/class-based-views.txt index 1b9a9f99ea0..150ffff2e13 100644 --- a/docs/ref/class-based-views.txt +++ b/docs/ref/class-based-views.txt @@ -76,39 +76,25 @@ TemplateResponseMixin The path to the template to use when rendering the view. - .. method:: render_to_response(context) + .. attribute:: response_class - Returns a full composed HttpResponse instance, ready to be returned to - the user. + The response class to be returned by ``render_to_response`` method. + Default is + :class:`TemplateResponse `. + The template and context of TemplateResponse instances can be + altered later (e.g. in + :ref:`template response middleware `). - Calls :meth:`~TemplateResponseMixin.render_template()` to build the - content of the response, and - :meth:`~TemplateResponseMixin.get_response()` to construct the - :class:`~django.http.HttpResponse` object. + Create TemplateResponse subclass and pass set it to + ``template_response_class`` if you need custom template loading or + custom context object instantiation. - .. method:: get_response(content, **httpresponse_kwargs) + .. method:: render_to_response(context, **response_kwargs) - Constructs the :class:`~django.http.HttpResponse` object around the - given content. If any keyword arguments are provided, they will be - passed to the constructor of the :class:`~django.http.HttpResponse` - instance. + Returns a ``self.template_response_class`` instance. - .. method:: render_template(context) - - Calls :meth:`~TemplateResponseMixin.get_context_instance()` to obtain - the :class:`Context` instance to use for rendering, and calls - :meth:`TemplateReponseMixin.get_template()` to load the template that - will be used to render the final content. - - .. method:: get_context_instance(context) - - Turns the data dictionary ``context`` into an actual context instance - that can be used for rendering. - - By default, constructs a :class:`~django.template.RequestContext` - instance. - - .. method:: get_template() + If any keyword arguments are provided, they will be + passed to the constructor of the response instance. Calls :meth:`~TemplateResponseMixin.get_template_names()` to obtain the list of template names that will be searched looking for an existent @@ -123,10 +109,6 @@ TemplateResponseMixin default implementation will return a list containing :attr:`TemplateResponseMixin.template_name` (if it is specified). - .. method:: load_template(names) - - Loads and returns a template found by searching the list of ``names`` - for a match. Uses Django's default template loader. Single object mixins -------------------- diff --git a/docs/ref/index.txt b/docs/ref/index.txt index 7b59589e74f..f544e3cd982 100644 --- a/docs/ref/index.txt +++ b/docs/ref/index.txt @@ -16,6 +16,7 @@ API Reference middleware models/index request-response + template-response settings signals templates/index diff --git a/docs/ref/template-response.txt b/docs/ref/template-response.txt new file mode 100644 index 00000000000..09bcd16378c --- /dev/null +++ b/docs/ref/template-response.txt @@ -0,0 +1,211 @@ +=========================================== +TemplateResponse and SimpleTemplateResponse +=========================================== + +.. versionadded:: 1.3 + +.. module:: django.template.response + :synopsis: Classes dealing with lazy-rendered HTTP responses. + +Standard HttpResponse objects are static structures. They are provided +with a block of pre-rendered content at time of construction, and +while that content can be modified, it isn't in a form that makes it +easy to perform modifications. + +However, it can sometimes be beneficial to allow decorators or +middleware to modify a response *after* it has been constructed by the +view. For example, you may want to change the template that is used, +or put additional data into the context. + +TemplateResponse provides a way to do just that. Unlike basic +HttpResponse objects, TemplateResponse objects retain the details of +the template and context that was provided by the view to compute the +response. The final output of the response is not computed until +it is needed, later in the response process. + +TemplateResponse objects +======================== + +.. class:: SimpleTemplateResponse() + +Attributes +---------- + +.. attribute:: SimpleTemplateResponse.template_name + + The name of the template to be rendered. Accepts + :class:`django.template.Template` object, path to template or list + of paths. + + Example: ``['foo.html', 'path/to/bar.html']`` + +.. attribute:: SimpleTemplateResponse.context_data + + The context data to be used when rendering the template. It can be + a dictionary or a context object. + + Example: ``{'foo': 123}`` + +.. attr:: SimpleTemplateResponse.rendered_content: + + The current rendered value of the response content, using the current + template and context data. + +.. attr:: SimpleTemplateResponse.is_rendered: + + A boolean indicating whether the response content has been rendered. + + +Methods +------- + +.. method:: SimpleTemplateResponse.__init__(template, context=None, mimetype=None, status=None, content_type=None) + + Instantiates an + :class:`~django.template.response.SimpleTemplateResponse` object + with the given template, context, MIME type and HTTP status. + + ``template`` is a full name of a template, or a sequence of + template names. :class:`django.template.Template` instances can + also be used. + + ``context`` is a dictionary of values to add to the template + context. By default, this is an empty dictionary. + :class:`~django.template.Context` objects are also accepted as + ``context`` values. + + ``status`` is the HTTP Status code for the response. + + ``content_type`` is an alias for ``mimetype``. Historically, this + parameter was only called ``mimetype``, but since this is actually + the value included in the HTTP ``Content-Type`` header, it can + also include the character set encoding, which makes it more than + just a MIME type specification. If ``mimetype`` is specified (not + ``None``), that value is used. Otherwise, ``content_type`` is + used. If neither is given, the ``DEFAULT_CONTENT_TYPE`` setting is + used. + + +.. method:: SimpleTemplateResponse.resolve_context(context) + + Converts context data into a context instance that can be used for + rendering a template. Accepts a dictionary of context data or a + context object. Returns a :class:`~django.template.Context` + instance containing the provided data. + + Override this method in order to customize context instantiation. + +.. method:: SimpleTemplateResponse.resolve_template(template) + + Resolves the template instance to use for rendering. Accepts a + path of a template to use, or a sequence of template paths. + :class:`~django.template.Template` instances may also be provided. + Returns the :class:`~django.template.Template` instance to be + rendered. + + Override this method in order to customize template rendering. + +.. method:: SimpleTemplateResponse.render(): + + Sets :attr:`response.content` to the result obtained by + :attr:`SimpleTemplateResponse.rendered_content`. + + :meth:`~SimpleTemplateResponse.render()` will only have an effect + the first time it is called. On subsequent calls, it will return + the result obtained from the first call. + + +.. class:: TemplateResponse() + + TemplateResponse is a subclass of :class:`SimpleTemplateResponse + ` that uses + RequestContext instead of Context. + +.. method:: TemplateResponse.__init__(request, template, context=None, mimetype=None, status=None, content_type=None) + + Instantiates an ``TemplateResponse`` object with the given + template, context, MIME type and HTTP status. + + ``request`` is a HttpRequest instance. + + ``template`` is a full name of a template to use or sequence of + template names. :class:`django.template.Template` instances are + also accepted. + + ``context`` is a dictionary of values to add to the template + context. By default, this is an empty dictionary; context objects + are also accepted as ``context`` values. + + ``status`` is the HTTP Status code for the response. + + ``content_type`` is an alias for ``mimetype``. Historically, this + parameter was only called ``mimetype``, but since this is actually + the value included in the HTTP ``Content-Type`` header, it can also + include the character set encoding, which makes it more than just a + MIME type specification. If ``mimetype`` is specified (not + ``None``), that value is used. Otherwise, ``content_type`` is used. + If neither is given, the ``DEFAULT_CONTENT_TYPE`` setting is used. + + +The rendering process +===================== + +Before a :class:`TemplateResponse()` instance can be returned to the +client, it must be rendered. The rendering process takes the +intermediate representation of template and context, and turns it into +the final byte stream that can be served to the client. + +There are three circumstances under which a TemplateResponse will be +rendered: + + * When the TemplateResponse instance is explicitly rendered, using + the :meth:`SimpleTemplateResponse.render()` method. + + * When the content of the response is explicitly set by assigning + :attr:`response.content`. + + * After passing through template response middleware, but before + passing through response middleware. + +A TemplateResponse can only be rendered once. The first call to +:meth:`SimpleTemplateResponse.render()` sets the content of the +response; subsequent rendering calls do not change the response +content. + +However, when :attr:`response.content` is explicitly assigned, the +change is always applied. If you want to force the content to be +re-rendered, you can re-evaluate the rendered content, and assign +the content of the response manually:: + + # Set up a baked TemplateResponse + >>> t = TemplateResponse(request, 'original.html', {}) + >>> t.render() + >>> print t.content + Original content + + # Rebaking doesn't change content + >>> t.template_name = 'new.html' + >>> t.render() + >>> print t.content + Original content + + # Assigning content does change, no render() call required + >>> t.content = t.rendered_content + >>> print t.content + New content + +Using TemplateResponse and SimpleTemplateResponse +================================================= + +A TemplateResponse object can be used anywhere that a normal +HttpResponse can be used. It can also be used as an alternative to +calling :method:`~django.shortcuts.render_to_response()`. + +For example, the following simple view returns a +:class:`TemplateResponse()` with a simple template, and a context +containing a queryset:: + + from django.template.response import TemplateResponse + + def blog_index(request): + return TemplateResponse(request, 'entry_list.html', {'entries': Entry.objects.all()}) diff --git a/docs/releases/1.3.txt b/docs/releases/1.3.txt index 79ab8b3f16a..0c2dedc4816 100644 --- a/docs/releases/1.3.txt +++ b/docs/releases/1.3.txt @@ -133,6 +133,27 @@ can also add special translator comments in the source. For more information, see :ref:`contextual-markers` and :ref:`translator-comments`. +TemplateResponse +~~~~~~~~~~~~~~~~ + +It can sometimes be beneficial to allow decorators or middleware to +modify a response *after* it has been constructed by the view. For +example, you may want to change the template that is used, or put +additional data into the context. + +However, you can't (easily) modify the content of a basic +:class:`~django.http.HttpResponse` after it has been constructed. To +overcome this limitation, Django 1.3 adds a new +:class:`~django.template.TemplateResponse` class. Unlike basic +:class:`~django.http.HttpResponse` objects, +:class:`~django.template.TemplateResponse` objects retain the details +of the template and context that was provided by the view to compute +the response. The final output of the response is not computed until +it is needed, later in the response process. + +For more details, see the :ref:`documentation ` +on the :class:`~django.template.TemplateResponse` class. + Everything else ~~~~~~~~~~~~~~~ diff --git a/docs/topics/http/middleware.txt b/docs/topics/http/middleware.txt index d376c6b1e0a..c03eb155d6d 100644 --- a/docs/topics/http/middleware.txt +++ b/docs/topics/http/middleware.txt @@ -97,6 +97,39 @@ calling ANY other request, view or exception middleware, or the appropriate view; it'll return that :class:`~django.http.HttpResponse`. Response middleware is always called on every response. +.. _template-response-middleware: + +``process_template_response`` +----------------------------- + +.. versionadded:: 1.3 + +.. method:: process_template_response(self, request, response) + +``request`` is an :class:`~django.http.HttpRequest` object. ``response`` is the +:class:`~django.template.response.SimpleTemplateResponse` subclass (e.g. +:class:`~django.template.response.TemplateResponse`) object returned by a +Django view. + +``process_template_response()`` must return an +:class:`~django.template.response.SimpleTemplateResponse` (or it's subclass) +object. It could alter the given ``response`` by changing +``response.template_name`` and ``response.template_context``, or it could +create and return a brand-new +:class:`~django.template.response.SimpleTemplateResponse` (or it's subclass) +instance. + +``process_template_response()`` will only be called if the response +instance has a ``render()`` method, indicating that it is a +:class:`~django.template.response.TemplateResponse`. + +You don't need to explicitly render responses -- responses will be +automatically rendered once all template response middleware has been +called. + +Middleware are run in reverse order during the response phase, which +includes process_template_response. + .. _response-middleware: ``process_response`` @@ -120,6 +153,7 @@ an earlier middleware method returned an :class:`~django.http.HttpResponse` classes are applied in reverse order, from the bottom up. This means classes defined at the end of :setting:`MIDDLEWARE_CLASSES` will be run first. + .. _exception-middleware: ``process_exception`` @@ -137,7 +171,7 @@ Django calls ``process_exception()`` when a view raises an exception. the browser. Otherwise, default exception handling kicks in. Again, middleware are run in reverse order during the response phase, which -includes ``process_exception``. If an exception middleware return a response, +includes ``process_exception``. If an exception middleware returns a response, the middleware classes above that middleware will not be called at all. ``__init__`` diff --git a/tests/regressiontests/generic_views/base.py b/tests/regressiontests/generic_views/base.py index a1da98690bd..f356c90a747 100644 --- a/tests/regressiontests/generic_views/base.py +++ b/tests/regressiontests/generic_views/base.py @@ -156,6 +156,7 @@ class TemplateViewTest(TestCase): rf = RequestFactory() def _assert_about(self, response): + response.render() self.assertEqual(response.status_code, 200) self.assertEqual(response.content, '

About

') diff --git a/tests/regressiontests/middleware_exceptions/tests.py b/tests/regressiontests/middleware_exceptions/tests.py index 5fee9a747c9..e68cd523135 100644 --- a/tests/regressiontests/middleware_exceptions/tests.py +++ b/tests/regressiontests/middleware_exceptions/tests.py @@ -3,9 +3,10 @@ import sys from django.conf import settings from django.core.signals import got_request_exception from django.http import HttpResponse +from django.template.response import TemplateResponse +from django.template import Template from django.test import TestCase - class TestException(Exception): pass @@ -16,6 +17,7 @@ class TestMiddleware(object): self.process_request_called = False self.process_view_called = False self.process_response_called = False + self.process_template_response_called = False self.process_exception_called = False def process_request(self, request): @@ -24,6 +26,10 @@ class TestMiddleware(object): def process_view(self, request, view_func, view_args, view_kwargs): self.process_view_called = True + def process_template_response(self, request, response): + self.process_template_response_called = True + return response + def process_response(self, request, response): self.process_response_called = True return response @@ -48,6 +54,11 @@ class ResponseMiddleware(TestMiddleware): super(ResponseMiddleware, self).process_response(request, response) return HttpResponse('Response Middleware') +class TemplateResponseMiddleware(TestMiddleware): + def process_template_response(self, request, response): + super(TemplateResponseMiddleware, self).process_template_response(request, response) + return TemplateResponse(request, Template('Template Response Middleware')) + class ExceptionMiddleware(TestMiddleware): def process_exception(self, request, exception): super(ExceptionMiddleware, self).process_exception(request, exception) @@ -66,6 +77,11 @@ class BadViewMiddleware(TestMiddleware): super(BadViewMiddleware, self).process_view(request, view_func, view_args, view_kwargs) raise TestException('Test View Exception') +class BadTemplateResponseMiddleware(TestMiddleware): + def process_template_response(self, request, response): + super(BadTemplateResponseMiddleware, self).process_template_response(request, response) + raise TestException('Test Template Response Exception') + class BadResponseMiddleware(TestMiddleware): def process_response(self, request, response): super(BadResponseMiddleware, self).process_response(request, response) @@ -93,6 +109,7 @@ class BaseMiddlewareExceptionTest(TestCase): def _add_middleware(self, middleware): self.client.handler._request_middleware.insert(0, middleware.process_request) self.client.handler._view_middleware.insert(0, middleware.process_view) + self.client.handler._template_response_middleware.append(middleware.process_template_response) self.client.handler._response_middleware.append(middleware.process_response) self.client.handler._exception_middleware.append(middleware.process_exception) @@ -113,9 +130,10 @@ class BaseMiddlewareExceptionTest(TestCase): exception, value, tb = self.exceptions[i] self.assertEquals(value.args, (error, )) - def assert_middleware_usage(self, middleware, request, view, response, exception): + def assert_middleware_usage(self, middleware, request, view, template_response, response, exception): self.assertEqual(middleware.process_request_called, request) self.assertEqual(middleware.process_view_called, view) + self.assertEqual(middleware.process_template_response_called, template_response) self.assertEqual(middleware.process_response_called, response) self.assertEqual(middleware.process_exception_called, exception) @@ -132,9 +150,9 @@ class MiddlewareTests(BaseMiddlewareExceptionTest): self.assert_exceptions_handled('/middleware_exceptions/view/', []) # Check that the right middleware methods have been invoked - self.assert_middleware_usage(pre_middleware, True, False, True, False) - self.assert_middleware_usage(middleware, True, False, True, False) - self.assert_middleware_usage(post_middleware, False, False, True, False) + self.assert_middleware_usage(pre_middleware, True, False, False, True, False) + self.assert_middleware_usage(middleware, True, False, False, True, False) + self.assert_middleware_usage(post_middleware, False, False, False, True, False) def test_process_view_middleware(self): pre_middleware = TestMiddleware() @@ -146,9 +164,9 @@ class MiddlewareTests(BaseMiddlewareExceptionTest): self.assert_exceptions_handled('/middleware_exceptions/view/', []) # Check that the right middleware methods have been invoked - self.assert_middleware_usage(pre_middleware, True, True, True, False) - self.assert_middleware_usage(middleware, True, True, True, False) - self.assert_middleware_usage(post_middleware, True, False, True, False) + self.assert_middleware_usage(pre_middleware, True, True, False, True, False) + self.assert_middleware_usage(middleware, True, True, False, True, False) + self.assert_middleware_usage(post_middleware, True, False, False, True, False) def test_process_response_middleware(self): pre_middleware = TestMiddleware() @@ -160,9 +178,23 @@ class MiddlewareTests(BaseMiddlewareExceptionTest): self.assert_exceptions_handled('/middleware_exceptions/view/', []) # Check that the right middleware methods have been invoked - self.assert_middleware_usage(pre_middleware, True, True, True, False) - self.assert_middleware_usage(middleware, True, True, True, False) - self.assert_middleware_usage(post_middleware, True, True, True, False) + self.assert_middleware_usage(pre_middleware, True, True, False, True, False) + self.assert_middleware_usage(middleware, True, True, False, True, False) + self.assert_middleware_usage(post_middleware, True, True, False, True, False) + + def test_process_template_response_middleware(self): + pre_middleware = TestMiddleware() + middleware = TemplateResponseMiddleware() + post_middleware = TestMiddleware() + self._add_middleware(post_middleware) + self._add_middleware(middleware) + self._add_middleware(pre_middleware) + self.assert_exceptions_handled('/middleware_exceptions/template_response/', []) + + # Check that the right middleware methods have been invoked + self.assert_middleware_usage(pre_middleware, True, True, True, True, False) + self.assert_middleware_usage(middleware, True, True, True, True, False) + self.assert_middleware_usage(post_middleware, True, True, True, True, False) def test_process_exception_middleware(self): pre_middleware = TestMiddleware() @@ -174,9 +206,9 @@ class MiddlewareTests(BaseMiddlewareExceptionTest): self.assert_exceptions_handled('/middleware_exceptions/view/', []) # Check that the right middleware methods have been invoked - self.assert_middleware_usage(pre_middleware, True, True, True, False) - self.assert_middleware_usage(middleware, True, True, True, False) - self.assert_middleware_usage(post_middleware, True, True, True, False) + self.assert_middleware_usage(pre_middleware, True, True, False, True, False) + self.assert_middleware_usage(middleware, True, True, False, True, False) + self.assert_middleware_usage(post_middleware, True, True, False, True, False) def test_process_request_middleware_not_found(self): pre_middleware = TestMiddleware() @@ -188,9 +220,9 @@ class MiddlewareTests(BaseMiddlewareExceptionTest): self.assert_exceptions_handled('/middleware_exceptions/not_found/', []) # Check that the right middleware methods have been invoked - self.assert_middleware_usage(pre_middleware, True, False, True, False) - self.assert_middleware_usage(middleware, True, False, True, False) - self.assert_middleware_usage(post_middleware, False, False, True, False) + self.assert_middleware_usage(pre_middleware, True, False, False, True, False) + self.assert_middleware_usage(middleware, True, False, False, True, False) + self.assert_middleware_usage(post_middleware, False, False, False, True, False) def test_process_view_middleware_not_found(self): pre_middleware = TestMiddleware() @@ -202,9 +234,23 @@ class MiddlewareTests(BaseMiddlewareExceptionTest): self.assert_exceptions_handled('/middleware_exceptions/not_found/', []) # Check that the right middleware methods have been invoked - self.assert_middleware_usage(pre_middleware, True, True, True, False) - self.assert_middleware_usage(middleware, True, True, True, False) - self.assert_middleware_usage(post_middleware, True, False, True, False) + self.assert_middleware_usage(pre_middleware, True, True, False, True, False) + self.assert_middleware_usage(middleware, True, True, False, True, False) + self.assert_middleware_usage(post_middleware, True, False, False, True, False) + + def test_process_template_response_middleware_not_found(self): + pre_middleware = TestMiddleware() + middleware = TemplateResponseMiddleware() + post_middleware = TestMiddleware() + self._add_middleware(post_middleware) + self._add_middleware(middleware) + self._add_middleware(pre_middleware) + self.assert_exceptions_handled('/middleware_exceptions/not_found/', []) + + # Check that the right middleware methods have been invoked + self.assert_middleware_usage(pre_middleware, True, True, False, True, True) + self.assert_middleware_usage(middleware, True, True, False, True, True) + self.assert_middleware_usage(post_middleware, True, True, False, True, True) def test_process_response_middleware_not_found(self): pre_middleware = TestMiddleware() @@ -216,9 +262,9 @@ class MiddlewareTests(BaseMiddlewareExceptionTest): self.assert_exceptions_handled('/middleware_exceptions/not_found/', []) # Check that the right middleware methods have been invoked - self.assert_middleware_usage(pre_middleware, True, True, True, True) - self.assert_middleware_usage(middleware, True, True, True, True) - self.assert_middleware_usage(post_middleware, True, True, True, True) + self.assert_middleware_usage(pre_middleware, True, True, False, True, True) + self.assert_middleware_usage(middleware, True, True, False, True, True) + self.assert_middleware_usage(post_middleware, True, True, False, True, True) def test_process_exception_middleware_not_found(self): pre_middleware = TestMiddleware() @@ -230,9 +276,9 @@ class MiddlewareTests(BaseMiddlewareExceptionTest): self.assert_exceptions_handled('/middleware_exceptions/not_found/', []) # Check that the right middleware methods have been invoked - self.assert_middleware_usage(pre_middleware, True, True, True, False) - self.assert_middleware_usage(middleware, True, True, True, True) - self.assert_middleware_usage(post_middleware, True, True, True, True) + self.assert_middleware_usage(pre_middleware, True, True, False, True, False) + self.assert_middleware_usage(middleware, True, True, False, True, True) + self.assert_middleware_usage(post_middleware, True, True, False, True, True) def test_process_request_middleware_exception(self): pre_middleware = TestMiddleware() @@ -244,9 +290,9 @@ class MiddlewareTests(BaseMiddlewareExceptionTest): self.assert_exceptions_handled('/middleware_exceptions/error/', []) # Check that the right middleware methods have been invoked - self.assert_middleware_usage(pre_middleware, True, False, True, False) - self.assert_middleware_usage(middleware, True, False, True, False) - self.assert_middleware_usage(post_middleware, False, False, True, False) + self.assert_middleware_usage(pre_middleware, True, False, False, True, False) + self.assert_middleware_usage(middleware, True, False, False, True, False) + self.assert_middleware_usage(post_middleware, False, False, False, True, False) def test_process_view_middleware_exception(self): pre_middleware = TestMiddleware() @@ -258,9 +304,9 @@ class MiddlewareTests(BaseMiddlewareExceptionTest): self.assert_exceptions_handled('/middleware_exceptions/error/', []) # Check that the right middleware methods have been invoked - self.assert_middleware_usage(pre_middleware, True, True, True, False) - self.assert_middleware_usage(middleware, True, True, True, False) - self.assert_middleware_usage(post_middleware, True, False, True, False) + self.assert_middleware_usage(pre_middleware, True, True, False, True, False) + self.assert_middleware_usage(middleware, True, True, False, True, False) + self.assert_middleware_usage(post_middleware, True, False, False, True, False) def test_process_response_middleware_exception(self): pre_middleware = TestMiddleware() @@ -272,9 +318,9 @@ class MiddlewareTests(BaseMiddlewareExceptionTest): self.assert_exceptions_handled('/middleware_exceptions/error/', ['Error in view'], Exception()) # Check that the right middleware methods have been invoked - self.assert_middleware_usage(pre_middleware, True, True, True, True) - self.assert_middleware_usage(middleware, True, True, True, True) - self.assert_middleware_usage(post_middleware, True, True, True, True) + self.assert_middleware_usage(pre_middleware, True, True, False, True, True) + self.assert_middleware_usage(middleware, True, True, False, True, True) + self.assert_middleware_usage(post_middleware, True, True, False, True, True) def test_process_exception_middleware_exception(self): pre_middleware = TestMiddleware() @@ -286,9 +332,9 @@ class MiddlewareTests(BaseMiddlewareExceptionTest): self.assert_exceptions_handled('/middleware_exceptions/error/', []) # Check that the right middleware methods have been invoked - self.assert_middleware_usage(pre_middleware, True, True, True, False) - self.assert_middleware_usage(middleware, True, True, True, True) - self.assert_middleware_usage(post_middleware, True, True, True, True) + self.assert_middleware_usage(pre_middleware, True, True, False, True, False) + self.assert_middleware_usage(middleware, True, True, False, True, True) + self.assert_middleware_usage(post_middleware, True, True, False, True, True) def test_process_request_middleware_null_view(self): pre_middleware = TestMiddleware() @@ -300,9 +346,9 @@ class MiddlewareTests(BaseMiddlewareExceptionTest): self.assert_exceptions_handled('/middleware_exceptions/null_view/', []) # Check that the right middleware methods have been invoked - self.assert_middleware_usage(pre_middleware, True, False, True, False) - self.assert_middleware_usage(middleware, True, False, True, False) - self.assert_middleware_usage(post_middleware, False, False, True, False) + self.assert_middleware_usage(pre_middleware, True, False, False, True, False) + self.assert_middleware_usage(middleware, True, False, False, True, False) + self.assert_middleware_usage(post_middleware, False, False, False, True, False) def test_process_view_middleware_null_view(self): pre_middleware = TestMiddleware() @@ -314,9 +360,9 @@ class MiddlewareTests(BaseMiddlewareExceptionTest): self.assert_exceptions_handled('/middleware_exceptions/null_view/', []) # Check that the right middleware methods have been invoked - self.assert_middleware_usage(pre_middleware, True, True, True, False) - self.assert_middleware_usage(middleware, True, True, True, False) - self.assert_middleware_usage(post_middleware, True, False, True, False) + self.assert_middleware_usage(pre_middleware, True, True, False, True, False) + self.assert_middleware_usage(middleware, True, True, False, True, False) + self.assert_middleware_usage(post_middleware, True, False, False, True, False) def test_process_response_middleware_null_view(self): pre_middleware = TestMiddleware() @@ -331,9 +377,9 @@ class MiddlewareTests(BaseMiddlewareExceptionTest): ValueError()) # Check that the right middleware methods have been invoked - self.assert_middleware_usage(pre_middleware, True, True, True, False) - self.assert_middleware_usage(middleware, True, True, True, False) - self.assert_middleware_usage(post_middleware, True, True, True, False) + self.assert_middleware_usage(pre_middleware, True, True, False, True, False) + self.assert_middleware_usage(middleware, True, True, False, True, False) + self.assert_middleware_usage(post_middleware, True, True, False, True, False) def test_process_exception_middleware_null_view(self): pre_middleware = TestMiddleware() @@ -348,9 +394,9 @@ class MiddlewareTests(BaseMiddlewareExceptionTest): ValueError()) # Check that the right middleware methods have been invoked - self.assert_middleware_usage(pre_middleware, True, True, True, False) - self.assert_middleware_usage(middleware, True, True, True, False) - self.assert_middleware_usage(post_middleware, True, True, True, False) + self.assert_middleware_usage(pre_middleware, True, True, False, True, False) + self.assert_middleware_usage(middleware, True, True, False, True, False) + self.assert_middleware_usage(post_middleware, True, True, False, True, False) def test_process_request_middleware_permission_denied(self): pre_middleware = TestMiddleware() @@ -362,9 +408,9 @@ class MiddlewareTests(BaseMiddlewareExceptionTest): self.assert_exceptions_handled('/middleware_exceptions/permission_denied/', []) # Check that the right middleware methods have been invoked - self.assert_middleware_usage(pre_middleware, True, False, True, False) - self.assert_middleware_usage(middleware, True, False, True, False) - self.assert_middleware_usage(post_middleware, False, False, True, False) + self.assert_middleware_usage(pre_middleware, True, False, False, True, False) + self.assert_middleware_usage(middleware, True, False, False, True, False) + self.assert_middleware_usage(post_middleware, False, False, False, True, False) def test_process_view_middleware_permission_denied(self): pre_middleware = TestMiddleware() @@ -376,9 +422,9 @@ class MiddlewareTests(BaseMiddlewareExceptionTest): self.assert_exceptions_handled('/middleware_exceptions/permission_denied/', []) # Check that the right middleware methods have been invoked - self.assert_middleware_usage(pre_middleware, True, True, True, False) - self.assert_middleware_usage(middleware, True, True, True, False) - self.assert_middleware_usage(post_middleware, True, False, True, False) + self.assert_middleware_usage(pre_middleware, True, True, False, True, False) + self.assert_middleware_usage(middleware, True, True, False, True, False) + self.assert_middleware_usage(post_middleware, True, False, False, True, False) def test_process_response_middleware_permission_denied(self): pre_middleware = TestMiddleware() @@ -390,9 +436,9 @@ class MiddlewareTests(BaseMiddlewareExceptionTest): self.assert_exceptions_handled('/middleware_exceptions/permission_denied/', []) # Check that the right middleware methods have been invoked - self.assert_middleware_usage(pre_middleware, True, True, True, True) - self.assert_middleware_usage(middleware, True, True, True, True) - self.assert_middleware_usage(post_middleware, True, True, True, True) + self.assert_middleware_usage(pre_middleware, True, True, False, True, True) + self.assert_middleware_usage(middleware, True, True, False, True, True) + self.assert_middleware_usage(post_middleware, True, True, False, True, True) def test_process_exception_middleware_permission_denied(self): pre_middleware = TestMiddleware() @@ -404,9 +450,18 @@ class MiddlewareTests(BaseMiddlewareExceptionTest): self.assert_exceptions_handled('/middleware_exceptions/permission_denied/', []) # Check that the right middleware methods have been invoked - self.assert_middleware_usage(pre_middleware, True, True, True, False) - self.assert_middleware_usage(middleware, True, True, True, True) - self.assert_middleware_usage(post_middleware, True, True, True, True) + self.assert_middleware_usage(pre_middleware, True, True, False, True, False) + self.assert_middleware_usage(middleware, True, True, False, True, True) + self.assert_middleware_usage(post_middleware, True, True, False, True, True) + + def test_process_template_response_error(self): + middleware = TestMiddleware() + self._add_middleware(middleware) + self.assert_exceptions_handled('/middleware_exceptions/template_response_error/', []) + + # Check that the right middleware methods have been invoked + self.assert_middleware_usage(middleware, True, True, True, True, False) + class BadMiddlewareTests(BaseMiddlewareExceptionTest): @@ -420,9 +475,9 @@ class BadMiddlewareTests(BaseMiddlewareExceptionTest): self.assert_exceptions_handled('/middleware_exceptions/view/', ['Test Request Exception']) # Check that the right middleware methods have been invoked - self.assert_middleware_usage(pre_middleware, True, False, True, False) - self.assert_middleware_usage(bad_middleware, True, False, True, False) - self.assert_middleware_usage(post_middleware, False, False, True, False) + self.assert_middleware_usage(pre_middleware, True, False, False, True, False) + self.assert_middleware_usage(bad_middleware, True, False, False, True, False) + self.assert_middleware_usage(post_middleware, False, False, False, True, False) def test_process_view_bad_middleware(self): pre_middleware = TestMiddleware() @@ -434,9 +489,23 @@ class BadMiddlewareTests(BaseMiddlewareExceptionTest): self.assert_exceptions_handled('/middleware_exceptions/view/', ['Test View Exception']) # Check that the right middleware methods have been invoked - self.assert_middleware_usage(pre_middleware, True, True, True, False) - self.assert_middleware_usage(bad_middleware, True, True, True, False) - self.assert_middleware_usage(post_middleware, True, False, True, False) + self.assert_middleware_usage(pre_middleware, True, True, False, True, False) + self.assert_middleware_usage(bad_middleware, True, True, False, True, False) + self.assert_middleware_usage(post_middleware, True, False, False, True, False) + + def test_process_template_response_bad_middleware(self): + pre_middleware = TestMiddleware() + bad_middleware = BadTemplateResponseMiddleware() + post_middleware = TestMiddleware() + self._add_middleware(post_middleware) + self._add_middleware(bad_middleware) + self._add_middleware(pre_middleware) + self.assert_exceptions_handled('/middleware_exceptions/template_response/', ['Test Template Response Exception']) + + # Check that the right middleware methods have been invoked + self.assert_middleware_usage(pre_middleware, True, True, False, False, False) + self.assert_middleware_usage(bad_middleware, True, True, True, False, False) + self.assert_middleware_usage(post_middleware, True, True, True, False, False) def test_process_response_bad_middleware(self): pre_middleware = TestMiddleware() @@ -448,9 +517,9 @@ class BadMiddlewareTests(BaseMiddlewareExceptionTest): self.assert_exceptions_handled('/middleware_exceptions/view/', ['Test Response Exception']) # Check that the right middleware methods have been invoked - self.assert_middleware_usage(pre_middleware, True, True, False, False) - self.assert_middleware_usage(bad_middleware, True, True, True, False) - self.assert_middleware_usage(post_middleware, True, True, True, False) + self.assert_middleware_usage(pre_middleware, True, True, False, False, False) + self.assert_middleware_usage(bad_middleware, True, True, False, True, False) + self.assert_middleware_usage(post_middleware, True, True, False, True, False) def test_process_exception_bad_middleware(self): pre_middleware = TestMiddleware() @@ -462,9 +531,9 @@ class BadMiddlewareTests(BaseMiddlewareExceptionTest): self.assert_exceptions_handled('/middleware_exceptions/view/', []) # Check that the right middleware methods have been invoked - self.assert_middleware_usage(pre_middleware, True, True, True, False) - self.assert_middleware_usage(bad_middleware, True, True, True, False) - self.assert_middleware_usage(post_middleware, True, True, True, False) + self.assert_middleware_usage(pre_middleware, True, True, False, True, False) + self.assert_middleware_usage(bad_middleware, True, True, False, True, False) + self.assert_middleware_usage(post_middleware, True, True, False, True, False) def test_process_request_bad_middleware_not_found(self): pre_middleware = TestMiddleware() @@ -476,9 +545,9 @@ class BadMiddlewareTests(BaseMiddlewareExceptionTest): self.assert_exceptions_handled('/middleware_exceptions/not_found/', ['Test Request Exception']) # Check that the right middleware methods have been invoked - self.assert_middleware_usage(pre_middleware, True, False, True, False) - self.assert_middleware_usage(bad_middleware, True, False, True, False) - self.assert_middleware_usage(post_middleware, False, False, True, False) + self.assert_middleware_usage(pre_middleware, True, False, False, True, False) + self.assert_middleware_usage(bad_middleware, True, False, False, True, False) + self.assert_middleware_usage(post_middleware, False, False, False, True, False) def test_process_view_bad_middleware_not_found(self): pre_middleware = TestMiddleware() @@ -490,9 +559,9 @@ class BadMiddlewareTests(BaseMiddlewareExceptionTest): self.assert_exceptions_handled('/middleware_exceptions/not_found/', ['Test View Exception']) # Check that the right middleware methods have been invoked - self.assert_middleware_usage(pre_middleware, True, True, True, False) - self.assert_middleware_usage(bad_middleware, True, True, True, False) - self.assert_middleware_usage(post_middleware, True, False, True, False) + self.assert_middleware_usage(pre_middleware, True, True, False, True, False) + self.assert_middleware_usage(bad_middleware, True, True, False, True, False) + self.assert_middleware_usage(post_middleware, True, False, False, True, False) def test_process_response_bad_middleware_not_found(self): pre_middleware = TestMiddleware() @@ -504,9 +573,9 @@ class BadMiddlewareTests(BaseMiddlewareExceptionTest): self.assert_exceptions_handled('/middleware_exceptions/not_found/', ['Test Response Exception']) # Check that the right middleware methods have been invoked - self.assert_middleware_usage(pre_middleware, True, True, False, True) - self.assert_middleware_usage(bad_middleware, True, True, True, True) - self.assert_middleware_usage(post_middleware, True, True, True, True) + self.assert_middleware_usage(pre_middleware, True, True, False, False, True) + self.assert_middleware_usage(bad_middleware, True, True, False, True, True) + self.assert_middleware_usage(post_middleware, True, True, False, True, True) def test_process_exception_bad_middleware_not_found(self): pre_middleware = TestMiddleware() @@ -518,9 +587,9 @@ class BadMiddlewareTests(BaseMiddlewareExceptionTest): self.assert_exceptions_handled('/middleware_exceptions/not_found/', ['Test Exception Exception']) # Check that the right middleware methods have been invoked - self.assert_middleware_usage(pre_middleware, True, True, True, False) - self.assert_middleware_usage(bad_middleware, True, True, True, True) - self.assert_middleware_usage(post_middleware, True, True, True, True) + self.assert_middleware_usage(pre_middleware, True, True, False, True, False) + self.assert_middleware_usage(bad_middleware, True, True, False, True, True) + self.assert_middleware_usage(post_middleware, True, True, False, True, True) def test_process_request_bad_middleware_exception(self): pre_middleware = TestMiddleware() @@ -532,9 +601,9 @@ class BadMiddlewareTests(BaseMiddlewareExceptionTest): self.assert_exceptions_handled('/middleware_exceptions/error/', ['Test Request Exception']) # Check that the right middleware methods have been invoked - self.assert_middleware_usage(pre_middleware, True, False, True, False) - self.assert_middleware_usage(bad_middleware, True, False, True, False) - self.assert_middleware_usage(post_middleware, False, False, True, False) + self.assert_middleware_usage(pre_middleware, True, False, False, True, False) + self.assert_middleware_usage(bad_middleware, True, False, False, True, False) + self.assert_middleware_usage(post_middleware, False, False, False, True, False) def test_process_view_bad_middleware_exception(self): pre_middleware = TestMiddleware() @@ -546,9 +615,9 @@ class BadMiddlewareTests(BaseMiddlewareExceptionTest): self.assert_exceptions_handled('/middleware_exceptions/error/', ['Test View Exception']) # Check that the right middleware methods have been invoked - self.assert_middleware_usage(pre_middleware, True, True, True, False) - self.assert_middleware_usage(bad_middleware, True, True, True, False) - self.assert_middleware_usage(post_middleware, True, False, True, False) + self.assert_middleware_usage(pre_middleware, True, True, False, True, False) + self.assert_middleware_usage(bad_middleware, True, True, False, True, False) + self.assert_middleware_usage(post_middleware, True, False, False, True, False) def test_process_response_bad_middleware_exception(self): pre_middleware = TestMiddleware() @@ -560,9 +629,9 @@ class BadMiddlewareTests(BaseMiddlewareExceptionTest): self.assert_exceptions_handled('/middleware_exceptions/error/', ['Error in view', 'Test Response Exception']) # Check that the right middleware methods have been invoked - self.assert_middleware_usage(pre_middleware, True, True, False, True) - self.assert_middleware_usage(bad_middleware, True, True, True, True) - self.assert_middleware_usage(post_middleware, True, True, True, True) + self.assert_middleware_usage(pre_middleware, True, True, False, False, True) + self.assert_middleware_usage(bad_middleware, True, True, False, True, True) + self.assert_middleware_usage(post_middleware, True, True, False, True, True) def test_process_exception_bad_middleware_exception(self): pre_middleware = TestMiddleware() @@ -574,9 +643,9 @@ class BadMiddlewareTests(BaseMiddlewareExceptionTest): self.assert_exceptions_handled('/middleware_exceptions/error/', ['Test Exception Exception']) # Check that the right middleware methods have been invoked - self.assert_middleware_usage(pre_middleware, True, True, True, False) - self.assert_middleware_usage(bad_middleware, True, True, True, True) - self.assert_middleware_usage(post_middleware, True, True, True, True) + self.assert_middleware_usage(pre_middleware, True, True, False, True, False) + self.assert_middleware_usage(bad_middleware, True, True, False, True, True) + self.assert_middleware_usage(post_middleware, True, True, False, True, True) def test_process_request_bad_middleware_null_view(self): pre_middleware = TestMiddleware() @@ -588,9 +657,9 @@ class BadMiddlewareTests(BaseMiddlewareExceptionTest): self.assert_exceptions_handled('/middleware_exceptions/null_view/', ['Test Request Exception']) # Check that the right middleware methods have been invoked - self.assert_middleware_usage(pre_middleware, True, False, True, False) - self.assert_middleware_usage(bad_middleware, True, False, True, False) - self.assert_middleware_usage(post_middleware, False, False, True, False) + self.assert_middleware_usage(pre_middleware, True, False, False, True, False) + self.assert_middleware_usage(bad_middleware, True, False, False, True, False) + self.assert_middleware_usage(post_middleware, False, False, False, True, False) def test_process_view_bad_middleware_null_view(self): pre_middleware = TestMiddleware() @@ -602,9 +671,9 @@ class BadMiddlewareTests(BaseMiddlewareExceptionTest): self.assert_exceptions_handled('/middleware_exceptions/null_view/', ['Test View Exception']) # Check that the right middleware methods have been invoked - self.assert_middleware_usage(pre_middleware, True, True, True, False) - self.assert_middleware_usage(bad_middleware, True, True, True, False) - self.assert_middleware_usage(post_middleware, True, False, True, False) + self.assert_middleware_usage(pre_middleware, True, True, False, True, False) + self.assert_middleware_usage(bad_middleware, True, True, False, True, False) + self.assert_middleware_usage(post_middleware, True, False, False, True, False) def test_process_response_bad_middleware_null_view(self): pre_middleware = TestMiddleware() @@ -619,9 +688,9 @@ class BadMiddlewareTests(BaseMiddlewareExceptionTest): ]) # Check that the right middleware methods have been invoked - self.assert_middleware_usage(pre_middleware, True, True, False, False) - self.assert_middleware_usage(bad_middleware, True, True, True, False) - self.assert_middleware_usage(post_middleware, True, True, True, False) + self.assert_middleware_usage(pre_middleware, True, True, False, False, False) + self.assert_middleware_usage(bad_middleware, True, True, False, True, False) + self.assert_middleware_usage(post_middleware, True, True, False, True, False) def test_process_exception_bad_middleware_null_view(self): pre_middleware = TestMiddleware() @@ -636,9 +705,9 @@ class BadMiddlewareTests(BaseMiddlewareExceptionTest): ValueError()) # Check that the right middleware methods have been invoked - self.assert_middleware_usage(pre_middleware, True, True, True, False) - self.assert_middleware_usage(bad_middleware, True, True, True, False) - self.assert_middleware_usage(post_middleware, True, True, True, False) + self.assert_middleware_usage(pre_middleware, True, True, False, True, False) + self.assert_middleware_usage(bad_middleware, True, True, False, True, False) + self.assert_middleware_usage(post_middleware, True, True, False, True, False) def test_process_request_bad_middleware_permission_denied(self): pre_middleware = TestMiddleware() @@ -650,9 +719,9 @@ class BadMiddlewareTests(BaseMiddlewareExceptionTest): self.assert_exceptions_handled('/middleware_exceptions/permission_denied/', ['Test Request Exception']) # Check that the right middleware methods have been invoked - self.assert_middleware_usage(pre_middleware, True, False, True, False) - self.assert_middleware_usage(bad_middleware, True, False, True, False) - self.assert_middleware_usage(post_middleware, False, False, True, False) + self.assert_middleware_usage(pre_middleware, True, False, False, True, False) + self.assert_middleware_usage(bad_middleware, True, False, False, True, False) + self.assert_middleware_usage(post_middleware, False, False, False, True, False) def test_process_view_bad_middleware_permission_denied(self): pre_middleware = TestMiddleware() @@ -664,9 +733,9 @@ class BadMiddlewareTests(BaseMiddlewareExceptionTest): self.assert_exceptions_handled('/middleware_exceptions/permission_denied/', ['Test View Exception']) # Check that the right middleware methods have been invoked - self.assert_middleware_usage(pre_middleware, True, True, True, False) - self.assert_middleware_usage(bad_middleware, True, True, True, False) - self.assert_middleware_usage(post_middleware, True, False, True, False) + self.assert_middleware_usage(pre_middleware, True, True, False, True, False) + self.assert_middleware_usage(bad_middleware, True, True, False, True, False) + self.assert_middleware_usage(post_middleware, True, False, False, True, False) def test_process_response_bad_middleware_permission_denied(self): pre_middleware = TestMiddleware() @@ -678,9 +747,9 @@ class BadMiddlewareTests(BaseMiddlewareExceptionTest): self.assert_exceptions_handled('/middleware_exceptions/permission_denied/', ['Test Response Exception']) # Check that the right middleware methods have been invoked - self.assert_middleware_usage(pre_middleware, True, True, False, True) - self.assert_middleware_usage(bad_middleware, True, True, True, True) - self.assert_middleware_usage(post_middleware, True, True, True, True) + self.assert_middleware_usage(pre_middleware, True, True, False, False, True) + self.assert_middleware_usage(bad_middleware, True, True, False, True, True) + self.assert_middleware_usage(post_middleware, True, True, False, True, True) def test_process_exception_bad_middleware_permission_denied(self): pre_middleware = TestMiddleware() @@ -692,9 +761,9 @@ class BadMiddlewareTests(BaseMiddlewareExceptionTest): self.assert_exceptions_handled('/middleware_exceptions/permission_denied/', ['Test Exception Exception']) # Check that the right middleware methods have been invoked - self.assert_middleware_usage(pre_middleware, True, True, True, False) - self.assert_middleware_usage(bad_middleware, True, True, True, True) - self.assert_middleware_usage(post_middleware, True, True, True, True) + self.assert_middleware_usage(pre_middleware, True, True, False, True, False) + self.assert_middleware_usage(bad_middleware, True, True, False, True, True) + self.assert_middleware_usage(post_middleware, True, True, False, True, True) _missing = object() diff --git a/tests/regressiontests/middleware_exceptions/urls.py b/tests/regressiontests/middleware_exceptions/urls.py index 5b477a4edeb..37d47f796ae 100644 --- a/tests/regressiontests/middleware_exceptions/urls.py +++ b/tests/regressiontests/middleware_exceptions/urls.py @@ -9,4 +9,7 @@ urlpatterns = patterns('', (r'^error/$', views.server_error), (r'^null_view/$', views.null_view), (r'^permission_denied/$', views.permission_denied), + + (r'^template_response/$', views.template_response), + (r'^template_response_error/$', views.template_response_error), ) diff --git a/tests/regressiontests/middleware_exceptions/views.py b/tests/regressiontests/middleware_exceptions/views.py index 96dbd05095d..ddf28c46a39 100644 --- a/tests/regressiontests/middleware_exceptions/views.py +++ b/tests/regressiontests/middleware_exceptions/views.py @@ -1,9 +1,18 @@ from django import http from django.core.exceptions import PermissionDenied +from django.template import Template +from django.template.response import TemplateResponse + def normal_view(request): return http.HttpResponse('OK') +def template_response(request): + return TemplateResponse(request, Template('OK')) + +def template_response_error(request): + return TemplateResponse(request, Template('{%')) + def not_found(request): raise http.Http404() diff --git a/tests/regressiontests/templates/response.py b/tests/regressiontests/templates/response.py new file mode 100644 index 00000000000..8bdf7f41965 --- /dev/null +++ b/tests/regressiontests/templates/response.py @@ -0,0 +1,174 @@ +import os +from django.utils import unittest +from django.test import RequestFactory +from django.conf import settings +import django.template.context +from django.template import Template, Context, RequestContext +from django.template.response import (TemplateResponse, SimpleTemplateResponse, + ContentNotRenderedError) + +def test_processor(request): + return {'processors': 'yes'} +test_processor_name = 'regressiontests.templates.response.test_processor' + +class BaseTemplateResponseTest(unittest.TestCase): + # tests rely on fact that global context + # processors should only work when RequestContext is used. + + def setUp(self): + self.factory = RequestFactory() + self._old_processors = settings.TEMPLATE_CONTEXT_PROCESSORS + self._old_TEMPLATE_DIRS = settings.TEMPLATE_DIRS + settings.TEMPLATE_CONTEXT_PROCESSORS = [test_processor_name] + settings.TEMPLATE_DIRS = ( + os.path.join( + os.path.dirname(__file__), + 'templates' + ), + ) + # Force re-evaluation of the contex processor list + django.template.context._standard_context_processors = None + + def tearDown(self): + settings.TEMPLATE_DIRS = self._old_TEMPLATE_DIRS + settings.TEMPLATE_CONTEXT_PROCESSORS = self._old_processors + # Force re-evaluation of the contex processor list + django.template.context._standard_context_processors = None + + +class SimpleTemplateResponseTest(BaseTemplateResponseTest): + + def _response(self, template='foo', *args, **kwargs): + return SimpleTemplateResponse(Template(template), *args, **kwargs) + + def test_template_resolving(self): + response = SimpleTemplateResponse('first/test.html') + response.render() + self.assertEqual('First template\n', response.content) + + templates = ['foo.html', 'second/test.html', 'first/test.html'] + response = SimpleTemplateResponse(templates) + response.render() + self.assertEqual('Second template\n', response.content) + + response = self._response() + response.render() + self.assertEqual(response.content, 'foo') + + 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() + self.assertEqual(response.content, 'foo') + + # rebaking doesn't change the rendered content + response.template_name = Template('bar{{ baz }}') + response.render() + self.assertEqual(response.content, 'foo') + + # but rendered content can be overridden by manually + # setting content + response.content = 'bar' + self.assertEqual(response.content, 'bar') + + 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] + self.assertEqual(res, ['foo']) + + 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() + self.assertEqual(response.content, 'foo') + + def test_set_content(self): + # content can be overriden + response = self._response() + self.assertFalse(response.is_rendered) + response.content = 'spam' + self.assertTrue(response.is_rendered) + self.assertEqual(response.content, 'spam') + response.content = 'baz' + self.assertEqual(response.content, 'baz') + + def test_dict_context(self): + response = self._response('{{ foo }}{{ processors }}', + {'foo': 'bar'}) + self.assertEqual(response.context_data, {'foo': 'bar'}) + response.render() + self.assertEqual(response.content, 'bar') + + def test_context_instance(self): + response = self._response('{{ foo }}{{ processors }}', + Context({'foo': 'bar'})) + self.assertEqual(response.context_data.__class__, Context) + response.render() + self.assertEqual(response.content, 'bar') + + def test_kwargs(self): + response = self._response(content_type = 'application/json', status=504) + 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) + + +class TemplateResponseTest(BaseTemplateResponseTest): + + def _response(self, template='foo', *args, **kwargs): + return TemplateResponse(self.factory.get('/'), Template(template), + *args, **kwargs) + + def test_render(self): + response = self._response('{{ foo }}{{ processors }}').render() + self.assertEqual(response.content, 'yes') + + def test_render_with_requestcontext(self): + response = self._response('{{ foo }}{{ processors }}', + {'foo': 'bar'}).render() + self.assertEqual(response.content, 'baryes') + + def test_render_with_context(self): + response = self._response('{{ foo }}{{ processors }}', + Context({'foo': 'bar'})).render() + self.assertEqual(response.content, 'bar') + + def test_kwargs(self): + response = self._response(content_type = 'application/json', + 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) diff --git a/tests/regressiontests/templates/tests.py b/tests/regressiontests/templates/tests.py index 326eb0a4716..405b333addc 100644 --- a/tests/regressiontests/templates/tests.py +++ b/tests/regressiontests/templates/tests.py @@ -28,6 +28,7 @@ from parser import ParserTests from unicode import UnicodeTests from nodelist import NodelistTest from smartif import * +from response import * try: from loaders import * diff --git a/tests/regressiontests/views/templates/debug/render_test.html b/tests/regressiontests/views/templates/debug/render_test.html new file mode 100644 index 00000000000..259cb80a5ca --- /dev/null +++ b/tests/regressiontests/views/templates/debug/render_test.html @@ -0,0 +1 @@ +{{ foo }}.{{ bar }}.{{ baz }}.{{ processors }}