Como acessar app.config em um blueprint?

114

Estou tentando acessar a configuração do aplicativo de acesso dentro de um blueprint authorisation.pyque em uma API de pacote. Estou inicializando o blueprint no __init__.pyqual é usado em 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)

Estou recebendo RuntimeError: trabalhando fora do contexto do aplicativo

Eu entendo por que isso acontece, mas qual é a maneira correta de acessar essas definições de configuração?

---- Atualizar ---- Temporariamente, eu fiz isso.

@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
fonte

Respostas:

133

Use flask.current_appno lugar de appna visualização do blueprint.

from flask import current_app

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

O current_appproxy está disponível apenas no contexto de uma solicitação .

Weihuang
fonte
25
Observe que o current_appproxy está disponível apenas no contexto de uma solicitação.
setembro de
1
@sephr Alguma dica sobre como acessar esse contexto de solicitação de outros lugares (sem passá-lo como um parâmetro, mas como algum tipo de parâmetro global)?
carkod
21

O 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
fonte
1
Para Python 3, use: app.config.items () em vez de app.config.iteritems ()
DhoTjai
1
Olá, preciso invocar ou registrar record_params. Tentei, mas não funcionou. Muito obrigado.
mrblue
Se você precisar acessar um aplicativo (por exemplo, obter a configuração para CONFIGURAR o blueprint), isso é ótimo!
Peter Lada
12

Para desenvolver a resposta de tbicr , aqui está um exemplo substituindo o exemplo do 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__)

E um exemplo usando o 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
fonte
'@ api.record' não funciona para mim,. De qual namespace é 'api'?
Tim Richardson
Desculpe, não copiei da linha da perguntafrom api import api_blueprint as api
Kyle James Walker
4

A current_appabordagem é boa, mas você deve ter algum contexto de solicitação. Se você não tiver um (alguns pré-trabalhos, como testes, por exemplo), é melhor colocar

with app.test_request_context('/'):

antes desta current_appchamada.

Você terá RuntimeError: working outside of application context, em vez disso.

Ben Usman
fonte
3
E quando o aplicativo é criado em uma fábrica e, portanto, 'aplicativo' (ou o que quer que seja chamado de aplicativo flask) não está disponível para ser importado? Solicitações internas não é problema porque durante as solicitações há um contexto de aplicativo, mas ao definir partes fora da lógica de solicitação que requerem configuração de aplicativo. como alguém pode acessar a configuração do aplicativo se você não pode usar o aplicativo para criar o contexto?
RobertoCuba
3

Você precisa importar a appvariável principal (ou como você a chamou) que é retornada por Flask():

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

Ou faça isso em uma solicitação:

@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
fonte
4
Sim, eu não queria fazer nenhum dos dois. A primeira é a criação de referências cruzadas e a segunda abordagem não é SECA.
Chirdeep Tomar
2
@ChirdeepTomar Se a primeira abordagem for criar importações circulares (que quebram o aplicativo), há algo errado com a forma como seu aplicativo está estruturado.
Daniel Chatfield
13
@DanielChatfield isso simplesmente não é verdade. O objeto de aplicativo é o objeto que registra projetos. Sugerir que é correto para o blueprint, em seguida, importar o objeto do aplicativo sempre causará uma dependência circular. Veja outras respostas para estratégia correta.
sholsapp
@sholsapp Eu sei que criará uma importação circular (assim como faz nos documentos do flask: flask.pocoo.org/docs/patterns/packages ), eu disse se ele criou uma importação circular que quebrou o aplicativo .
Daniel Chatfield
1

Você também pode envolver o blueprint em uma função e passar o appcomo um argumento:

Projeto:

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

A Principal:

from . import my_blueprint
app.register_blueprint(my_blueprint.get_blueprint(app))
Georg Schölly
fonte
Tentei fazer isso, mas recebi um "Erro interno do servidor".
MD004
Alguma desvantagem dessa abordagem?
Tuukka Mustonen,
@Tuukka: Não me lembro de nenhuma desvantagem em particular, faz muito tempo que não o uso. Pode haver algumas vantagens de usar flask.current_appquando você usa o blueprint em vários aplicativos. Eu sugeriria que se esta abordagem resolver seus problemas para usá-lo, o Flask não impõe uma abordagem específica.
Georg Schölly