As a consequence of the proposed CSRF changes, we brought up wanting
to add a shortcut like render_to_response that uses RequestContext, as
a refinement, but didn't decide on anything. Ideally, we would have
something as short as this:
return render(request, "template_name.html", {'foo':'bar'})
Currently you can do this:
return render_to_response("template_name.html", {'foo':'bar'},
context_instance=RequestContext(request))
This is a bit cumbersome, especially with the need to import
RequestContext as well as render_to_response. My proposal: add
'request' as a keyword argument to this same function:
return render_to_response("template_name.html", {'foo':'bar'},
request=request)
It's slightly longer than the 'ideal' could be, but has some quite big
advantages:
* you only have one import whether you want to use RequestContext
or Context or both. And it's an import most people will
have already in existing code.
* you don't have to *learn* a new function name or import
* in the context of the tutorial, it works very well - no
new import to add, just a small change which can be explained
without even going into how RequestContext works or that
it even exists.
* 'render_to_response' is nice and explicit, and it's very
difficult to shorten it without losing that.
Given where we are right now, I think it's the best option so far.
Comments?
Luke
--
You meet a lot of smart guys with stupid wives, but you almost
never meet a smart woman with a stupid husband. (Erica Jong)
Luke Plant || http://lukeplant.me.uk/
it's better, but I think it's still far from perfection.
I strongly believe any provided django views (django contrib views,
your own views or 3rd-party views) should allow enhancing with
"logic-reusability-fu":
def djangoview(request):
...
def betterview(request):
src = djangoview(request)
if some_test: return src # return as is
return ...[enhance src]
def evenbetterview(request):
src = betterview(request)
return ...[enhance src]
I mean, you should be able to use other view logic but wrap it with
your own additions to the template data and change used template.
It's much useful in class-based views controllers, like in admin app.
Currently i'm doing this with my own @render_to (
http://github.com/buriy/django-beautils/blob/master/beautils/decorators.py
), though it's not perfect too:
def djangoview(request):
# Short-circuit bailout should work properly too.
if not request.POST:
return HttpResponseRedirect('/some-other-place/')
if not 'send' in request.POST:
return {'template': 'template2.html'}
return {'template': 'template1.html', 'form': make_form(request),
'arg1': 'val1'}
@render_to('bettertemplate.html') # see how explicit it is, huh?
def betterview(request):
out = djangoview(request) # returns that dictionary,
unfortunately, double @render_to won't work
out.update({
'arg1': 'val2',
'arg2': 'val1',
}
return out
This is much better way, but it's not still there.
I see there's one more useful pattern, error short-circuiting, like
it's happening in boolean "and" and "or" operators:
def djangoview(request):
if something: raise HttpResponseRedirect()
return render_to_response(...)
def betterview(request):
partial = djangoview(request)
return render_to_response(...)
better to be caught later in default django middleware and treat it as
a regular return (not as Http404).
This will allow somewhat like "raise HttpResponse('error')" or "raise
make_error_output(request)".
That means,
1) perfect render_to_response should be lazy.
2) it should use subclass of HttpResponse (LazyHttpResponse), to be
combined with old consumers.
3) when treating as string, LazyHttpResponse should render itself to string.
4) decorator is still usable for older views, to short-circuit
non-LazyHttpResponse outputs.
def djangoview(request):
# Short-circuit bailout should work properly too.
if not request.POST:
return HttpResponseRedirect('/some-other-place/')
if not 'send' in request.POST:
# changing template. won't work properly after subclassing
# if you still used any render_to_response
raise LazyHttpResponse(request, template='template2.html')
return LazyHttpResponse(request, template='template1.html',
data={'form': make_form(request), 'arg1': 'val1'})
djangoview12 = enhance(djangoview)
def betterview(request):
out = djangoview12(request) # only LazyHttpResponse will come here
out.template = 'template5.html'
out.update({
'arg1': 'val2',
'arg2': 'val1',
}
return out
Someone provided similar design already.
In fact, implementation is pretty straightforward.
render_to_response can be just a wrapper to these views, or it might
have other name.
And this shouldn't cause any backward-incompatibility problems
(LazyHttpResponse obeys plain HttpResponse contract).
Just you'll be able to add your own variables to, say, provided django
login view with your own template, not copy-pasting it to your code.
Yes, and for even better compatibility with admin's "multitemplates",
additional argument templates=['template2.html', 'app/template2.html',
'app/model/template2.html']
is one more valuable enhancement.
Regarding names and learning, actually, every novice will easily learn
*any* name proposed to him, it will be considered part of the
learning. Of course, explicit name is better, but it doesn't matter a
lot to novice. Only when gets more acknowledged with Django, users
will whine that name is not that perfect it could be, or not that
short, or not that useful (i'm the third type! useful is better than
explicit! ;) )
Given that only your proposal is considered, I think that yes, it's a
very good thing. It's making render_to_response useful after all!
Cause I hate render_to_response usage pattern now, it smells soooo
bad! (highlighted in tutorials!)
Of course there's direct_to_template. Don't know how that happened
that it appeared instead of enhancing render_to_response...
Can be that no one knows why it has happened so?
Well, generally, I use to think that everything related to "django
views in general" constantly lacks devs attention.
Like it's perfect since long time ago. Or it is just too boring topic,
or API stability contract tied their hands, or just everyone get used
to it...
Or everyone code their own renderer once on project start, and then
use that renderer everywhere, like I do.
I also think direct_to_response was one of these personal renderers.
And I believe no one really care of render_to_response weirdness ;)
--
Best regards, Yuri V. Baburov, ICQ# 99934676, Skype: yuri.baburov,
MSN: bu...@live.com
Interesting idea. I think the advantages you list are all on the
money. My only question is on the pathological case:
render_to_response("template_name.html", {'foo','bar'},
context_instance=Context(), request=request)
What is the interpretation here? An error? ignore the request? ignore
the context?
Introducing an API where there is a usage that is legal syntax but
ambiguous argument usage makes me slightly nervous - although, I will
admit the potential for accidental misuse here is small.
Also - needing to type request=request is a bit of a syntactical wart
- less of a wart than RequestContext(request), but still a wart. I
would warrant that render_to_response is one of (if not the) most used
APIs in Django, there is something to be said for it to be seriously
polished and clean.
For completeness, it's worth mentioning all the options that are on the table:
1) Add a request argument to render_to_response().
2) Do nothing.
render_to_response() works as-is - we're just talking about a convenience.
3) Do nothing, but tell people to use direct_to_template()
While this is a possible solution, it's worth noting that
direct_to_template and render_to_response aren't perfect analogs. If
you look at the internals, direct_to_template does a bunch of other
interesting things to allow for the fact that it is intended for use
as a generic view. To that end, it does a lot of extra work that most
use cases won't need.
4) Add a completely new shortcut:
bikeshed(request, *args, **kwargs)
which does exactly what render_to_response does, but instantiates a
RequestContext. I've deliberately chosen a nonsense name - at the
moment, deciding if this is the solution we want is more important
than the actual name.
Personally, I'd actually be in favour of (4). This is such a common
use case - If we're going to do this, it makes sense that the shortcut
really is short.
Yours,
Russ Magee %-)
Me too.
I'd like this shortcut to be (similar to?) Simon's TemplateResponse
(http://code.google.com/p/django-openid/source/browse/trunk/django_openid/response.py).
I'm going to have to introduce something very much like this to get
class-based generic views done, so it makes sense to kill two birds
with one stone.
Jacob
This isn't a new idea - Simon Willison has been proposing this general
idea for a while. He's been talking specifically about making template
rendering lazy, so you can use middleware or wrapper views to modify
the template or the contents of the context that is used for
rendering, right up until the response is served back to the user.
There is some working code here:
http://code.google.com/p/django-openid/source/browse/trunk/django_openid/response.py
> Of course there's direct_to_template. Don't know how that happened
> that it appeared instead of enhancing render_to_response...
> Can be that no one knows why it has happened so?
Easy - direct_to_response is a generic view, not a template utility.
It's designed to be the simplest possible generic view - the view that
renders a template with some interpreted context. The internal
processing done by direct_to_response (evaluating callables, etc) is
driven entirely by its intended use case - deployment in urls.py.
The fact that it can be used as a substitute for render_to_response is
mostly an accident of the intentionally simple contract for a view -
that it accepts a request as it's first argument, and returns a
response. As is regularly noted on django-users and in various books,
you can easily write view functions that wrap generic views to do
pre-processing on view arguments before passing those arguments to the
generic view. Using direct_to_response as a replacement for
render_to_response is really just the ultimate extension of this
theory.
> Well, generally, I use to think that everything related to "django
> views in general" constantly lacks devs attention.
> Like it's perfect since long time ago. Or it is just too boring topic,
> or API stability contract tied their hands, or just everyone get used
> to it...
Part of the problem here is betrayed by your choice of phrase - that
views are "lack(ing) devs attention". The core developers aren't here
to do your bidding. We work on the issues that we see as important -
recently, that means we've been busy trying to get features like model
validation and multi-db support into trunk.
Views might not be perfect, and they might be able to work better, but
they _work_. If you disagree, then *you* need to solve it. Remember -
Django is open source. We'll take submissions from anyone. You just
need to demonstrate to us that your ideas are sound and your
implementation is robust.
Yours
Russ Magee %-)
> Well, generally, I use to think that everything related to "django
> views in general" constantly lacks devs attention.
> Like it's perfect since long time ago. Or it is just too boring
> topic, or API stability contract tied their hands, or just
> everyone get used to it...
> Or everyone code their own renderer once on project start, and then
> use that renderer everywhere, like I do.
> I also think direct_to_response was one of these personal
> renderers. And I believe no one really care of render_to_response
> weirdness ;)
Russell answered the direct_to_template() history, so I'll skip that
bit...
Your other ideas about views are interesting, and remind me of ideas
of Simon Willison - see 'TemplateResponse' about half way down this
message:
http://groups.google.com/group/django-developers/msg/b1b3f8854b9ae2b1
To respond to your specific suggestion, there are plenty of problems
adopting it as a global way of doing things. For example, what if a
view function returns HttpResponses with different templates depending
on the path through, or returns a different kind of response, like a
HttpRedirectResponse? Your simple wrapping function will now break.
What if it decides not to use a template at all? Or a completely
different templating engine? By adopting LazyHttpResponse everywhere,
you would effectively be saying that a HttpResponse is "a Django-like
template and some context variables, plus some headers". But at the
moment, a HttpResponse is not that, it is some content and some
headers, nothing more.
Yes, your LazyHttpResponse obeys the HttpResponse contract, but not
vice-versa. If people rely on view functions returning
LazyHttpResponse, any view which does not will be regarded as broken,
or any decorator/wrapper that assumes a LazyHttpResponse will be
considered broken.
The Django devs don't think that everything to do with view functions
is perfect, but I for one would be against changing the whole way they
work. The basic definition of a view function:
a callable that takes a HttpRequest (and possibly more arguments),
and returns a HttpResponse of some kind.
...is, as I see it, exactly as it should be. There are no requirements
about using our template engine, or using templates at all. How you
return the value, and the exact type etc, is entirely up to you.
Starting with that, you can always build other abstractions on top,
and it isn't hard to do so. You immediately have the ability to wrap
existing views that take keyword arguments. If you want a view
function that has bits you need to customize, you can use a class
based solution, with methods that can be overridden, like the admin
does for instance. There are an infinite number of other things you
can do, including your LazyHttpResponse etc., TemplateResponse etc.
Some of these may be good enough to make it into core patterns.
But I think it would be a very bad idea build these into the way
Django works and make everyone pay for the complexity and overhead
that only some people might need, or tie the definition of a view
function to some other technology (like templates).
Luke
--
The probability of someone watching you is proportional to the
stupidity of your action.
Luke Plant || http://lukeplant.me.uk/
> Interesting idea. I think the advantages you list are all on the
> money. My only question is on the pathological case:
>
> render_to_response("template_name.html", {'foo','bar'},
> context_instance=Context(), request=request)
>
> What is the interpretation here? An error? ignore the request?
> ignore the context?
>
> Introducing an API where there is a usage that is legal syntax but
> ambiguous argument usage makes me slightly nervous - although, I
> will admit the potential for accidental misuse here is small.
I implemented this the other day, and it seemed like the only sensible
thing to do was raise a ValueError if they try to supply both, rather
than silently overwrite a value in **kwargs. The patch is essentially
the following lines inserted into render_to_response:
...
request = kwargs.pop('request', None)
if request is not None:
if 'context_instance' in kwargs:
raise ValueError("blah blah blah")
else:
kwargs['context_instance'] = RequestContext(request)
...
> 4) Add a completely new shortcut:
>
> bikeshed(request, *args, **kwargs)
>
> which does exactly what render_to_response does, but instantiates a
> RequestContext. I've deliberately chosen a nonsense name - at the
> moment, deciding if this is the solution we want is more important
> than the actual name.
I am inclined towards 4) as well. I would just *slightly* quibble
that the name is not important - a name that makes sense and is
memorable is very important for a shortcut. If I have to look it up,
it's not a shortcut any more. That's why I think that modifying
render_to_response rather than adding a new shortcut has a slight edge
here - because people already know it. Also, it means that I can just
use "M-x grep" in emacs to find all instances that can be updated, and
I don't have to worry about import lines :-) Not that I don't have a
complete test suite on all my projects that would catch any missing
imports, of course... ;-)
Hmm, for me this boils down to whether I'm feeling lazy or feeling
like a perfectionist, option 4) is definitely cleaner...
On Fri, Oct 16, 2009 at 8:14 PM, Russell Keith-Magee
<freakb...@gmail.com> wrote:
>
> On Fri, Oct 16, 2009 at 6:01 PM, Yuri Baburov <bur...@gmail.com> wrote:
>>
>> That means,
>> 1) perfect render_to_response should be lazy.
>
> This isn't a new idea - Simon Willison has been proposing this general
> idea for a while. He's been talking specifically about making template
> rendering lazy, so you can use middleware or wrapper views to modify
> the template or the contents of the context that is used for
> rendering, right up until the response is served back to the user.
> There is some working code here:
>
> http://code.google.com/p/django-openid/source/browse/trunk/django_openid/response.py
Thanks.
Exactly this is what I wasn't able to find today.
So, what's the status of this proposal?
Do you think this is what a lot of people would like to see in django or not?
Would you, personally, like?
>> Of course there's direct_to_template. Don't know how that happened
>> that it appeared instead of enhancing render_to_response...
>> Can be that no one knows why it has happened so?
>
> Easy - direct_to_response is a generic view, not a template utility.
> It's designed to be the simplest possible generic view - the view that
> renders a template with some interpreted context. The internal
> processing done by direct_to_response (evaluating callables, etc) is
> driven entirely by its intended use case - deployment in urls.py.
>
> The fact that it can be used as a substitute for render_to_response is
> mostly an accident of the intentionally simple contract for a view -
> that it accepts a request as it's first argument, and returns a
> response.
> As is regularly noted on django-users and in various books,
> you can easily write view functions that wrap generic views to do
> pre-processing on view arguments before passing those arguments to the
> generic view.
Actually, I've found I always want to do _postprocessing_ for generic views :)
In the place between making final dict and rendering it with template.
This is why I need lazy responses.
> Using direct_to_response as a replacement for
> render_to_response is really just the ultimate extension of this
> theory.
Great! So you confirm lack of attention? ;)
Why else have nobody found this implication before committing this feature?
>> Well, generally, I use to think that everything related to "django
>> views in general" constantly lacks devs attention.
>> Like it's perfect since long time ago. Or it is just too boring topic,
>> or API stability contract tied their hands, or just everyone get used
>> to it...
>
> Part of the problem here is betrayed by your choice of phrase - that
> views are "lack(ing) devs attention". The core developers aren't here
> to do your bidding. We work on the issues that we see as important -
> recently, that means we've been busy trying to get features like model
> validation and multi-db support into trunk.
You mean, you have not enough time to support faster development of Django?
Ok with that, but please, admit it, don't make a secret from this.
> Views might not be perfect, and they might be able to work better, but
> they _work_. If you disagree, then *you* need to solve it. Remember -
> Django is open source. We'll take submissions from anyone. You just
> need to demonstrate to us that your ideas are sound and your
> implementation is robust.
I don't owe you anything. You don't owe anything to me.
But I'd like you to _encourage_ Django contributors to do stuff for Django,
not impersonate a contribution to Django as a kind of "prize" only for
already privileged people and unpredictable "if you can attract us"
behavior, like you always do.
This bitchy "we might devote some time to you if you can attract us"
politics keep me away from any serious time effort in direction of
contribution to Django for few years.
Like you don't want to allow any contribution, but it is possible to
make contribution "by force" of idea superiority.
Why should I try to contribute anything, if you aren't interested in
my contributions?
Django just doesn't have any mechanism to take decision on small
enhancements, to support and to encourage them.
I checked this few times already, I've seen a lot of tickets with such
enhancements.
Nobody cares of them. There's no person I can talk with about my contributions.
There's no person currently who can tell what's wrong with my
suggestion and, what's more important, how I can fix it to get it into
Django, and what exactly I should do to be sure it will be included.
Total irresponsibility, lack of management, the worst side of Django communism.
I want a win-win model: you and me get a feature added to Django, you
want it and I want it, I make a contribution, you help me to integrate
my contribution. Let's work *together* on contributing to django, not
putting a bitch shield (
http://www.urbandictionary.com/define.php?term=bitch+shield ) between
Django and enhancements.
Sorry that I concentrated on politics issues again instead of technological.
But it seems I've finally found words to express what I think about
the django development process.
And most technological issues have been discussed since long time ago,
but nothing is done! Why?
I attempted to explain, why it is so.
Ok, now I'll get to technological talks again.
https://labs.pyrox.eu/common/jinja2/tree/django.py
This could be easily adapted for django templates, though I'm not a huge
fan of this solution anymore.
I've found it to be way more practical to just store the request object
inside a threading.local object and let my functions access it directly.
That would allow you to write very simple view functions that don't even
need the request to be passed as argument.
from django.http import request
def index():
return response('index.html', user=request.user)
A nice side effect of this would be that the whole middleware thing for
adding lazy fields to the request object becomes obsolete, too. No more
passing around request objects like a madman in order to access the
request object inside template tags, filters, ModelAdmins, etc.
Getting rid of the superfluous request argument would be more a pony
request for Django 2.0, as it'd break a lot of code. I like the idea
nonetheless.
--mp
LazyHttpResponse problem wasn't appearing from void.
It was attempt to solve bigger problem -- subclassing of generic controllers.
Let's say we want our Django code to finally have beautiful
controllers, i.e, in some case, it might look so:
class BlogPostController(GenericController): # also it implements "singleton"
model = BlogPost
@render_to('blog/post_list.html')
def list(request, year=None, month=None, day=None):
....
@render_to('blog/post_details.html')
def view(request, year, month, day, slug):
response = super(BlogPostController, self).view(request,
pub_date=date_or_404(year, month, day), slug=slug)
comments = Comment.objects.filter(post=response['object'].id,
is_allowed=True).order_by('-date')
response['comments'] = paginate(request, comments)
response['form'] = CommentForm(request.POST or None)
return response
urls = BlogPostController.urls() # so you are able to do this
I took this as a prototype and looked from this perspective.
On Fri, Oct 16, 2009 at 10:10 PM, Luke Plant <L.Pla...@cantab.net> wrote:
>
> On Friday 16 October 2009 11:01:16 Yuri Baburov wrote:
>
>> Well, generally, I use to think that everything related to "django
>> views in general" constantly lacks devs attention.
>> Like it's perfect since long time ago. Or it is just too boring
>> topic, or API stability contract tied their hands, or just
>> everyone get used to it...
>> Or everyone code their own renderer once on project start, and then
>> use that renderer everywhere, like I do.
>> I also think direct_to_response was one of these personal
>> renderers. And I believe no one really care of render_to_response
>> weirdness ;)
>
> Russell answered the direct_to_template() history, so I'll skip that
> bit...
>
> Your other ideas about views are interesting, and remind me of ideas
> of Simon Willison - see 'TemplateResponse' about half way down this
> message:
>
> http://groups.google.com/group/django-developers/msg/b1b3f8854b9ae2b1
>
> To respond to your specific suggestion, there are plenty of problems
> adopting it as a global way of doing things. For example, what if a
> view function returns HttpResponses with different templates depending
> on the path through, or returns a different kind of response, like a
> HttpRedirectResponse? Your simple wrapping function will now break.
Then it needs not to be that simple ;)
> What if it decides not to use a template at all? Or a completely
> different templating engine?
Then it will be not able to subclass.
You will treat it as HttpResponseRedirect or Http404. Not changeable view.
Or they can support their own LazyHttpResponse with their renderer.
> By adopting LazyHttpResponse everywhere,
> you would effectively be saying that a HttpResponse is "a Django-like
> template and some context variables, plus some headers". But at the
> moment, a HttpResponse is not that, it is some content and some
> headers, nothing more.
Not, HttpResponse isn't, but LazyHttpResponse is. Do you get the difference?
And this prevents me from reusing other "django-like templates with
some context variables, plus some headers" now, cause they are treated
as "plain views" now. Often methods allow extra_context= and template=
hacks. But not changeable response.
And I'm not the only one.
> Yes, your LazyHttpResponse obeys the HttpResponse contract, but not
> vice-versa.
I agree.
> If people rely on view functions returning
> LazyHttpResponse, any view which does not will be regarded as broken,
> or any decorator/wrapper that assumes a LazyHttpResponse will be
> considered broken.
I'd like to treat them so.
I think nothing's wrong with making it throw a error if view was
expected to be LazyHttpResponse but is not.
Everyone know what view they are reusing, right?
Right now, one is rarely reusing anything exactly because of that problem!
Another way to solve this is like one created AnonymousUser instead of
None: effectively make HttpResponse to provide data that
LazyHttpResponse provides: context None, template None (or even
further: NoTemplate, {} in context). Yes, this option will change
HttpResponse contract.
You forget that views created in standard ways, will be turned into
LazyHttpResponse automagically.
And "raise" instead of "return" is solution to "assuming
LazyHttpResponse not HttpResponse".
Other subclasses will be raised and handled at Django's get_response
and middleware level.
> The Django devs don't think that everything to do with view functions
> is perfect, but I for one would be against changing the whole way they
> work. The basic definition of a view function:
>
> a callable that takes a HttpRequest (and possibly more arguments),
> and returns a HttpResponse of some kind.
>
> ...is, as I see it, exactly as it should be. There are no requirements
> about using our template engine, or using templates at all. How you
> return the value, and the exact type etc, is entirely up to you.
Lack of requirements is not a feature here, but IMHO a bug ;)
> Starting with that, you can always build other abstractions on top,
> and it isn't hard to do so. You immediately have the ability to wrap
> existing views that take keyword arguments. If you want a view
> function that has bits you need to customize, you can use a class
> based solution, with methods that can be overridden, like the admin
> does for instance. There are an infinite number of other things you
> can do, including your LazyHttpResponse etc., TemplateResponse etc.
> Some of these may be good enough to make it into core patterns.
It isn't hard to do so?
The exact problem is postprocessing.
It's hard to do so now.
> But I think it would be a very bad idea build these into the way
> Django works and make everyone pay for the complexity and overhead
Really, Django's Template class makes 10 times more complexity than
TemplateResponse implementation.
> that only some people might need, or tie the definition of a view
> function to some other technology (like templates).
I'm talking about providing more data *where* these views *were tied
already*, not *tying to template renderer*.
And please don't forget, that Django does the last with
render_to_response *now*.
I'm slightly confused about what were debating. In your first
response, you said:
> I strongly believe any provided django views (django contrib views,
> your own views or 3rd-party views) should allow enhancing with
> "logic-reusability-fu":
> ...
In the more recent one:
You wrote:
> Luke Plant wrote:
> > The Django devs don't think that everything to do with view
> > functions is perfect, but I for one would be against changing the
> > whole way they work. The basic definition of a view function:
> >
> > a callable that takes a HttpRequest (and possibly more
> > arguments), and returns a HttpResponse of some kind.
> >
> > ...is, as I see it, exactly as it should be. There are no
> > requirements about using our template engine, or using templates
> > at all. How you return the value, and the exact type etc, is
> > entirely up to you.
>
> Lack of requirements is not a feature here, but IMHO a bug ;)
(i.e. it would be best to *require* use of Django's template system
for all views)
But also you wrote:
> I'm talking about providing more data *where* these views *were
> tied already*, not *tying to template renderer*.
> And please don't forget, that Django does the last with
> render_to_response *now*.
So is your proposal that all views must return an enhanced
HttpResponse, or that builtin shortcuts like render_to_response, which
already used Django's template system, should return an enhanced
HttpResponse?
The first proposal, IMO, is a non-starter, unless we are talking
"Django 2000", but I still think it would be really bad idea even
then.
The second proposal is worth talking about I think. But I think you
need a really concrete proposal. It opens up huge issues - the
'contract' of a view would then include the names of all variables in
its output context. Some views already have that contract in effect -
the ones that are designed to allow you to supply your own template -
but many do not. We then have to think about whether this is the best
pattern for allowing selective overriding of parts of what a view does
- I suspect that messing around with the context dictionary afterwards
is of very limited use, not to mention fragile, compared to a full
class based method that splits the view up into methods that can be
overridden as required.
mp> I've found it to be way more practical to just store the request object
mp> inside a threading.local object and let my functions access it directly.
mp> (...)
Alex> Quite simply, no. Storing anything in a threadlocal instead of
Alex> passing it around is indicative of bad coding practice, and poor
Alex> architecture.
Stating that the use of threading.local objects is "indicative of bad
coding practice, and poor architecture" is plainly wrong.
I say: It depends.
Django is probably most famous for it's easy to use ORM and steep
learning curve. This surely wasn't the case if you had to pass around
some database session object all the time. I'd go that far to say if the
ORM wasn't so nice to use I would never have picked up Django in the
first place.
Some people claim that thread locals kill performance, but that's mainly
caused by bad code design. Looking up some thread local object a
bazillion times in a loop can be avoided just like most 1+N database
queries.
If Django was designed with c10k in mind with a sophisticated non
blocking IO event dispatcher I couldn't agree more with you. In this
case using threading.local object would in fact be an architectural
disaster. Since Django is essentially build around the 'one request =
one thread' idiom it's perfectly valid to use a threading.local object
for storing the request object inside.
Django was designed with a 'get things done' mentality. It is a
framework for perfectionists with deadlines. So instead of making it
difficult to query the database it provides a convenient way of
providing access to the database connection in a threading.local object.
So I wonder what would be wrong with putting the request object in a
threading.local object. It would neither kill performance nor cause a
poor architecture by itself.
I've spent way too much time with frameworks that aim for a perfect
architecture, but fail horribly when it comes to productivity. I finally
came to Django to get my work done and not having to monkey patch the
framework continuously.
--mp
(1)
> all views must return an enhanced
> HttpResponse,
> or that
(2)
> builtin shortcuts like render_to_response, which
> already used Django's template system, should return an enhanced
> HttpResponse?
Option 2. I'm pretty consistent on the topic. Maybe just express my
position not very consistently.
I want to have enhanced views where it's possible. The much the
better. In the same time, it's not a must, but "it's useful to enhance
views, not breaking any contracts, not making
backward-incompatibilities".
That enhancing is swap from HttpResponse to TemplateResponse. In fact,
I really like that name.
> The first proposal, IMO, is a non-starter, unless we are talking
> "Django 2000", but I still think it would be really bad idea even
> then.
>
> The second proposal is worth talking about I think. But I think you
> need a really concrete proposal. It opens up huge issues - the
> 'contract' of a view would then include the names of all variables in
> its output context. Some views already have that contract in effect -
> the ones that are designed to allow you to supply your own template -
> but many do not. We then have to think about whether this is the best
> pattern for allowing selective overriding of parts of what a view does
> - I suspect that messing around with the context dictionary afterwards
> is of very limited use, not to mention fragile, compared to a full
> class based method that splits the view up into methods that can be
> overridden as required.
Yes, views might be turned into classes. Sometimes it's overkill, sometimes not.
And, are we going to solve the first problem of postprocessing context
and request?
You called it "messing around with the context dictionary afterwards".
Yes, it's of limited use.
But it's also wrapping user view into generic view with additional
actions after generic view will do its job.
Paginate results, or find specific object by url...
Let's consider this as an example:
def login(request, template_name='registration/login.html',
redirect_field_name=REDIRECT_FIELD_NAME):
"Displays the login form and handles the login action."
redirect_to = request.REQUEST.get(redirect_field_name, '')
if request.method == "POST":
form = AuthenticationForm(data=request.POST)
if form.is_valid():
# Light security check -- make sure redirect_to isn't garbage.
if not redirect_to or '//' in redirect_to or ' ' in redirect_to:
redirect_to = settings.LOGIN_REDIRECT_URL
from django.contrib.auth import login
login(request, form.get_user())
if request.session.test_cookie_worked():
request.session.delete_test_cookie()
return HttpResponseRedirect(redirect_to)
else:
form = AuthenticationForm(request)
request.session.set_test_cookie()
if Site._meta.installed:
current_site = Site.objects.get_current()
else:
current_site = RequestSite(request)
return render_to_response(template_name, {
'form': form,
redirect_field_name: redirect_to,
'site': current_site,
'site_name': current_site.name,
}, context_instance=RequestContext(request))
login = never_cache(login)
I'd like:
1) that i was able to change form (say, adding "[ ] Remember me"
checkbox, or "Select site you want to edit [v]" combobox), doing form
enhancements when i am sure this is not a redirect, and customizing
redirect if needed.
2) to split it into 2 views: one that don't know anything about sites
at all, second that enhances first, knowing about sites.
3) that i was able to create my 3rd one, reusing the second and
having also openid form, with my own logic.
4) to add one flash message that user is logged in cause you forgot
to add one in your view.
This is now not possible with django.contrib.auth.views.login.
Actually, view splitting here doesn't need to be class-based.
Yes, Feature 1 -- changing form -- is a bit easier with class having
get_form, like James did in his
http://docs.b-list.org/django-registration/0.8/backend-api.html or in
Simon's code at
http://code.google.com/p/django-openid/source/browse/trunk/django_openid/registration.py#33
, but form=AuthenticationForm in arguments like with
password_reset_form in password_reset is enough since form is keeped
for rendering and so after that can be effectively preprocessed
(subclassed or replaced with a callable) and postprocessed in wrapping
method.
Other features also don't require class-based approach.
Some other example, implementing similar features:
http://code.google.com/p/django-openid/source/browse/trunk/django_openid/auth.py#61
"Aspect-oriented programming (AOP) is a programming paradigm that
increases modularity by allowing the separation of cross-cutting
concerns". Tiny bit of AOP is what I suggest to add. Postprocessing.
I believe such views problem is particular one preventing from having
single commonly usable and extensible registration system in
django.contrib.
Now I'd like to talk on how I want to change the current situation.
We can't change HttpResponse, but can substitute construction of
HttpResponse with TemplateResponse:
change render_to_response, auth views, freaky remote_user_auth_view
(in contrib.auth.test.urls), freaky flatpages.views.flatpage, generic
create_update, direct_to_template and all others; when possible,
enhance get_template+render_to_string+HttpResponse logic.
And then we'll be able to write better class-based generic views and
object controllers, like ModelAdmin.
Adding renderer field to TemplateResponse might also be considered.
This will allow easier replacing Django render_to_string with Jinja,
Cheetah or ClearSilver renderer.
Since templates are lazy, simple user-written middleware or wrapping
with decorator could switch renderer *using postprocessing* for any
specific part of templates, including ones from third-party code (if
it uses TemplateResponse).
Factory method pattern or preprocessing themselves without
postprocessing would never give such agility.
Alex just spent a bunch of time adding multi-db support; part of the
effort involved in that work was removing the
request=thread=connection assumption that tied django to a single DB.
So he's got a very specific viewpoint.
I've previously advocated for something I'd call a blackboard-- a
globally reachable place where middleware could scribble information,
possibly useful to something else somewhere downstream in the system.
This information would be safe to ignore if you weren't interested in
this information. I think of it as a sort of persistent, opt-in
signal. He reacted badly to that one, too. :-)
But I'll side with Alex and say that a thread-local request is a bad
idea. In fact, people do use Django in async systems:
Old but useful here: http://code.google.com/p/django-on-twisted/
@render_to('myapp/mypage.html')
def home(request, foo):
return {'foo':foo}
http://www.djangosnippets.org/snippets/821/
This isn't a 1:1 replacement, but I've started using it in all of my
projects.
-justin
What's wrong, to me, is that it's a code smell which conflicts with
how Django works
Django's approach is very simple and very much in line with how any
other Python code would be expected to work: you write functions and
methods which take the information they'll need as arguments, then
ensure that information is passed as necessary. In the case of views,
you write them to take the HttpRequest as an argument, and Django
itself ensures they're called with it.
Setting aside async concerns, making the request available implicitly
would be both counter to that overall design (and I'm very much a fan
of that design, and I suspect its simplicity and understandability is
part of why Django's popular), and feels like an attempt to compensate
for either poor practice on the part of developers, or a
misunderstanding of what it means to be easy to use (I occasionally
see people complain that explicitly taking the request as an argument
in views, on grounds of DRY, and then wonder why those people choose
to use a language that requires explicit 'self' in methods...).
--
"Bureaucrat Conrad, you are technically correct -- the best kind of correct."
@provides_html('myapp/mypage.html')
@provides_json(optional_serialize_callback_can_go_here)
def my_view(request):
return {'foo': 'bar')
Then your provides_* could check the accept-header and know how to
response. Html renders a template and json would serialize the dict.
With that said this probably falls into the domain specific use case
and is probably not needed by enough people to be added to core,
however doing ajax apps are a breeze.
sean
Untying the database session from the request response cycle makes sense
for multi-db applications. But imho there should always be a simple and
convenient way of getting easy access to some kind of default database
session. (As well as to the request object, no?)
Cal Henderson in the Keynote "Why I Hate Django" pointed out in a very
funny way that most projects never get THAT big that you need multi db
support, partitioning, extensive database clusters, etc. Just skip to
19:20 to see his "scientific graph".
I'm not saying that scalability does not matter. I'm saying that Django
should not change the slogan from "for perfectionists with deadlines" to
"for scale nerds with a PhD in software architecture".
I love Django because it lets me get stuff done and not having to worry
about dependency injection, sophisticated design patterns, etc. And if
it ever gets into my way I can easily get around it and do stuff the
hard way.
Jeremy Dunck wrote:
> But I'll side with Alex and say that a thread-local request is a bad
> idea. In fact, people do use Django in async systems:
> Old but useful here: http://code.google.com/p/django-on-twisted/
The use of twisted.web2 doesn't change the fact that one thread handles
one request at a time. In order to do async I/O you either need highly
sophisticated continuations, white threads or a complete different
program structure. The less you want the user to care about async I/O
the more the programming language and/or VM has to do heavy lifting
which in turn slows things down. White threads (a Java feature) for
example don't really speed up things compared to system threads as
they're not lightweight. Stackless Python tries to solve this issue with
so called Microthreads, but I've never used it, so I can't really judge
if it's ready for production code. Guido once stated that he doesn't
like Stackless Python, so I'm suspicious.
Thread local objects are often considered evil, but as long as Django is
aiming for the one-thread per request model I rather see them as a very
practical way of scoping data on a per request basis.
--mp
Your point about 'self' made me think. When I first got in touch with
Python I was like "wtf" when seing the explicit passing of the object
reference. I disliked it for quite some time until I came to realize how
easy and understandable currying, bound methods and dynamic class
definition suddenly became. No more magic this pointer hanging around,
but some explicit self reference. These days I love it and I would bury
my own ideals of beautiful code if I'd continue defending the request
object being put in a thread local.
So in the end I have to agree with you and Alex that the request object
registered inside a threading.local object is a bad idea. Not bad as in
awful, but a "don't do unless the customer is bugging you to get some
'simple feature' done which requires a massive rewrite because the
request object is not around".
I'm still not fully convinced that thread local objects are evil in
generally, as I've been bitten by the reality way too often and needed
them to get stuff done. However, your point makes perfect sense to me
and I have to agree that it probably shouldn't be part of the Django core.
--mp
It reminds me a proof of concept: http://code.welldev.org/djangorators
You can chain a lot of decorators but it will lead to hardly readable
code, mostly because the order matters.
Anyway, it's interesting to see that a lot of people are thinking
about the same approach...
David
I'm broadly in favour of the idea, details notwithstanding. Others
have indicated their interest in the idea in this thread.
>> Using direct_to_response as a replacement for
>> render_to_response is really just the ultimate extension of this
>> theory.
> Great! So you confirm lack of attention? ;)
> Why else have nobody found this implication before committing this feature?
Because the feature was implemented over 4 years ago, and the core of
the framework was still in rapid flux. At that point, the idea was a
generic view. As often happens with the benefit of time, thinking on
the topic has evolved somewhat since then, but there have been other
development priorities (magic-removal, newforms, v1.0, ...).
>> Views might not be perfect, and they might be able to work better, but
>> they _work_. If you disagree, then *you* need to solve it. Remember -
>> Django is open source. We'll take submissions from anyone. You just
>> need to demonstrate to us that your ideas are sound and your
>> implementation is robust.
> I don't owe you anything. You don't owe anything to me.
>
> But I'd like you to _encourage_ Django contributors to do stuff for Django,
> not impersonate a contribution to Django as a kind of "prize" only for
> already privileged people and unpredictable "if you can attract us"
> behavior, like you always do.
I beg your pardon? I openly challenge this assertion. I've lost track
of the number of times I've said "Patches welcome", or invited someone
to expand on an idea. I even said it in the paragraph you responded
to:
"We'll take submissions from anyone"
I have been actively encouraging people to contribute to Django for
almost 4 years, and so have the other core developers. To claim
otherwise is disingenuous.
> This bitchy "we might devote some time to you if you can attract us"
> politics keep me away from any serious time effort in direction of
> contribution to Django for few years.
Somewhere, some wires have become badly crossed.
Django's consistent position has been the *exact opposite* of what you
seem to think it is. We *want* external contributors. We *don't* want
to do al the work ourselves.
That said, we need external contributors to work *with us*. It's no
good just dumping a 1000 line patch on our doorstep and saying "have
fun". We need contributors to engage with the core team to finesse
ideas and code until they are ready.
> Like you don't want to allow any contribution, but it is possible to
> make contribution "by force" of idea superiority.
Well, if you mean that we are only going to commit good ideas into the
Django trunk, then yes. And I won't apologize for this. It's how we
guarantee that Django remains a good framework.
If this means that some ideas take a while to get into trunk, then
thats the price we pay. I'm willing to slow the rate of technological
progress if it means we have API stability and a robust framework.
> Django just doesn't have any mechanism to take decision on small
> enhancements, to support and to encourage them.
What complete rubbish. You're using one of those mechanisms *right now*.
We have an extensive section in the documentation that describes
exactly how to get involved, and how Django's feature development
process works [1]. The mailing list is one of the channels by which
you can get feedback - there are others.
[1] http://docs.djangoproject.com/en/dev/internals/contributing/
You should also note that *this very thread* disproves your claim.
Your suggestion about views has spawned a discussion about what we
could do improve view handling. Core committers are involved, as are
other community members. There isn't universal agreement that your
original idea is right, but there is discussion, out of which I hope
consensus will eventually emerge.
> I checked this few times already, I've seen a lot of tickets with such
> enhancements.
> Nobody cares of them. There's no person I can talk with about my contributions.
> There's no person currently who can tell what's wrong with my
> suggestion and, what's more important, how I can fix it to get it into
> Django, and what exactly I should do to be sure it will be included.
> Total irresponsibility, lack of management, the worst side of Django communism.
>
> I want a win-win model: you and me get a feature added to Django, you
> want it and I want it, I make a contribution, you help me to integrate
> my contribution. Let's work *together* on contributing to django, not
> putting a bitch shield (
> http://www.urbandictionary.com/define.php?term=bitch+shield ) between
> Django and enhancements.
And that's *exactly* how it works.
However, don't confuse a lack of direct and immediate feedback on one
specific issue with a lack of feedback on *all* issues. There are 5000
members on django-dev. It simply isn't possible for the core team to
keep every single one of those people happy, especially when they are
doing so in their spare time, as volunteers.
I've written over 150 messages to django-developers since the release
of v1.1, specifically answering design questions and helping evolve
features. This has taken up almost all of my free time over the last 3
months. If you're wondering why the Django trunk hasn't had many
commits lately, it's mostly because my time has been spent doing
exactly what you say I don't do.
Still, I know I have missed some proposals. And during the period
where proposals for v1.2 have been the focus, reviewing tickets has
taken second priority. There have been patches submitted to tickets
that I haven't looked at. Once the feature proposal phase stops, this
should change. When we move to the bug-fixing phase, tickets will get
a lot more attention.
What you need to keep in mind is that you are but 1 person in this
group of 5000. If you want someone to pay attention to your proposals
and your tickets, you need to do three things:
1) Make proposals that are worthy of attention
2) Express your ideas in a way that gets a member of the core interested
3) Have a reputation that warrants attention.
(1) is the easy part.
(2) isn't so easy - many proposals get ignored simply because the
proposer can't express themselves clearly or succinctly. If I can't
work out why I should be interested in your idea from your mailing
list submission, then your idea doesn't stand much chance.
(3) is the really hard one. All open source projects work on a network
of trust, and Django is no exception. It takes time to review a patch,
and the relationship between the size of a patch and the time required
to review it isn't linear - it's exponential.
Given that my time is limited, I'm not going to commit to reviewing a
big patch unless I know the author has a good reputation, or that the
idea is *really* interesting.
To put it very bluntly: You say we should review your tickets, and
respond to your proposals. So do 5000 other people. It simply isn't
possible for me to keep 5000 people happy. Therefore, I need to
prioritize. I prioritize based on what I perceive to be the most
effective use of my time, which means paying attention to people that
have a history of producing good work, and a history of working with
me effectively. You need to give me a reason to pay attention to you,
rather than anyone else.
Yours,
Russ Magee %-)
I certainly acknowledge the use case that you (and Justin) have raised
here, and I'm sure you're making good use of this pattern in your own
projects. However, I'm slightly hesitant to support adding this
approach to core.
In particular, I'm a little bit nervous about the pseudo change to the
view contract. Django has always strictly enforced the 'view returns a
response' contract. Moving to the use of a decorator like this means
that a view's contract becomes "... or you can return a context, as
long as you're decorated the right way".
The best way to highlight my problem with this change is to consider
the edge case where the list of decorators provided doesn't capture
the context. You mention using a decorator list like:
@provides_html('myapp/mypage.html')
@provides_json(optional_serialize_callback_can_go_here)
How do you guarantee that this stack *always* returns a response? If I
leave off the provides_html decorator, who is responsible for catching
the fact that the HTML case hasn't been handled?
This is why I'm more in favour of Simon's TemplateResponse. The
general problem you are trying to solve can also be solved, but
without the change to the view contract. Using TemplateResponse (or
any 'lazy' payload evaluation approach) means that a view is still
required to return some sort of response. The TemplateResponse
provides a guaranteed fallback - the default rendering of the
TemplateResponse that the view has created. However, that response can
be manipulated further up the chain - by a decorator, or a wrapper
view, or a middleware, or anything else that can insert itself into
the dispatch cycle.
The one thing we will need to keep in mind is your use case of using a
serialization callback rather than a template for rendering, but I
suspect that's more of a naming issue than a technical problem.
There's no reason that the render() function for a JSON handled
request couldn't call a serializer (or any other callback), rather
than invoking a template renderer.
Yours,
Russ Magee %-)
If anyone is interested I can produce sample code.
>
> As a consequence of the proposed CSRF changes, we brought up wanting
> to add a shortcut like render_to_response that uses RequestContext
I want to propose another method.
Why we need RequestContext? We need it to provide global template
variables.
Why this is a bad idea? It's bad because when you are use foreign
code and that code doesn't use RequestContext in it's view you can't
provide global variables to templates(like 'user' and so).
I propose to implement another way to do this job. Does anybody agree
with me? I'd make a (ugly)patch...
If you have a third-party project that you want to use that uses
Context instead of RequestContext, then you need to do one of the
following:
1) You need to understand why the developer chose to *not* include
request level data. There are some legitimate reasons to leave this
data out.
2) You need to bug the developer to use RequestContext by default for the view
3) You need to bug the developer to make the context a configurable
option (much in the same way that render_to_template makes the Context
class a configuration option).
To me, the fact that you need to interact with your vendors and
suggest improvements and configuration options doesn't point to a flaw
in Django. If the template name isn't a configurable option for a
view, it doesn't mean that Django is fundamentally flawed because you
can't set a global template name to override the template used by a
view - it means you need to work with the vendor to provide more
useful configuration options.
> I propose to implement another way to do this job. Does anybody agree
> with me? I'd make a (ugly)patch...
It's difficult to agree when you don't explain what your "another way" is.
Yours
Russ Magee %-)
Multi-db will make working with legacy-systems much easier. Not
systems that are partioned for scalability but due to for instance
separation of concerns; different tables in each db, with different
use cases, but that needs to be presented as a whole now and then.
I've got systems here that use more than one database-backend...
"Scalability" isn't all.
HM
>
> On Mon, Oct 19, 2009 at 4:47 PM, Exe <reg...@messir.net> wrote:
> >
> > Hello!
> >
> >> As a consequence of the proposed CSRF changes, we brought up
> >> wanting to add a shortcut like render_to_response that uses
> >> RequestContext
> >
> > I want to propose another method.
> >
> > Why we need RequestContext? We need it to provide global template
> > variables.
> >
> > Why this is a bad idea? It's bad because when you are use foreign
> > code and that code doesn't use RequestContext in it's view you can't
> > provide global variables to templates(like 'user' and so).
>
> If you have a third-party project that you want to use that uses
> Context instead of RequestContext, then you need to do one of the
> following:
>
> 1) You need to understand why the developer chose to *not* include
> request level data. There are some legitimate reasons to leave this
> data out.
> 2) You need to bug the developer to use RequestContext by default
> for the view 3) You need to bug the developer to make the context a
> configurable option (much in the same way that render_to_template
> makes the Context class a configuration option).
Please look at another side. I think possibility of adding own template
variables is a core feature. Why we must think about it in any view?
It's just inconvenient. I can't find a real case where using a
TEMPLATE_CONTEXT_PROCESSORS by default will break a code.