62

As I understand the g variable in Flask, it should provide me with a global place to stash data like holding the current user after login. Is this correct?

I would like my navigation to display my user's name, once logged in, across the site.

My views contain

from Flask import g #among other things

During login, I assign

user = User.query.filter_by(username = form.username.data).first()
if validate(user):
    session['logged_in'] = True
    g.user = user

It doesn't seem I can access g.user. Instead, when my base.html template has the following...

<ul class="nav">
    {% if session['logged_in'] %}
        <li class="inactive">logged in as {{ g.user.username }}</li>
    {% endif %}
</ul>

I get the error:

jinja2.exceptions.UndefinedError
UndefinedError: 'flask.ctx._RequestGlobals object' has no attribute 'user'

The login otherwise works fine. What am I missing?

Mittenchops
  • 15,641
  • 28
  • 103
  • 200
  • 2
    `g.user` isn't specific to a session- if I understand what you're trying to do correctly, you'd probably be better off doing something like `session['user'] = user` instead. – Nathan Nov 29 '12 at 01:35
  • 1
    Yikes! Okay, if it isn't specific to the session, it sounds like you're right. – Mittenchops Nov 29 '12 at 03:04
  • 2
    Wait, can that be true? I'm looking at the most extensive online tutorial I've found: http://blog.miguelgrinberg.com/post/the-flask-mega-tutorial-part-vi-profile-page-and-avatars and he frequently uses the g variable for things like `g.user = current_user`. Is that actually incorrect? – Mittenchops Nov 29 '12 at 17:08
  • 3
    Actually, both you and @SeanVieira are right and I am thoroughly and helplessly misguided- even the [Flask docs](http://flask.pocoo.org/docs/api/#flask.g) say that I'm wrong. Sorry for the trouble. :/ – Nathan Nov 30 '12 at 03:02

3 Answers3

80

g is a thread local and is per-request (See A Note On Proxies). The session is also a thread local, but in the default context is persisted to a MAC-signed cookie and sent to the client.

The problem that you are running into is that session is rebuilt on each request (since it is sent to the client and the client sends it back to us), while data set on g is only available for the lifetime of this request.

The simplest thing to do (note simple != secure - if you need secure take a look at Flask-Login) is to simply add the user's ID to the session and load the user on each request:

@app.before_request
def load_user():
    if session["user_id"]:
        user = User.query.filter_by(username=session["user_id"]).first()
    else:
        user = {"name": "Guest"}  # Make it better, use an anonymous User instead

    g.user = user
Sean Vieira
  • 140,251
  • 31
  • 286
  • 277
  • 2
    Thanks, Sean, that clarifies for me what's going on with g. I don't suppose you have further comment on what is the insecure part about sessions and how to implement this better with Flask-Login? Is the solution so simple as use Flask-Login and then using the session[] variable is then secure? Would you recommend against using g at all as seen in the tutorial I linked to? – Mittenchops Dec 05 '12 at 19:54
  • 3
    @Mittenchops - Flask-Login still uses `session` - it simply deals with all the corner cases of managing login / logouts, forcing re-authentication when necessary, etc. The potential of getting those corner cases wrong is what makes your application less secure. `g` is safe to use regardless - it is not constructed out of user-provided data and so may be considered "safe". – Sean Vieira Dec 05 '12 at 23:15
  • 1
    Sean, can I follow up one thing on this? Suppose I'm using the kvsession to secure my cookies (rather than simply to sign them). Is there then any benefit using g instead of session? Is there some esoteric difference in memory usage or something whether I set session["user"] or g.user? – Mittenchops Dec 29 '12 at 21:23
  • 2
    @Mittenchops - as long as what you are storing is serializable `session` is probably a better bet (simply because `g` is torn down at the end of every request while `session` is preserved between requests). So, assuming that `User` could be serialized you could do: `if "user" not in session: # query database` and avoid numerous calls to the DB (An un-enhanced `g` would require a DB call on every request since it is always built afresh). – Sean Vieira Dec 31 '12 at 04:26
  • 3
    Since I'm a bit new to web development, may I check if I understand everything? I should use session['key']='value' if 'value' is a string (or something which can be stored as a string), and use g.key = value is values is a bit more 'complicated' python object (or something what is not storable as a string). And session will be stored per connection 'until the browser is closed', and g will be deleted for each request i.e. for each 'enter' pressed in the address bar. – Emil Vatai Dec 18 '14 at 14:53
  • 1
    Well, the Flask-Login document has been moved [here](https://flask-login.readthedocs.org/en/latest/) now. – Casimir Crystal Oct 08 '15 at 02:08
  • 2
    @Kevin - fixed! Thanks for helping keep the answer up-to-date! – Sean Vieira Oct 08 '15 at 02:10
  • It seems from [the code](https://github.com/pallets/flask/blob/master/flask/globals.py) that `g` is **not** defined per request but per app context. That makes a hell of a difference. – Antoine Lizée Aug 31 '16 at 12:34
  • 1
    @AntoineLizée - that is correct now - at the time this answer was written though it was not (appcontext was added in the 0.10 release, which came out the month after this answer was written). I'll update. – Sean Vieira Aug 31 '16 at 13:57
  • In either case, what is *on* `g` is done for each request (you can't share values across requests on `g`). – Sean Vieira Aug 31 '16 at 15:33
13

Minor correction, the g object is bound to the application context now instead of the request context.

"Starting with Flask 0.10 this is stored on the application context and no longer on the request context which means it becomes available if only the application context is bound and not yet a request."

simic0de
  • 741
  • 1
  • 8
  • 21
AlexLordThorsen
  • 6,777
  • 2
  • 39
  • 82
  • So the g will be the same on every request on the same proccess ? So if i send a request and save something on 'g' and another request cames ths value would still be there ? – FabioCosta Feb 05 '16 at 16:10
  • 1
    @FabioCosta nope, you still need the session for that – pors Jun 20 '16 at 13:10
-26

I would try to get rid of globals all together, think of your applications as a set of functions that perform tasks, each function has inputs and outputs, and should not touch globals. Just fetch your user and pass it around, it makes your code much more testable. Better yet: get rid of flask, flask promotes using globals such as

from flask import request
TjerkW
  • 1,813
  • 17
  • 25