171

I am trying to access access application configuration inside a blueprint authorisation.py which in a package api. I am initializing the blueprint in __init__.py which is used in authorisation.py.

__init__.py

from flask import Blueprint
api_blueprint = Blueprint("xxx.api", __name__, None)
from api import authorisation

authorisation.py

from flask import request, jsonify, current_app

from ..oauth_adapter import OauthAdapter
from api import api_blueprint as api

client_id = current_app.config.get('CLIENT_ID')
client_secret = current_app.config.get('CLIENT_SECRET')
scope = current_app.config.get('SCOPE')
callback = current_app.config.get('CALLBACK')

auth = OauthAdapter(client_id, client_secret, scope, callback)


@api.route('/authorisation_url')
def authorisation_url():
    url = auth.get_authorisation_url()
    return str(url)

I am getting RuntimeError: working outside of application context

I understand why that is but then what is the correct way of accessing those configuration settings?

----Update---- Temporarily, I have done this.

@api.route('/authorisation_url')
def authorisation_url():
    client_id, client_secret, scope, callback = config_helper.get_config()
    auth = OauthAdapter(client_id, client_secret, scope, callback)
    url = auth.get_authorisation_url()
    return str(url)
lord63. j
  • 4,500
  • 2
  • 22
  • 30
Chirdeep Tomar
  • 4,281
  • 8
  • 37
  • 66

8 Answers8

224

Use flask.current_app in place of app in the blueprint view.

from flask import current_app

@api.route("/info")
def get_account_num():
    num = current_app.config["INFO"]

The current_app proxy is only available in the context of a request.

davidism
  • 121,510
  • 29
  • 395
  • 339
weihuang
  • 2,257
  • 1
  • 9
  • 3
  • 36
    Note that the `current_app` proxy is only available in the context of a request. – sepehr Aug 06 '16 at 15:20
  • 5
    @sephr Any tips on how to access that request context from other places (without passing it as a parameter, but as some sort of global parameter)? – carkod Nov 16 '19 at 17:39
  • To solve the issue `RuntimeError: Working outside of application context`, you can have a look at this [link](https://stackoverflow.com/questions/39769666/flask-how-to-use-app-context-inside-blueprints). – Mathador Jul 15 '21 at 11:24
27

Overloading record method seems to be quite easy:

api_blueprint = Blueprint('xxx.api',  __name__, None)
api_blueprint.config = {}

@api_blueprint.record
def record_params(setup_state):
  app = setup_state.app
  api_blueprint.config = dict([(key,value) for (key,value) in app.config.iteritems()])
Ashalynd
  • 12,363
  • 2
  • 34
  • 37
17

To build on tbicr's answer, here's an example overriding the register method example:

from flask import Blueprint

auth = None

class RegisteringExampleBlueprint(Blueprint):
    def register(self, app, options, first_registration=False):
        global auth

        config = app.config
        client_id = config.get('CLIENT_ID')
        client_secret = config.get('CLIENT_SECRET')
        scope = config.get('SCOPE')
        callback = config.get('CALLBACK')

        auth = OauthAdapter(client_id, client_secret, scope, callback)

        super(RegisteringExampleBlueprint,
              self).register(app, options, first_registration)

the_blueprint = RegisteringExampleBlueprint('example', __name__)

And an example using the record decorator:

from flask import Blueprint
from api import api_blueprint as api

auth = None

# Note there's also a record_once decorator
@api.record
def record_auth(setup_state):
    global auth

    config = setup_state.app.config
    client_id = config.get('CLIENT_ID')
    client_secret = config.get('CLIENT_SECRET')
    scope = config.get('SCOPE')
    callback = config.get('CALLBACK')

    auth = OauthAdapter(client_id, client_secret, scope, callback)
ankostis
  • 8,579
  • 3
  • 47
  • 61
Kyle James Walker
  • 1,238
  • 14
  • 16
7

Blueprints have register method which called when you register blueprint. So you can override this method or use record decorator to describe logic which depends from app.

tbicr
  • 24,790
  • 12
  • 81
  • 106
6

The current_app approach is fine but you must have some request context. If you don't have one (some pre-work like testing, e.g.) you'd better place

with app.test_request_context('/'):

before this current_app call.

You will have RuntimeError: working outside of application context , instead.

Ben Usman
  • 7,969
  • 6
  • 46
  • 66
  • 5
    What about when the app is created in a factory and therefore 'app' (or whatever one calls the flask app) is not available to be imported? Inside requests it is no problem because during requests there is an app context, but when defining parts outside of request logic that require app config. how can one access app configuration if you can't use the app to create the context? – RobertoCuba Jun 18 '16 at 02:15
  • 1
    https://flask.palletsprojects.com/en/1.1.x/api/#flask.current_app – Bill Somen Jun 29 '20 at 03:19
2

You either need to import the main app variable (or whatever you have called it) that is returned by Flask():

from someplace import app
app.config.get('CLIENT_ID')

Or do that from within a request:

@api.route('/authorisation_url')
def authorisation_url():
    client_id = current_app.config.get('CLIENT_ID')
    url = auth.get_authorisation_url()
    return str(url)
Daniel Chatfield
  • 2,952
  • 3
  • 19
  • 17
  • 4
    Yeah, I didn't want to do either of the two. The first is creating cross references and second approach is not DRY. – Chirdeep Tomar Aug 13 '13 at 19:58
  • 2
    @ChirdeepTomar If the first approach is creating circular imports (that break the app) then there is something wrong with how your app is structured. – Daniel Chatfield Aug 13 '13 at 21:35
  • 21
    @DanielChatfield that's simply not true. The app object is the object which registers blueprints. Suggesting that it's correct for the blueprint then to import the app object will _always_ cause a circular dependency. See other answers for correct strategy. – sholsapp Jul 04 '14 at 21:42
  • @sholsapp I know it will create a circular import (just as it does in the flask docs: http://flask.pocoo.org/docs/patterns/packages/), I said if it created a circular import **that broke the app**. – Daniel Chatfield Jul 05 '14 at 08:27
1

You could also wrap the blueprint in a function and pass the app as an argument:

Blueprint:

def get_blueprint(app):
    bp = Blueprint()
    return bp

Main:

from . import my_blueprint
app.register_blueprint(my_blueprint.get_blueprint(app))
Georg Schölly
  • 124,188
  • 49
  • 220
  • 267
  • I tried this, but I got an "Internal Server Error". – MD004 Jul 11 '17 at 23:20
  • Any drawbacks with this approach? – Tuukka Mustonen Sep 21 '17 at 19:46
  • @Tuukka: I don't remember any particular drawbacks, it's been a bit too long since I used it. There might be some advantages using `flask.current_app` when you use the blueprint in multiple apps. I would suggest if this approach solves your issues to use it, Flask does not enforce a specific approach. – Georg Schölly Sep 27 '17 at 19:17
1

I know this is an old thread. But while writing a flask service, I used a method like this to do it. It's longer than the solutions above but it gives you the possibility to use customized class yourself. And frankly, I like to write services like this.

Step 1:

I added a struct in a different module file where we can make the class structs singleton. And I got this class structure from this thread already discussed. Creating a singleton in Python

class Singleton(type):
    _instances = {}

    def __call__(cls, *args, **kwargs):
        if cls not in cls._instances:
            cls._instances[cls] = super(Singleton, cls).__call__(*args, **kwargs)
        else:
            cls._instances[cls].__init__(*args, **kwargs)

        return cls._instances[cls]

Step 2:

Then I created a Singleton EnvironmentService class from our Singleton class that we defined above, just for our purpose. Instead of recreating such classes, create them once and use them in other modules, routes, etc. import. We can access the class with the same reference.

from flask import Config
from src.core.metaclass.Singleton import Singleton


class EnvironmentService(metaclass=Singleton):
    __env: Config = None

    def initialize(self, env):
        self.__env = env
        return EnvironmentService()

    def get_all(self):
        return self.__env.copy()

    def get_one(self, key):
        return self.__env.get(key)

Step 3:

Now we include the service in the application in our project root directory. This process should be applied before the routes.

from flask import Flask
from src.services.EnvironmentService import EnvironmentService

app = Flask(__name__)

# Here is our service
env = EnvironmentService().initialize(app.config)

# Your routes...

Usage:

Yes, we can now access our service from other routes.

from src.services.EnvironmentService import EnvironmentService

key = EnvironmentService().get_one("YOUR_KEY")
HyopeR
  • 387
  • 2
  • 11