¿Cómo acceder a app.config en un plano?

114

Estoy tratando de acceder a la configuración de la aplicación de acceso dentro de un plano authorisation.pyque en un paquete api. Estoy inicializando el plano en el __init__.pyque se usa en 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)

Recibo RuntimeError: trabajando fuera del contexto de la aplicación

Entiendo por qué es así, pero ¿cuál es la forma correcta de acceder a esos ajustes de configuración?

---- Actualizar ---- Temporalmente, he hecho esto.

@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)
Chirdeep Tomar
fuente

Respuestas:

133

Utilizar flask.current_appen lugar de appen la vista de planos.

from flask import current_app

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

El current_appproxy solo está disponible en el contexto de una solicitud .

Weihuang
fuente
25
Tenga en cuenta que el current_appproxy solo está disponible en el contexto de una solicitud.
septiembre
1
@sephr ¿Algún consejo sobre cómo acceder a ese contexto de solicitud desde otros lugares (sin pasarlo como parámetro, sino como algún tipo de parámetro global)?
Carkod
21

El recordmétodo de sobrecarga parece ser bastante fácil:

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
fuente
1
Para Python 3 use: app.config.items () en lugar de app.config.iteritems ()
DhoTjai
1
Hola, necesito invocar o registrar record_params, lo intenté pero no funcionó. Muchas gracias.
mrblue
Si necesita acceder a una aplicación (por ejemplo, obtener la configuración para CONFIGURAR el plano), ¡esto es genial!
Peter Lada
12

Para construir sobre la respuesta de tbicr , aquí hay un ejemplo que anula el ejemplo del registermétodo :

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__)

Y un ejemplo usando el recorddecorador :

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)
Kyle James Walker
fuente
'@ api.record' no me funciona,. ¿De qué espacio de nombres es 'api'?
Tim Richardson
Lo siento, no he copiado eso de la línea de la preguntafrom api import api_blueprint as api
Kyle James Walker
4

El current_appenfoque está bien, pero debe tener algún contexto de solicitud. Si no tiene uno (algunos trabajos previos como pruebas, por ejemplo), será mejor que coloque

with app.test_request_context('/'):

antes de esta current_appllamada.

Tendrás RuntimeError: working outside of application context, en su lugar.

Ben Usman
fuente
3
¿Qué sucede cuando la aplicación se crea en una fábrica y, por lo tanto, la 'aplicación' (o lo que sea que se llame la aplicación del matraz) no está disponible para ser importada? Dentro de las solicitudes no hay problema porque durante las solicitudes hay un contexto de aplicación, pero cuando se definen partes fuera de la lógica de solicitud que requieren la configuración de la aplicación. ¿Cómo se puede acceder a la configuración de la aplicación si no se puede utilizar la aplicación para crear el contexto?
RobertoCuba
3

Necesita importar la appvariable principal (o como la haya llamado) que es devuelta por Flask():

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

O haz eso desde dentro de una solicitud:

@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
fuente
4
Sí, no quería hacer ninguna de las dos. El primero es crear referencias cruzadas y el segundo enfoque no es SECO.
Chirdeep Tomar
2
@ChirdeepTomar Si el primer enfoque es crear importaciones circulares (que rompen la aplicación), entonces hay algún problema con la estructura de la aplicación.
Daniel Chatfield
13
@DanielChatfield, eso simplemente no es cierto. El objeto de la aplicación es el objeto que registra los planos. Sugerir que es correcto para el plano y luego importar el objeto de la aplicación siempre causará una dependencia circular. Vea otras respuestas para la estrategia correcta.
sholsapp
@sholsapp Sé que creará una importación circular (tal como lo hace en los documentos del matraz: flask.pocoo.org/docs/patterns/packages ), dije que si creó una importación circular que rompió la aplicación .
Daniel Chatfield
1

También puede envolver el plano en una función y pasar el app como argumento:

Plano:

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

Principal:

from . import my_blueprint
app.register_blueprint(my_blueprint.get_blueprint(app))
Georg Schölly
fuente
Intenté esto, pero obtuve un "Error interno del servidor".
MD004
¿Algún inconveniente con este enfoque?
Tuukka Mustonen
@Tuukka: No recuerdo ningún inconveniente en particular, ha pasado demasiado tiempo desde que lo usé. Puede haber algunas ventajas flask.current_appal usar el plano en varias aplicaciones. Sugeriría que si este enfoque resuelve sus problemas para usarlo, Flask no aplica un enfoque específico.
Georg Schölly