Question pertaining to availability of ``request`` throughout different scopes

47 views
Skip to first unread message

Yo-Yo Ma

unread,
Dec 28, 2010, 6:40:34 PM12/28/10
to Django developers
I understand that accessing ``request`` inside of a form's clean
method, or in a model's save method is a bit of a "leaky abstraction",
but it can be very useful in a number of cases. I've seen solutions
which use ``threading.local`` in middleware to accomplish this, but
the consensus seems to be, "avoid doing that", which finally leads me
to my question: Does the core team intend on solving this issue, or
should I be focused on trying to find a workaround for this? Also, do
you have suggestions that might lead me (or somebody else) to write
something that could eventually be put into Django's core? I believe
Pylons uses a sort of thread local style proxy object. Is Django
strongly opposed to this style?

Russell Keith-Magee

unread,
Dec 28, 2010, 10:24:06 PM12/28/10
to django-d...@googlegroups.com
You are correct that threadlocal approaches are generally discouraged
by the core team. A threadlocal is just a fancy word for a global
variable, and should be discouraged for exactly the same reasons that
globals are discouraged.

As for "do we intend to solve this issue" -- you haven't been exactly
clear as to what the issue *is* -- or rather, why it isn't already
trivially solveable using completely standard OO techniques.

If you clean method on a form requires access to the request, then
just pass it in as an argument to the form. In your subclassed form,
add a keyword argument for the request.

class MyForm(forms.ModelForm):
class Meta:
model = MyModel

def __init__(self, *args, **kwargs):
self.request = kwargs.pop('request')
super(MyForm, self).__init__(*args, **kwargs)

def clean(self):
# whatever self.request based validation logic is required

def my_view(request):
my_form_instance = MyForm(request=request)
# use the form instance

Problem solved, no threadlocals required. You can call this a
"workaround" if you like; I'd prefer to call it "good software
engineering practice".

Yours,
Russ Magee %-)

> --
> You received this message because you are subscribed to the Google Groups
> "Django developers" group.
> To post to this group, send email to django-d...@googlegroups.com.
> To unsubscribe from this group, send email to
> django-develop...@googlegroups.com.
> For more options, visit this group at
> http://groups.google.com/group/django-developers?hl=en.
>
>

Yo-Yo Ma

unread,
Dec 29, 2010, 12:59:59 AM12/29/10
to Django developers
Sorry, I did not mean a form. I meant a model's clean method. Normally
you could do something in a model by similar means as your example
though, but in the admin the only context where the request is
available is through admin actions. Perhaps this is a non-problem, and
more of just a lack of one convenience that os best left out.

On Dec 28, 8:24 pm, Russell Keith-Magee <russ...@keith-magee.com>
wrote:

Alex Gaynor

unread,
Dec 29, 2010, 2:58:13 AM12/29/10
to django-d...@googlegroups.com
Beg pardon?  Just about every public API on the admin takes request as a parameter.

Alex


--
"I disapprove of what you say, but I will defend to the death your right to say it." -- Evelyn Beatrice Hall (summarizing Voltaire)
"The people's good is the highest law." -- Cicero
"Code can always be simpler than you think, but never as simple as you want" -- Me

Shai Berger

unread,
Dec 29, 2010, 4:20:37 AM12/29/10
to django-d...@googlegroups.com
On Wednesday 29 December 2010 05:24:06 Russell Keith-Magee wrote:
>
> As for "do we intend to solve this issue" -- you haven't been exactly
> clear as to what the issue *is* -- or rather, why it isn't already
> trivially solveable using completely standard OO techniques.
>

The real issue, as I've seen it, is not in cases such as raised by the
original poster, but in cases where the code that needs the request is located
some distance in the call-chain below the code that has access to the request;
then, solving the problem in the way Russell outlined involves having several
places in the code which accept (and sometimes, as in Russell's example, even
keep) a request object only so that they can pass it on.

As a concrete example, consider a situation where you have different pieces of
code activated in order to generate a response, and now you want to add info
from the request to your logging. I can think of no decent way to do that
except for thread-locals and a middleware (and that's actually the way I've
done it; I made sure that the middleware doesn't copy the whole request, just
the pieces I need, but still).

My 2 cents,
Shai.

Luke Plant

unread,
Dec 29, 2010, 6:56:35 AM12/29/10
to django-d...@googlegroups.com
On Wed, 2010-12-29 at 11:20 +0200, Shai Berger wrote:
> On Wednesday 29 December 2010 05:24:06 Russell Keith-Magee wrote:
> >
> > As for "do we intend to solve this issue" -- you haven't been exactly
> > clear as to what the issue *is* -- or rather, why it isn't already
> > trivially solveable using completely standard OO techniques.
> >
>
> The real issue, as I've seen it, is not in cases such as raised by the
> original poster, but in cases where the code that needs the request is located
> some distance in the call-chain below the code that has access to the request;
> then, solving the problem in the way Russell outlined involves having several
> places in the code which accept (and sometimes, as in Russell's example, even
> keep) a request object only so that they can pass it on.

This is a general problem in all software in all programming languages.
It is called the configurations problems [1], [2], and all solutions
have problems.

Making request a parameter of all objects/functions means that I can't
use those functions from a non-web context. Even making it a keyword arg
is less than desirable, because I have to alter all these functions in
the call stack, and some of them are outside my control, and some them
just don't have anything to do with web requests. Using threadlocals is
bad because it introduces global variables. However, the request is (or
ought to be treated as) an immutable global variable, which are much
better than mutable global variables, so this solution is arguably no
worse than the others. Another approach is callstack tracing to find a
request object [3], which makes threadlocals look good in comparison!

Other languages have some different solutions (Haskell has lots, because
it has a particularly powerful type system, and it suffers from this
problem much more acutely than other impure languages). I don't expect
we will come up with a new solution to it in Python.

Personally, I will use a mixture of these techniques, depending on the
context. If I use threadlocals I ensure that the code explicit handling
for the case when there is no request available.

Luke


[1] http://okmij.org/ftp/Haskell/types.html#Prepose
[2]
http://www.joachim-breitner.de/blog/archives/443-A-Solution-to-the-Configuration-Problem-in-Haskell.html
[3]
http://chris-lamb.co.uk/2010/06/01/appending-request-url-sql-statements-django/


--
"The one day you'd sell your soul for something, souls are a glut."

Luke Plant || http://lukeplant.me.uk/

Shai Berger

unread,
Dec 31, 2010, 10:33:23 AM12/31/10
to django-d...@googlegroups.com
On Wednesday 29 December 2010, Luke Plant wrote:
> On Wed, 2010-12-29 at 11:20 +0200, Shai Berger wrote:
> > On Wednesday 29 December 2010 05:24:06 Russell Keith-Magee wrote:
> > > As for "do we intend to solve this issue" -- you haven't been exactly
> > > clear as to what the issue *is* -- or rather, why it isn't already
> > > trivially solveable using completely standard OO techniques.
> >
> > The real issue, as I've seen it, is not in cases such as raised by the
> > original poster, but in cases where the code that needs the request is
> > located some distance in the call-chain below the code that has access
> > to the request; then, solving the problem in the way Russell outlined
> > involves having several places in the code which accept (and sometimes,
> > as in Russell's example, even keep) a request object only so that they
> > can pass it on.
>
> This is a general problem in all software in all programming languages.
> It is called the configurations problems (refs to Haskell), and all
> solutions have problems.
>

I didn't think to bring other languages up here, but since you have, venerable
Common Lisp has a pretty good one in dynamically-scoped variables -- they are
like thread-locals, except that their bindings are stacked rather than
desctructive, and (essentially, in translation to Python term) bindings are
popped when the function that made them returns. This gives assignments
exactly the right level of locality. But we don't seem to have those at hand.
I


> Making request a parameter of all objects/functions means that I can't
> use those functions from a non-web context. Even making it a keyword arg
> is less than desirable, because I have to alter all these functions in
> the call stack, and some of them are outside my control, and some them
> just don't have anything to do with web requests. Using threadlocals is
> bad because it introduces global variables. However, the request is (or
> ought to be treated as) an immutable global variable, which are much
> better than mutable global variables, so this solution is arguably no
> worse than the others. Another approach is callstack tracing to find a
> request object [3], which makes threadlocals look good in comparison!
>

While callstack tracing is definitely ugly, it does give the semantics of
dynamically scoped vars, which IMHO is the best possible semantics.

But euh, at what cost!

Have fun,
Shai.

Reply all
Reply to author
Forward
0 new messages