RFC: #12815/#12816 -- TemplateResponse and render() shortcut

676 views
Skip to first unread message

Russell Keith-Magee

unread,
Nov 28, 2010, 2:13:36 AM11/28/10
to Django Developers
Hi all,

For your consideration, I present a patch to resolve #12815 and
#12816, adding a TemplateResponse and a render() shortcut.

Barring objection or feedback, my intention is to commit this patch mid week.

This patch adds two features.

Firstly, it adds a render() shortcut. This is just
render_to_response(), but defaulting to using a RequestContext.

Secondly, it adds a TemplateResponse class. This class is a 'lazy
rendered' response -- you give it a request, template and context, and
it defers rendering until late in the response process.

The original idea for this feature was proposed by Simon Willison. The
purpose is to allow decorators and middleware to modify a response
after the view has finished with it. For example, you may have a
middleware that modifies the template context, or changes the template
depending on request-specific conditions -- for example, moving to
using a different template if a mobile platform is detected.

To do this, a TemplateResponse defers the rendering of a template
until it is 'baked'. A TemplateResponse must be baked before
response.content can be accessed; it is the baking process that
combines the template and context into the final response content.
Once baked, a TemplateResponse cannot be rebaked -- but you can
manually set the response content. Attempts to access response.content
before it is baked will raise an exception.

This patch also introduces a new middleware layer -- the Template
Response middleware. This middleware layer is invoked once the view
has been invoked. When all template_response middlewares have been
invoked, the response is baked, and the response middlewares are
invoked.

This patch also modifies the generic TemplateReponseMixin to use the
new TemplateResponse class. This allows for the removal of several
methods on the TemplateResponseMixin, as their functions are subsumed
by the TemplateResponse itself. If you don't want to use a
TemplateResponse, you can provide your own factory for responses;
because render() has the same signature as the TemplateResponse
constructor, you can use the render shortcut as a substitute.

A big thanks to Mikhail Korobov and Ivan Sagalaev for their input in
the design process, and especially to Mikhail for producing the draft
patch.

For those that followed the discussions and the original draft
patches, some notable changes:

- The render() shortcut doesn't use TemplateResponse. Since render()
and TemplateReponse() have exactly the same prototype, I didn't see
the value in adding a shortcut that just redirected to a different
constructor. However, there is still value in making an easy-to-use
render_to_response, for those situations where a TemplateResponse
can't be used.

- I've disabled repeat baking. My motivation for this is the fact
that there is an automated baking step at the end of the template
response middleware, so the effects of any calls to bake will be
automatically overridden by Django's own stack. By disabling repeat
baking, we can ensure that if the user actually requests baking, that
baking sticks. Manual assignment of response.content can be used to
overcome this -- response.bake() won't rebake content, but
response.content = response.render() will force the new content to be
applied.

Yours,
Russ Magee %-)

Jacob Kaplan-Moss

unread,
Nov 28, 2010, 10:30:19 AM11/28/10
to django-d...@googlegroups.com
Hi Russ --

On Sun, Nov 28, 2010 at 1:13 AM, Russell Keith-Magee
<rus...@keith-magee.com> wrote:
> For your consideration, I present a patch to resolve #12815 and
> #12816, adding a TemplateResponse and a render() shortcut.

Good stuff.

A couple of things though:

* I'm really not a fan of "bake()" and ".baked" -- it really feels too
clever a name to me. I know there's a bit of precedent w.r.t. "baked" and
"fried" responses, but I still think the name's just non-obvious.
Especially to people who don't speak English natively.

Can we just go with something bording like "render()" and ".rendered"?

* I think we should deprecate render_to_response() in favor of render().
render_to_response() is just render(request=None, ...), right? Any
reason to keep both around?

Thanks!

Jacob

sago

unread,
Nov 28, 2010, 11:21:02 AM11/28/10
to Django developers
*very nice* Russ. Congrats and thanks! Totally removes a wart I've
just got used to working around.

"bake" is a pretty universal verb for this, afaict. I wouldn't say it
was clever or new. "fry" however (in Jacob's post) was new to me and
required googling.

Ian.

Ivan Sagalaev

unread,
Nov 28, 2010, 3:21:25 PM11/28/10
to django-d...@googlegroups.com
On 11/28/2010 10:13 AM, Russell Keith-Magee wrote:
> For your consideration, I present a patch to resolve #12815 and
> #12816, adding a TemplateResponse and a render() shortcut.

Thank you!

> - The render() shortcut doesn't use TemplateResponse. Since render()
> and TemplateReponse() have exactly the same prototype, I didn't see
> the value in adding a shortcut that just redirected to a different
> constructor. However, there is still value in making an easy-to-use
> render_to_response, for those situations where a TemplateResponse
> can't be used.

I have a slight (about -0) concern about this� It might turned out as
the same mistake that was made with render_to_response() in the first
place: it wasn't powerful enough (i.e. it didn't use RequestContext) to
be used as a default shortcut. If people would like TemplateResponse
they would be frustrated that render() doesn't use it. Using
TemplateResponse directly is perfectly possible but then we have to
explain to every newcomer why are there two slightly different ways of
doing one thing. I think that cases where one wants to care about using
pure HttpResponse instead of TemplateResponse would be rare and it's
better not be the default behavior.

P.S. Noticed a typo in the patch:

@@ -164,6 +154,13 @@
urlresolvers.set_urlconf(None)

try:
+ # Apply template response middleware and the bake the response
+ # if the response can be baked
+ if hasattr(response, 'bake') and callable(response.bake):

The first line of the comment should be "and the*n* bake the response".

Russell Keith-Magee

unread,
Nov 29, 2010, 6:49:17 AM11/29/10
to django-d...@googlegroups.com
On Sun, Nov 28, 2010 at 11:30 PM, Jacob Kaplan-Moss <ja...@jacobian.org> wrote:
> Hi Russ --
>
> On Sun, Nov 28, 2010 at 1:13 AM, Russell Keith-Magee
> <rus...@keith-magee.com> wrote:
>> For your consideration, I present a patch to resolve #12815 and
>> #12816, adding a TemplateResponse and a render() shortcut.
>
> Good stuff.
>
> A couple of things though:
>
> * I'm really not a fan of "bake()" and ".baked" -- it really feels too
>  clever a name to me. I know there's a bit of precedent w.r.t. "baked" and
>  "fried" responses, but I still think the name's just non-obvious.
>  Especially to people who don't speak English natively.
>
>  Can we just go with something bording like "render()" and ".rendered"?

There is a slight complication -- the current API has a render()
method in addition to bake(). bake() only works once; render() can be
called (and assigned to render.content) at any time to get rendered
content using the current template/context, even if bake() has been
called.

At one point in the history of the patch, the API had a bake() and
force_bake(); I suppose we could use render() and force_render().

> * I think we should deprecate render_to_response() in favor of render().
>  render_to_response() is just render(request=None, ...), right? Any
>  reason to keep both around?

There's no particular reason to keep both around, other than the code
churn that deprecation would entail.

This is something that I have no problem deprecating on the 2.0
schedule, but migrating every use of render_to_response() over the
next 18 months/2 releases seems like an extreme measure to enforce on
the entire user base when maintaining render_to_response() doesn't
take any real effort.

Yours
Russ Magee %-)

Russell Keith-Magee

unread,
Nov 29, 2010, 6:58:56 AM11/29/10
to django-d...@googlegroups.com
On Mon, Nov 29, 2010 at 4:21 AM, Ivan Sagalaev
<man...@softwaremaniacs.org> wrote:
> On 11/28/2010 10:13 AM, Russell Keith-Magee wrote:
>>
>> For your consideration, I present a patch to resolve #12815 and
>> #12816, adding a TemplateResponse and a render() shortcut.
>
> Thank you!
>
>>  - The render() shortcut doesn't use TemplateResponse. Since render()
>> and TemplateReponse() have exactly the same prototype, I didn't see
>> the value in adding a shortcut that just redirected to a different
>> constructor. However, there is still value in making an easy-to-use
>> render_to_response, for those situations where a TemplateResponse
>> can't be used.
>
> I have a slight (about -0) concern about this… It might turned out as the

> same mistake that was made with render_to_response() in the first place: it
> wasn't powerful enough (i.e. it didn't use RequestContext) to be used as a
> default shortcut. If people would like TemplateResponse they would be
> frustrated that render() doesn't use it. Using TemplateResponse directly is
> perfectly possible but then we have to explain to every newcomer why are
> there two slightly different ways of doing one thing. I think that cases
> where one wants to care about using pure HttpResponse instead of
> TemplateResponse would be rare and it's better not be the default behavior.

My counterargument would be this -- if you use TemplateResponse,
there's no need to use a shortcut at all. Unlike HttpResponse, you
don't need to construct a context, load a template and render it. The
constructor for TemplateResponse is all you need.

This is born out by the fact that the TemplateReponse shortcut isn't
even a method definition -- it's a rename:

render = TemplateResponse

I don't see the point in just putting a rename into shortcuts; so we
might as well use the shortcut for something that might be useful --
such as providing a way to easily use ResponseContext on normal
HttpResponses.

> P.S. Noticed a typo in the patch:
>
> @@ -164,6 +154,13 @@
>             urlresolvers.set_urlconf(None)
>
>         try:
> +            # Apply template response middleware and the bake the response
> +            # if the response can be baked
> +            if hasattr(response, 'bake') and callable(response.bake):
>
> The first line of the comment should be "and the*n* bake the response".

Thanks - I've fixed this in my copy.

Yours,
Russ Magee %-)

Ivan Sagalaev

unread,
Nov 29, 2010, 7:09:34 AM11/29/10
to django-d...@googlegroups.com
On 11/29/2010 02:58 PM, Russell Keith-Magee wrote:
> My counterargument would be this -- if you use TemplateResponse,
> there's no need to use a shortcut at all.

Yes, this is what I understood from your reasoning. I'm concerned more
with documentation. Namely, what are we going to suggest for usage in
the tutorial. The nice short `render()` shortcut lacks the power of
TemplateResponse and TemplateResponse is not *that* nice and short.

Harro

unread,
Nov 29, 2010, 9:07:41 AM11/29/10
to Django developers
I agree with Jacob on the bake/baked thing.. but maybe it's just
CakePHP coming to haunt me :S

Mikhail Korobov

unread,
Dec 1, 2010, 2:55:35 AM12/1/10
to Django developers
Just for the record: I'm with Ivan here and think that

from django.template.response import TemplateResponse
def my_view(request):
return TemplateResponse(request, 'foo.html')

is worse than

from django.shortcuts import render
def my_view(request):
return render(request, 'foo.html')

I think that the cases where user should prefer 'render' returning
HttpResponse over 'render' returning TemplateResponse are rare so the
API by itself should suggest TemplateResponse. TemplateResponse is
more flexible and can provide a performance benefit (the template
rendering can be prevented in some cases).

I don't see much value in adding 'render' shortcut that just use
RequestContext after TemplateResponse is introduced - one can still
use render_to_response if TemplateResponse is undesired and one
shouldn't use such 'render' in other cases.

'render' as alias for TemplateResponse seems fine for me: it is a
shortcut and it adds value by simplifying the API.

Russell Keith-Magee

unread,
Dec 1, 2010, 5:03:42 AM12/1/10
to django-d...@googlegroups.com
On Wed, Dec 1, 2010 at 3:55 PM, Mikhail Korobov <kmi...@googlemail.com> wrote:
> Just for the record: I'm with Ivan here and think that
>
> from django.template.response import TemplateResponse
> def my_view(request):
>    return TemplateResponse(request, 'foo.html')
>
> is worse than
>
> from django.shortcuts import render
> def my_view(request):
>    return render(request, 'foo.html')
>
> I think that the cases where user should prefer 'render' returning
> HttpResponse over 'render' returning TemplateResponse are rare so the
> API by itself should suggest TemplateResponse. TemplateResponse is
> more flexible and can provide a performance benefit (the template
> rendering can be prevented in some cases).
>
> I don't see much value in adding 'render' shortcut that just use
> RequestContext after TemplateResponse is introduced - one can still
> use render_to_response if TemplateResponse is undesired and one
> shouldn't use such 'render' in other cases.
>
> 'render' as alias for TemplateResponse seems fine for me: it is a
> shortcut and it adds value by simplifying the API.

I'd argue it doesn't simplify anything. It saves you a grand total of
10 characters (plus a couple more on import), but at the cost of the
added complexity of having two ways of doing *exactly* the same thing.
There is also a loss of explicitness -- there's no doubting what
TemplateResponse will return.

On the other hand, there *is* value in adding a render() shortcut --
because there will be a subset of cases where a TemplateResponse isn't
needed, but a HttpResponse with a RequestContext is.

Yours,
Russ Magee %-)

bur...@gmail.com

unread,
Dec 1, 2010, 5:41:34 AM12/1/10
to django-d...@googlegroups.com
Hi Russell,

If I got it right, you want to make default shortcut render() that
would not use TemplateResponse.
But then how one will alter 3rd-party rendered responses in middleware?
I suggest people use TemplateResponse by default, and making render =
TemplateResponse just to be sure of this,
and render_to_response is "just 10 more characters" or a line to make
your own render that uses RequestContext.
I think, talking about "saving few characters" is not constructive.
So I'm talking about good architecture and code beauty.
I think your will to make render=render_to_response with
RequestContext means you want people to use render_to_response by
default.
But I'd like if we have a way to make template creators to use
TemplateResponse by default. That would encourage better view reusing
in django. And that means we would have render=TemplateResponse.

However, render_to_response has another feature -- ability to use
custom context, and the one real use case is using Context for Http500
when you can't rely on anything.

--
Best regards, Yuri V. Baburov, ICQ# 99934676, Skype: yuri.baburov,
MSN: bu...@live.com

Ivan Sagalaev

unread,
Dec 1, 2010, 6:30:26 AM12/1/10
to django-d...@googlegroups.com
On 12/01/2010 01:03 PM, Russell Keith-Magee wrote:
> I'd argue it doesn't simplify anything. It saves you a grand total of
> 10 characters (plus a couple more on import), but at the cost of the
> added complexity of having two ways of doing *exactly* the same thing.
> There is also a loss of explicitness -- there's no doubting what
> TemplateResponse will return.
>
> On the other hand, there *is* value in adding a render() shortcut --
> because there will be a subset of cases where a TemplateResponse isn't
> needed, but a HttpResponse with a RequestContext is.

I'd argue :-) that these cases would be quite rare (in fact I can't even
imagine one). And it'd be pity to use such delicious shortcut as
'render()' for the rare case. Especially if we would recommend it as the
default way of returning responses from users' views because then almost
nobody would use TemplateResponse thus defeating its value altogether.

I understand why you don't like `render = TemplateResponse` but I think
it's a minimal disturbance given the value that it provides.

Anyway I believe everyone here has expressed their points of view
completely so now it's a judgement call for a committer.

Łukasz Rekucki

unread,
Dec 1, 2010, 6:52:45 AM12/1/10
to django-d...@googlegroups.com
On 1 December 2010 11:41, bur...@gmail.com <bur...@gmail.com> wrote:
> Hi Russell,
>
> On Wed, Dec 1, 2010 at 4:03 PM, Russell Keith-Magee
> <rus...@keith-magee.com> wrote:
>> On Wed, Dec 1, 2010 at 3:55 PM, Mikhail Korobov <kmi...@googlemail.com> wrote:
>>> Just for the record: I'm with Ivan here and think that
>>>
>>> from django.template.response import TemplateResponse
>>> def my_view(request):
>>>    return TemplateResponse(request, 'foo.html')
>>>
>>> is worse than
>>>
>>> from django.shortcuts import render
>>> def my_view(request):
>>>    return render(request, 'foo.html')

I disagree. "TemplateResponse" is more explicit and readable. render()
is confusing, because it doesn't actually render anything - it creates
a lazy response.

>>>
>>> I think that the cases where user should prefer 'render' returning
>>> HttpResponse over 'render' returning TemplateResponse are rare so the
>>> API by itself should suggest TemplateResponse. TemplateResponse is
>>> more flexible and can provide a performance benefit (the template
>>> rendering can be prevented in some cases).
>>>
>>> I don't see much value in adding 'render' shortcut that just use
>>> RequestContext after TemplateResponse is introduced - one can still
>>> use render_to_response if TemplateResponse is undesired and one
>>> shouldn't use such 'render' in other cases.

Lots of people find render_to_response so verbose, that they are using
direct_to_template() view instead. Django 1.3 deprecates that one, so
it would be fair to offer those people a replacement. Forcing them at
the same time into the world of lazy baked responses isn't very fair,
imho.

>>>
>>> 'render' as alias for TemplateResponse seems fine for me: it is a
>>> shortcut and it adds value by simplifying the API.
>>
>> I'd argue it doesn't simplify anything. It saves you a grand total of
>> 10 characters (plus a couple more on import), but at the cost of the
>> added complexity of having two ways of doing *exactly* the same thing.
>> There is also a loss of explicitness -- there's no doubting what
>> TemplateResponse will return.

I agree :).

>>
>> On the other hand, there *is* value in adding a render() shortcut --
>> because there will be a subset of cases where a TemplateResponse isn't
>> needed, but a HttpResponse with a RequestContext is.

I think that subset is quite large (basicaly, I didn't found a use
case for a TemplateResponse in my apps yet).

>>
>> Yours,
>> Russ Magee %-)
>
> If I got it right, you want to make default shortcut render() that
> would not use TemplateResponse.
> But then how one will alter 3rd-party rendered responses in middleware?

You can always alter the 3-rd party view to render the response a bit
differently, which should be easy with class-based views now.

> I suggest people use TemplateResponse by default, and making render =
> TemplateResponse just to be sure of this,
> and render_to_response is "just 10 more characters" or a line to make
> your own render that uses RequestContext.
> I think, talking about "saving few characters" is not constructive.
> So I'm talking about good architecture and code beauty.

Having basicly the same thing under 2 names is not code beauty, imho -
but I don't think we should go this way in this discussion.

> I think your will to make render=render_to_response with
> RequestContext means you want people to use render_to_response by
> default.

I would like to doing, what I did with direct_to_template() right now.
If I decide TemplateResponse is a better alternative, I'll switch to
that.

> But I'd like if we have a way to make template creators to use
> TemplateResponse by default. That would encourage better view reusing
> in django. And that means we would have render=TemplateResponse.

There is not point in having render shortcut then (apart from saving
10 characters). You can just tell people to use TempateResponse.

Generally, I don't like the current render() that much, 'cause it
still doesn't let you render a page with status different from 200
(something simillar to http://code.djangoproject.com/ticket/9081)

--
Łukasz Rekucki

Ivan Sagalaev

unread,
Dec 1, 2010, 7:51:02 AM12/1/10
to django-d...@googlegroups.com
On 12/01/2010 02:52 PM, Łukasz Rekucki wrote:
> Lots of people find render_to_response so verbose, that they are using
> direct_to_template() view instead. Django 1.3 deprecates that one, so
> it would be fair to offer those people a replacement. Forcing them at
> the same time into the world of lazy baked responses isn't very fair,
> imho.

It's not "forcing". TemplateResponse is a transparent replacement for
HttpResponse. Normal response middleware shouldn't event notice that
it's working with a TemplateResponse (modulo possible bugs in its
implementation).

Łukasz Rekucki

unread,
Dec 1, 2010, 8:26:34 AM12/1/10
to django-d...@googlegroups.com

What about view decorators?

I don't want to invent use cases here, but if I right now have a view
decorator that on it's way out changes something that could alter how
a template is rendered (like current language, some settings or the
database contents), then changing to TemplateResponse will break this
view. It's not entirely transparent, because rendering doesn't depend
only on the template and it's context. This adds some complexity to
writing view decorators and I don't know if I want to pay it for a
feature I have no use case atm.

--
Łukasz Rekucki

Ivan Sagalaev

unread,
Dec 1, 2010, 8:37:23 AM12/1/10
to django-d...@googlegroups.com
On 12/01/2010 04:26 PM, Łukasz Rekucki wrote:
> What about view decorators?
>
> I don't want to invent use cases here, but if I right now have a view
> decorator that on it's way out changes something that could alter how
> a template is rendered (like current language, some settings or the
> database contents), then changing to TemplateResponse will break this
> view.

If you're talking about "right now" and "alter how a template is
rendered" then this view either doesn't return an HttpResponse instance
or returns its custom descendant. Hence such view won't be affected by
introduction of a new shortcut or TemplateResponse.

What I'm talking about is that currently a middleware or a decorator
that expects a normal HttpResponse instance can only access its headers
or completely rendered content. The laziness of TemplateResponse can't
break it.

May be I don't understand what you're trying to show here. Can you
provide a small example?

Łukasz Rekucki

unread,
Dec 1, 2010, 9:05:38 AM12/1/10
to django-d...@googlegroups.com

Sure. Maybe I'm just missing something (note: this doesn't have to be
a setting - any side effect that can affect rendering, which includes
the database).

from django.conf import settings

def without_localization(view):
@wraps(view):
def decorated(*args, **kwargs):
# NOTE: I'm assuming this will actually have any effect -
settings caching is a different issue
old_value, settings.USE_L10N = settings.USE_L10N, False
try:
# If view uses HttpResponse, the template will be rendered
with USE_L10N = False
# If it uses TemplateResponse, nothing will be rendered (yet!)
return view(*args, **kwargs)
finally:
# USE_L10N will be back to it's original value
# and that value will be used at the time of baking
settings.USE_L10N = old_value
return decorated

--
Łukasz Rekucki

Ivan Sagalaev

unread,
Dec 1, 2010, 9:53:09 AM12/1/10
to django-d...@googlegroups.com
On 12/01/2010 05:05 PM, Łukasz Rekucki wrote:
> from django.conf import settings
>
> def without_localization(view):
> @wraps(view):
> def decorated(*args, **kwargs):
> # NOTE: I'm assuming this will actually have any effect -
> settings caching is a different issue
> old_value, settings.USE_L10N = settings.USE_L10N, False
> try:
> # If view uses HttpResponse, the template will be rendered
> with USE_L10N = False
> # If it uses TemplateResponse, nothing will be rendered (yet!)
> return view(*args, **kwargs)
> finally:
> # USE_L10N will be back to it's original value
> # and that value will be used at the time of baking
> settings.USE_L10N = old_value
> return decorated

Ugh :-(. Thanks, I'll keep it in my notebook of examples where
imperative style sucks…

But I see your point now. Indeed there are cases where TemplateResponse
is not a transparent replacement for HttpResponse. I'll crawl back to my
cave now and cry a little.

Reply all
Reply to author
Forward
0 new messages