It's one of the things on my list. If you'd like to make it happen
faster, a patch + tests would make it a no-brainer for me.
Jacob
I agree completely with this reasoning - just render returning an
HttpResponse is fine, I think.
Jacob
Both render_to_response() and direct_to_template() have one very
annoying flaw: http://code.djangoproject.com/ticket/12669. Please add
a "response_class" keyword to your render() function ;). Thanks!
--
Łukasz Rekucki
Wait!!!
Sorry… Hello everyone :-)
If I remember correctly TemplateResponse was solving a problem of some
middleware wanting to mess with a view context before it's baked into
final string representation. This would solve in a good way what now is
accomplished by things like @render_to decorator.
What I don't understand from your reasoning is how class-based views are
going to help here? From what I see only Django-supplied generic views
are now class-based and we didn't deprecate simple user view functions.
Which means that "render" won't be as useful for them if it wouldn't
provide any means to post-process the context and if the context won't
be aware of request: these are two main points why people are not happy
with render_to_response right now.
Mikhail, do you have any actual objections against TemplateResponse or
you just don't want to complicate your implementation? If it is the
latter then TemplateResponse has been already implemented[1] by Simon
Willison. You might just use it.
[1]:
http://github.com/simonw/django-openid/blob/master/django_openid/response.py
This is where our views differ. I have to admit that I didn't follow
closely the process of designing class-based generic views but I never
had a feeling that the class-based approach was now somehow recommended
way to create views in general. If it's the case it would be the most
significant change in overall Django design since introducing meta-class
based models (and a really bad change if you ask me).
Can someone of core committers clarify this: is it now recommended to
write all views as classes?
> b) The main benefit from TemplateResponse is the ability to reuse view
> responses. I made this assumption because of the example in #12815,
> the 'this pattern would be particularly valuable for customising the
> admin' statement and the original example in your proposal (
> http://groups.google.com/group/django-developers/msg/d5df254f01800ee2
> ).
Yes this is the main benefit. The example with middleware from my
previous email was misleading, sorry.
> Thanks for pointing out the implemented TemplateResponse. No, I
> haven't actual objections against it and just don't want to complicate
> the implementation. There was an issue with TemplateResponse approach
> for Ben Firshman ( http://groups.google.com/group/django-developers/msg/fc9e0f8810d3e784
> ) and the ticket is in DDN so it is not as simple as just replace
> HttpResponse with TemplateResponse.
I can't test it right now myself but from what I remember exceptions in
the response middleware were always handled. And in the 1.2 cycle we've
fixed it for request middleware too. So may be the problem doesn't even
exist anymore. Can you point me to that ticket which is in DDN so I
could test it?
Django cares about whether your views meet the following criteria:
1. Is a callable object.
2. When called, accepts an instance of HttpRequest as its first
positional argument.
3. When called, returns an instance of HttpResponse or raises an exception.
That's it. Write them as functions, if that makes sense for your use
case. Write them as classes with callable instances, if that makes
sense for your use case. Generic views are now class-based because
that's what seems to work best *for the case of generic views*;
subclassing and overriding is, for the type of flexibility the generic
views need, simpler and cleaner than supporting long lists of keyword
arguments. But the generic views are not all views, and not all views
have to do what the generic views do. So my position is that you
should think about what you need from your view, and choose
class-based or function-based as appropriate.
--
"Bureaucrat Conrad, you are technically correct -- the best kind of correct."
Thanks, that what I thought.
Going back to the argument about TemplateResponse I can say that it's a
good way for a view-as-function to keep its response hackable.
I'll look into it this evening (MSD).
So I did.
There are actually two problems:
- Exceptions in response middleware are indeed happen outside of the
request's `try .. except` block. This is a problem by itself[1] and I'd
be happy to fix it after ticket 9886[2] is committed. It's another
refactoring of core request code so I don't want to mess things up doing
one patch on top of another :-).
- Non-pretty plain text tracebacks can be caused not only be middleware
but also by any error occurring during template rendering. Because all
this happen *after* request was returned to the web server handler and
is being iterated over.
This second problem can be easily fixed by introducing a method for
explicit evaluation of the content: `evaluate()` or `force_content()`
that will be a noop for any HttpRespone class except the
TemplateResponse. The method will be called by the request handler right
before returning the response.
Sounds good?
P.S. BTW looking at the TemplateResponse implementation I see that Simon
had actually intended it to be effectively *the* render shortcut[3]. I
find it quite beautiful really :-)
[1]: http://code.djangoproject.com/ticket/14523
[2]: http://code.djangoproject.com/ticket/9886
[3]:
http://github.com/simonw/django-openid/blob/master/django_openid/response.py#L85
Hi Ivan and Mikhail,
Just so you don't get the feeling that you're just discussing this
between yourselves -- I'm historically on record of being in favor of
the render() shortcut and the TemplateResponse(), and my position
hasn't changed recently. This is exactly the sort of problem that I
want to target for 1.3 (especially since we're now tracking 6 closely
related tickets -- #9081, #9886, #12815, #12816, #12669 and #14523 --
making this a sweet spot for attention).
Jacob has already marked #9886 RFC, and on first inspection, the patch
looks good to me too; I want to have a closer look before I commit,
though. If you want to proceed assuming that #9886 will be committed
(i.e., make the fix for #14523 have #9866 as a prerequisite), I doubt
it would be wasted effort.
Regarding the second problem you describe -- unless I'm mistaken,
Simon's TemplateResponse addresses this with his "baking" concept; a
response is baked the first time it is iterated or otherwise
evaluated.
I have a couple of non-Django things to attend to over the next week
or so; but once those are sorted out, I should be able to take a
detailed look at any patches you have prepared.
Yours,
Russ Magee %-)
This is starting to look quite good. Some quick review comments:
* Integration with generic views. I would have thought that
TemplateResponseMixin would be a natural place to be using a
TemplateResponse.
* Tests can't rely on assumed context processors, as the comment in
the TemplateResponse tests states. See the flatpages views tests for
an example of how to work around this sort of problem.
* MOAR TESTS!1!! :-) There are a couple of API points that aren't
tested, such as set_content(), force_bake() and iteration over content
as a baking trigger. There might be a couple of others; these are just
the ones that jumped out at me.
* Docs! I appreciate that writing docs is premature when we're still
fiddling with details, but I think the core API is getting close, so
now would be a good time to at least put in some stub documentation
indicating what needs to be filled out later.
Yours,
Russ Magee %-)
Yours,
Russ Magee %-)
Is this addressed by the status_code argument to TemplateResponse? i.e.,
return TemplateResponse(request, template, context, status_code=403)
We could also include some constants to make things a little prettier:
return TemplateResponse(request, template, context, status_code=FORBIDDEN)
Would that satisfy your use case for #12669?
Yours,
Russ Magee %-)
On 10/22/2010 05:20 AM, Russell Keith-Magee wrote:
> Jacob has already marked #9886 RFC, and on first inspection, the patch
> looks good to me too; I want to have a closer look before I commit,
> though. If you want to proceed assuming that #9886 will be committed
> (i.e., make the fix for #14523 have #9866 as a prerequisite), I doubt
> it would be wasted effort.
I didn't feel it would be wasted :-). I was going to start doing the new
patch on top of my local bzr branch with #9886 applied (arent' DVCSes
awesome?). What I meant is that I didn't want to actually attach a diff
file to the ticket yet because it has chances of not being applicable
against trunk.
> Regarding the second problem you describe -- unless I'm mistaken,
> Simon's TemplateResponse addresses this with his "baking" concept; a
> response is baked the first time it is iterated or otherwise
> evaluated.
The problem is that this iteration may (and does) happen after response
is returned from the handler � and hence outside the `try .. except`.
The point is to call baking explicitly right before the return.
Yes, it does, but constants are a must have, imho.
Also, excuse my ignorance, but even after reading this thread, code
and tests I don't understand what are the use-cases for
TemplateResponse and why I would want to use a one in a CBV. Some
examples would be helpful.
--
Łukasz Rekucki
The use case is to be able to modify the way that content will be
rendered *after* a view has finished processing, instead of the view
producing a block of rendered HTML (or whatever) that response
middlewares and decorators must accept without question. By deferring
the rendering process, you can make changes such as adding items or
changing items in the context, or changing the template that is used
to render the result.
Yes, you could get the same result by introducing an additional mixin
to a generic view. However, a TemplateResponse allows you to implement
the functionality as a decorator, rather than subclassing; and it can
be used on all views, not just class-based views.
Yours,
Russ Magee %-)
I would have thought so. Is there a compelling reason why CBV's
shouldn't return a TemplateResponse?
Yours,
Russ Magee %-)
This is starting to look good to me; here are some comments, going
back a couple of messages:
* Yes, you've got the right idea with regards to the role played by
the various TemplateResponseMixin methods
* It seems reasonable to me that assertTemplateUsed would require
some baking, and yes, that should be happening at the test client
before template rendering signals are disconnected.
* The problem with messages is a big one -- probably even a
show-stopper if we can't find a way to reconcile the general use case
that it represents (i.e., we don't just need a fix for
contrib.messages -- we need to explain how/why the problem exists,
and provide a consistent approach for analogous problems)
* Following the convention of the rest of the API, the call to
get_template_names() should be internal to get_response(), not passed
in as an argument.
* I'm not entirely convinced that get_response() is needed now. As
your implementation currently stands, render_to_response() is just a
call to get_response() -- which suggests that the extra level of
indirection isn't needed.
Backing up this position -- most of the flexibility that
TemplateResponseMixin has is to enable the easy integration of
different rendering engines and contexts; those API points are now
provided by TemplateResponse, so there isn't any need to preserve them
in the mixin. If you want to use a different loader, template
renderer, context instance, etc, you subclass TemplateResponse.
So - revised source code:
class TemplateResponseMixin(object):
"""
A mixin that can be used to render a template.
"""
template_name = None
template_response_class = TemplateResponse
def render_to_response(self, context):
"""
Returns a response with a template rendered with the given context.
"""
return self.template_response_class(
request=self.request,
template=self.get_template_names(),
context=context,
**response_kwargs
)
def get_template_names(self):
"""
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]
However, all this is a moot point if we can't find a fix for the
contrib.messages problem.
Yours,
Russ Magee %-)
I apologize in advance if I missed some important bits of the
conversation � I'm on vacation right now :-).
Why can't we teach messages middleware to just explicitly bake a
response and call it a proper solution? By adding `force_bake` to the
HttpResponse class itself we effectively declare that *any* HttpResponse
can be lazy and middleware that expects some side-effects from a
response has to bake it.
Sure it must be documented that middleware forcing response baking
should work after the ones that don't.
I understand your points now, thanks. Two things bother me about
'border' middleware:
- its semantics is a bit different than that of others middleware in the
list in settings and this difference is not explicitly clear when
looking at the list
- it's bad to have a boilerplate code that people just have to put somewhere
I've spent a night with a thought and now I think I can propose even
better solution.
We can introduce a new kind of middleware — "template response
middleware" for lack of a better name. A user who wants to do something
with a template response *before* it is baked has to write a middleware
like this:
class ContextInjectionMiddleware:
def process_template_response(self, request, response):
# do something with response
Request handling code would look like this then:
response = get_response(...)
if hastattr(response, 'force_bake'):
# apply template response middleware
response.force_bake()
# apply normal response middleware
This way we:
- are getting rid of force_bake in HttpResponse where it's a noop
- maintain backward compatibility since response is baked before all
currently written middleware
- require explicitly named method to deal with a new concept
What's not to like? :-)
I'd say it's even worth to wait for
http://code.djangoproject.com/ticket/14523 that moves response
middleware application into the base code.
I like this idea -- it's is an elegant solution to the problem, and
avoids all the backwards compatibility issues I can think of.
I have two comments:
Firstly, there needs to be a shortcut for non-template responses. If
your response isn't a template response, there's no point putting it
through Template Reponse Middleware.
Secondly, it seems to me like there may be some need for baking
protection here. If any template-response-middleware were to bake the
response, subsequent template-reposnse-middlewares could potentially
have problems, as any changes they make to context etc will be
ignored. Wouldn't it make sense to put a flag on the TemplateResponse
that prohibits accidental baking? That way the force_bake() that
happens between the template-response-middleware and the normal
response-middleware would be the guaranteed point at which the
template is writ large as content.
Regarding #9886 and #14523 -- they're both RFC, and they're on my todo
list of things to commit in the near future. I just need to find a few
spare moments to give the patches a final review and commit.
Yours,
Russ Magee %-)
Ah - I wasn't aware there was a working implementation of this idea --
did I miss a link somewhere?
>> Secondly, it seems to me like there may be some need for baking
>> protection here. If any template-response-middleware were to bake the
>> response, subsequent template-reposnse-middlewares could potentially
>> have problems, as any changes they make to context etc will be
>> ignored. Wouldn't it make sense to put a flag on the TemplateResponse
>> that prohibits accidental baking? That way the force_bake() that
>> happens between the template-response-middleware and the normal
>> response-middleware would be the guaranteed point at which the
>> template is writ large as content.
>>
>
> As I can see, users shouldn't bake responses not only in middlewares.
> They shouldn't bake responses anywhere in their code.
>
> The original TemplateResponse idea was not the same.
Agreed. This is a change from the original completely-lazy-evaluated
TemplateResponse idea, but I think it makes sense in terms of being
explicit and avoiding a wealth of potential bugs in implicit
evaluation.
> So maybe it will be better not to make bake/force_bake public so that
> users won't be able to shoot themselves in the foot? And maybe it'll
> be better even not to bake response magically on first content
> access?
That's essentially what I was suggesting -- that if a middleware or
decorator accidental accessed response.content before the end of
template-context-processor handling (when the explicit baking occurs),
it should raise an exception rather than implicitly baking the
response.
Yours,
Russ Magee %-)
Mikhail:
> So maybe it will be better not to make bake/force_bake public so that
> users won't be able to shoot themselves in the foot?
I don't think it's doable at all. People still can call any method in as
well as they can ignore or alter any protection flag. I believe it's
sufficient to abide to the Python way here, write a proper documentation
and treat people as grown-ups trusting them not to do bad things in
their process_template_response.
I agree that it's important to treat people as grown ups. However,
this is something that is trivial to do by accident -- for example,
printing response.content would be an obvious debug step -- and it
will be a non-trivial thing to identify that this is the cause of your
problems.
Maybe an unconditional exception is a little strong, but some sort of
safety catch seems called for.
Yours,
Russ Magee %-)
Aha, I see the point now. On a second thought I think we can avoid this
problem altogether by not passing actual response object into
middleware. Instead we could pass just those bits that a middleware
should care about: a template name and a context instance. The
middleware then may (or even must) return new values for those that
would be placed back into the response by the request handler.
Something still bothers me about it, but I can't invent any real
objections myself.
You right. And it's not feasible (or beautiful) to just rip everything
out of response and pass as local arguments. Back to thinking :-).
Russel and others: do you think the issue of accidental baking is a
show-stopper here?
True. I seem to somehow missed it between the lines. Thanks! I'm +1 on
it by the way.
Full feature freeze is expected only by the time of beta so I don't
think it's absolutely necessary to push it before alpha 1.
Anyway since I care very much for the patch I can pick it up if you
couldn't find a time to maintain it. Just drop me a line off-list in
this case.
No - this isn't urgent for alpha 1; I'm happy to see it land in time
for the beta.
I've had a quick look at the patch, and it looks pretty good. Thanks
for your work on this, Mikhail.
Yours,
Russ Magee %-)