How to get current session user in models.py?

2,435 views
Skip to first unread message

Aaron

unread,
Mar 9, 2010, 5:23:40 AM3/9/10
to Django users
How to get current session user in models.py?

I want to use a custom 'Manager' on a certain Model, which contains a
'user' field.
In this customized Manager, I want to filter records according to
whether the 'user' field equals to the current session username.

I know I can get 'user' from 'request' parameter, but the Manager is
located in the Meta part of a Model, how can I get the 'user' in this
case?

Can I achieve this without using other code in views?

Thanks in advance.

Shawn Milochik

unread,
Mar 9, 2010, 9:47:32 AM3/9/10
to django...@googlegroups.com
I wonder if this is something that might end up in Django as a built-
in feature at some point. It comes up regularly on this list.

Here's what I used. It's a hack, but it works.

http://code.djangoproject.com/wiki/CookBookThreadlocalsAndUser

Shawn

James Bennett

unread,
Mar 9, 2010, 10:37:18 AM3/9/10
to django...@googlegroups.com
On Tue, Mar 9, 2010 at 8:47 AM, Shawn Milochik <sh...@milochik.com> wrote:
> I wonder if this is something that might end up in Django as a built-in
> feature at some point. It comes up regularly on this list.

Were I to sit here all morning doing nothing but typing, I wouldn't be
able to say "no" enough times to that.

If you need access to the user somewhere, write a function or method
which takes the user as an argument, and have your view call that
function or method, passing the user. This is approximately eighty
billion times simpler than coming up with threadlocal hacks to try to
create a magic global user variable, results in cleaner,
easier-to-understand code, reduces the likelihood of
difficult-to-track bugs in the code and, best of all, is a perfectly
obvious way to accomplish the task.

And, in all seriousness, when even the page you've linked to says that
it's not a good solution and should be avoided, why do you insist on
continuing to use it? One begins to suspect that some malevolent
entity has been going around magically crippling the fingers of
programmers in such a way that they're no longer capable of writing a
method signature including an argument named "user".


--
"Bureaucrat Conrad, you are technically correct -- the best kind of correct."

Shawn Milochik

unread,
Mar 9, 2010, 10:50:41 AM3/9/10
to django...@googlegroups.com

> --


In my particular case, the purpose was to be able to have an audit log
of changes made to a model, knowing who made the changes, without
having to change all the models and views. I asked around on this list
and nobody had a solution for doing this Someone pointed me to the
threadlocals page on the official wiki. In a typical case, a user
performing an action on a page results in methods being called on a
model, some of which cause other model instances to be created or
updated. Passing the user around 100% of the time would result in
quite a bit of repeated code, and would require every model and every
view in every application to be touched.

I'm not saying this to refute your points. I'm saying this because if
there's a better way to do this then I'd like to know it. If there's
something glaringly obvious that I'm missing, do let me know. Remember
that not everyone who works with Django understands it as broadly and
in as much depth as you, and realize that just because I may be doing
something obviously stupid to you doesn't necessarily mean that I'm
being intentionally thick.

Shawn

Waldek Herka

unread,
Jan 2, 2012, 1:06:59 PM1/2/12
to django...@googlegroups.com
Hi,
I actually have already sent this to Shawn, but thought it might be useful if I post to it a broader audience. Here is my solution to this problem:

<code>
        user = None
        outer_frames = inspect.getouterframes(inspect.currentframe())
        for tpl in outer_frames:
            arginfo = inspect.getargvalues(tpl[0])
            if(type(arginfo[3]) is dict):
                arg_dict = arginfo[3].copy()
                for k in arg_dict:
                    if(type(arg_dict.get(k)) is WSGIRequest):
                        user = arg_dict.get(k).user
                        break
                del arg_dict
            del arginfo
            if(user):                
                break

</code>

This code is quite generic and should work in any place you ended up provided the origin was the django view/class method. I'm using it on models and customized admin forms for storing audit trails. It uses inspect to scan the parameters from the callers of the current frame stack. You can modify it to fish out pretty much anything.. there is no need to change the interfaces.

Hope it helps.

Cheers,
Waldek  

Andy McKay

unread,
Jan 2, 2012, 2:49:27 PM1/2/12
to django...@googlegroups.com
Inspecting your stack to find the user seems a pretty convoluted way to do it.

You could either a) not access users in your models b) explicitly pass the user around or c) just place your user into a local threading and retrieve it anywhere else for example: https://github.com/andymckay/arecibo/blob/master/listener/lib/userstorage/utils.py or any other methods for doing that.

Waldek Herka

unread,
Jan 2, 2012, 3:25:09 PM1/2/12
to Django users
@Andy: I agree it may be convoluted a bit indeed.. but your
alternative a) is not an option - I would not do it if I did not
needed it, wouldn't I ;-)
b) requires interface changes, sometimes it's even messier.. changing
the signatures means you need make sure all the callers would accept
that change , c) local threading way posted by you is not as straight
forward as it looks - I'd say it's very similar in complexity to
mine.. you still need to explicitly call 'activate' somewhere(view
method) to populate local thread storage while in my case you 'only'
inspect the call stack - the change is limited to a single block of
code.. All in all - I never claimed the way I solved it's perfect - it
just works fine for me and wanted to share it with others.

Thanks for your comment! I really appreciate it :-)

Cheerio,
Waldek

On Jan 2, 7:49 pm, Andy McKay <a...@clearwind.ca> wrote:
> Inspecting your stack to find the user seems a pretty convoluted way to do
> it.
>
> You could either a) not access users in your models b) explicitly pass the
> user around or c) just place your user into a local threading and retrieve
> it anywhere else for example:https://github.com/andymckay/arecibo/blob/master/listener/lib/usersto...

Andy McKay

unread,
Jan 3, 2012, 11:04:01 PM1/3/12
to django...@googlegroups.com
On Mon, Jan 2, 2012 at 12:25 PM, Waldek Herka <waldek...@googlemail.com> wrote:
c) local threading way posted by you is not as straight
forward as it looks - I'd say it's very similar in complexity to
mine.. you still need to explicitly call 'activate' somewhere(view
method)

Middleware can do that just fine.

Waldek Herka

unread,
Jan 4, 2012, 6:49:49 AM1/4/12
to Django users
Indeed, it's clean and goes well with Django design principles..
However if I need to track just a few updates a day on a single model
or admin form, the extra thousands or millions of calls generated by
middleware(on every single request), it is really hard to chew.. at
least one needs to be aware of this penalty. Otherwise really good
point again!
For a broader use I'd go for your solution, my usecase is a bit
different tough.

Cheers Andy!

On Jan 4, 4:04 am, Andy McKay <a...@clearwind.ca> wrote:
> On Mon, Jan 2, 2012 at 12:25 PM, Waldek Herka
> <waldek.he...@googlemail.com>wrote:
Reply all
Reply to author
Forward
0 new messages