Search code examples
pythonflaskopenidflask-login

How to make the Flask-OpenID object global to the app?


I'm using Flask-OpenID for user logins in my silly practice app.

The first thing to do create an openid object, which is used later to decorate the login handlers (and other things):

oid = flask.ext.openid.OpenID(app, '/path/to/store')

@oid.loginhandler
def login():
    ...

@oid.after_login
def after_login():
    ...

However, I want to initialize Flask-OpenID in my app's __init__.py file but use the OpenId object oid in other files, like my app's views.py file and possible others. What is the expected way to do this? How do flask developers usually make something like oid global to the app?

In this similar question, the SQLAlchemy object is moved to the models module but initialized somewhere else during application setup, which makes some sense because the db object is coupled to the models. The OpenID object follows the same pattern. But I don't want to put oid in views.py; it clearly doesn't belong there. So where would you put it? I can think of solutions, but I want to know what flask developers usually do. Here are some ideas:

  • Put oid in __init__.py and initialize it there as well. In this option, how do you access oid in another part of the app module?

  • Make a auth.py file for objects and methods associated with Flask-OpenID and Flask-Login. Then auth.oid would work anywhere in the app. Do I then create a new file for every extension that isn't coupled directly somewhere else? Is this overkill, or is it the right pattern for scaling up and staying organized?

  • Alternatively, create a single file for all of those little extension objects, perhaps called globals.py or exts.py. This seems awkward and kludgy. Or do most flask apps eventually have a random bucket for all this other crap that just needs to be located somewhere?


Solution

  • The three options have various trade offs (as you have discovered):

    1. Create auth in __init__.py - this results in circular references between your views and your base app - it can work, but it makes it more difficult to factor (as moving two otherwise unrelated imports can result in an error).
    2. Create a separate module to handle the separate concern - this avoids the circular reference problem and adds clarity to larger projects ("ah, here is where the code dealing with authentication belongs"). On the other hands, small projects with several small extensions can wind up with a Java-like explosion of modules.
    3. Create a separate module to initialize all of the Flask extensions that are used in the application. This avoids the circular reference problem too and keeps small projects from multiplying modules unnecessarily. However, on larger projects, such a module becomes a big-ball-of-mud. ("This is where the authentication, flat-file processing, and error handling are configured").

    There is also a 4th option - add auth to your view layer, as how you are authenticated is specific to your application logic (rather than your domain model, for example).

    I would recommend avoiding #1 entirely (the extra cost of skipping the "two modules" phase to go straight to the "three or more modules phase" is negligible). Which of the remaining three options is right for your project is really specific to the project and the developers working on it.