13.14 SimpleTemplateResponse and TemplateResponse
SimpleTemplateResponse and TemplateResponse are responses whose content is a template not yet rendered. They keep the template and the context, render them when SimpleTemplateResponse is called, run their post-render callbacks after it, and raise ContentNotRenderedError if their content is read before it.
A view that renders its template itself returns a finished response: nothing after the view can change the template or the context. A response that holds the ingredients lets the middleware’s process_template_response hooks, and the decorators made from middleware, change the template or the context after the view has returned. The handler renders it once they have run (The centre of the chain); django renders at once (The shortcuts).
What the response holds
SimpleTemplateResponse is an HttpResponse that keeps what it will render with: the template as SimpleTemplateResponse, the context as SimpleTemplateResponse, an engine’s alias as SimpleTemplateResponse, and the request as SimpleTemplateResponse, which it sets to None. TemplateResponse takes the request as its first argument and stores it there.
In rendering, the request is the whole difference between the two. It goes to the template’s render, and the Django backend makes a RequestContext from it, so that the engine’s context processors run, or a plain Context when there is none (make_context; The context: Context, RequestContext and context processors). With the processor that adds the request installed, {{ request prints the path from a TemplateResponse and nothing from a SimpleTemplateResponse.
Rendering late: rendered_content
SimpleTemplateResponse is a property that renders the template and returns the string without keeping it. It resolves both ingredients when read, so a hook that assigns SimpleTemplateResponse or changes SimpleTemplateResponse before the render changes what is rendered.
SimpleTemplateResponse gives a list or a tuple of names to select_template and a string to get_template, each with SimpleTemplateResponse, and returns anything else as it is. So a missing template is an error only at render time. Any other object should be a backend’s template, whose render takes a dictionary and a request; a Template takes a context alone, and a response holding one raises TypeError when rendered.
SimpleTemplateResponse returns the context unchanged, for a subclass to override. The Django backend takes a dictionary or None, and refuses a Context with TypeError: context must be a dict rather than Context.
render and the post-render callbacks
SimpleTemplateResponse does its work once. On a response not yet rendered it assigns SimpleTemplateResponse to the content, then calls each callback in SimpleTemplateResponse, in the order they were added. A callback that returns anything but None replaces the response: the callbacks after it are given the replacement, and render returns it, which is what the handler keeps. Called again, render does nothing and returns the response itself, not that replacement.
SimpleTemplateResponse appends a callback, or, if the response is already rendered, calls it at once and discards what it returns. Django adds callbacks in two places:
- The view’s wrapper in a decorator made from a middleware registers the middleware’s
process_responseas a callback on a template response, so that it sees the rendered content (A middleware as a decorator). UpdateCacheMiddlewarecaches a response with a callable.process_response renderthrough a callback. It does not ask whether the response is rendered, and one from a view has been by the time aprocess_responsesees it, in the chain or undercache_page; so the callback runs at once.
Two other places render a template response after asking SimpleTemplateResponse: response_for_exception, when an error view returned one, and the test case’s assertions on content.
Content refused before a render: ContentNotRenderedError
SimpleTemplateResponse turns true in one place: the setter of SimpleTemplateResponse, which stores the value as HttpResponse does and marks the response rendered. The empty content the constructor passes to HttpResponse goes through that setter too, so the constructor sets the flag back to false after it. Until a render the getter of content and SimpleTemplateResponse raise ContentNotRenderedError, and so does whatever reads content, HttpResponse and HttpResponse among them.
Pickling: __getstate__ and rendering_attrs
SimpleTemplateResponse refuses to pickle a response that is not rendered, with ContentNotRenderedError. From a rendered one it leaves out the attributes named in SimpleTemplateResponse: template_name, context_data and _post_render_callbacks, and for a TemplateResponse, whose list adds it, _request. Its docstring: pickle only rendered data, not the data used to construct the response. Unpickled, it is a finished response: SimpleTemplateResponse returns it unchanged, and a callback added to it runs at once. A response the cache middleware stores is pickled by the local-memory backend, for one.