Quando o Flask.g deve ser usado?

173

I saw que gvai passar a partir do contexto de solicitação ao contexto aplicativo na garrafa de 0,10, o que me fez confuso sobre o uso pretendido g.

Meu entendimento (para o Flask 0.9) é que:

  • g vive no contexto da solicitação, ou seja, criado novamente quando a solicitação é iniciada e disponível até o término
  • gdestina-se a ser usado como um "quadro de solicitação", onde eu posso colocar coisas relevantes para a duração da solicitação (ou seja, definir uma sinalização no início da solicitação e manipulá-la no final, possivelmente de um before_request/ after_requestpar)
  • além de manter o estado de nível de solicitação, gpode e deve ser usado para gerenciamento de recursos, ou seja, manter conexões de banco de dados, etc.

Quais dessas frases não são mais verdadeiras no Flask 0.10? Alguém pode me indicar um recurso que discuta os motivos da mudança? O que devo usar como um "quadro de solicitação" no Flask 0.10 - devo criar meu próprio proxy local de segmento específico de aplicativo / extensão e enviá-lo para a pilha de contexto before_request? Qual é o sentido do gerenciamento de recursos no contexto do aplicativo, se meu aplicativo permanecer por um longo tempo (não como uma solicitação) e, assim, os recursos nunca forem liberados?

Yaniv Aknin
fonte
Eu concordo, isso é uma mudança bastante estranha. Espero que o mitsuhiko implemente algum tipo de objeto de contexto de solicitação para substituir gna 0.10, caso contrário, parece que muito código pode começar a desenvolver alguns erros desonestos.
Anorov
11
FWIW, Armin Ronacher (autor do Flask) lançou uma sequência de "Advanced Flask Patterns", que mostra alguns exemplos de código sobre como usar o novo flask.g. speakerdeck.com/mitsuhiko/advanced-flask-patterns-1 #
Markus Unterwaditzer
1
também um novo contexto pedido implica um novo contexto aplicativo, por isso deve apenas funcionar bem durante o uso normal
Ronny

Respostas:

119

O Advanced Flask Patterns , conforme vinculado por Markus , explica algumas das alterações gno 0.10:

  • g agora vive no contexto do aplicativo.
  • Cada solicitação cria um novo contexto de aplicativo , limpando o antigo, portanto gainda pode ser usado para definir sinalizadores por solicitação sem alterar o código.
  • O contexto do aplicativo é exibido depois que teardown_request é chamado. (A apresentação de Armin explica isso porque coisas como criar conexões com o banco de dados são tarefas que configuram o ambiente para a solicitação e não devem ser tratadas internamente before_requeste after_request)
theY4Kman
fonte
No código-fonte ao qual você vinculou, quando app_ctx is None or app_ctx.app != self.appé False, o contexto antigo do aplicativo parece ser reutilizado? Este não parece ser certo, uma vez que o contexto de aplicação "não será compartilhada entre os pedidos" ...
nalzok
2
Você está se referindo ao empurrão deapp.app_context() ? Nesse caso, deve-se notar que app_context()instancia um novo contexto de aplicativo a cada chamada - nunca reutiliza um contexto.
theY4Kman
1
Sim, isso é verdade, mas quando app_ctx is not None and app_ctx.app == self.app, a app_ctx = self.app.app_context()linha não é executada; somente self._implicit_app_ctx_stack.append(None)é executado neste caso.
Nalzok 19/07
1
Oh, desculpe, eu interpretei errado! Em uma configuração de produção, há apenas uma solicitação atendida por encadeamento (ou greenlet). Somente um RequestContexté empurrado, então apenas um AppContexté empurrado. Mas se o modo de depuração estiver ativado e uma solicitação falhar, o Flask salva o contexto , para que possa ser usado com o depurador . Noneé anexado ao _app_ctx_stack, portanto, quando a solicitação está sendo cortada, ele sabe que ainda não deve aparecer AppContext. O mesmo ocorre com o cliente de teste, que mantém o contexto, para que possa ser inspecionado.
theY4Kman
Portanto, o escopo de g é por solicitação (encadeamento) e não reterá o valor na solicitação subsequente.
variável
83

Como um adendo às informações neste tópico: Também fiquei um pouco confuso com o comportamento flask.g, mas alguns testes rápidos me ajudaram a esclarecê-lo. Aqui está o que eu tentei:

from flask import Flask, g
app = Flask(__name__)

with app.app_context():
    print('in app context, before first request context')
    print('setting g.foo to abc')
    g.foo = 'abc'
    print('g.foo should be abc, is: {0}'.format(g.foo))

    with app.test_request_context():
        print('in first request context')
        print('g.foo should be abc, is: {0}'.format(g.foo))
        print('setting g.foo to xyz')
        g.foo = 'xyz'
        print('g.foo should be xyz, is: {0}'.format(g.foo))

    print('in app context, after first request context')
    print('g.foo should be abc, is: {0}'.format(g.foo))

    with app.test_request_context():
        print('in second request context')
        print('g.foo should be abc, is: {0}'.format(g.foo))
        print('setting g.foo to pqr')
        g.foo = 'pqr'
        print('g.foo should be pqr, is: {0}'.format(g.foo))

    print('in app context, after second request context')
    print('g.foo should be abc, is: {0}'.format(g.foo))

E aqui está o resultado que ele fornece:

in app context, before first request context
setting g.foo to abc
g.foo should be abc, is: abc  

in first request context
g.foo should be abc, is: abc
setting g.foo to xyz
g.foo should be xyz, is: xyz  

in app context, after first request context
g.foo should be abc, is: xyz  

in second request context
g.foo should be abc, is: xyz
setting g.foo to pqr
g.foo should be pqr, is: pqr  

in app context, after second request context
g.foo should be abc, is: pqr

Como o Y4Kman disse acima, "Toda solicitação impulsiona um novo contexto de aplicativo". E, como dizem os documentos do Flask , o contexto do aplicativo "não será compartilhado entre solicitações". Agora, o que não foi declarado explicitamente (embora eu ache que esteja implícito nessas declarações) e o que meus testes mostram claramente é que você nunca deve criar explicitamente vários contextos de solicitação aninhados dentro de um contexto de aplicativo, porque flask.g(e co) não ' não possui mágica em que funcione nos dois "níveis" diferentes de contexto, com estados diferentes existindo independentemente nos níveis de aplicação e solicitação.

A realidade é que "contexto de aplicativo" é potencialmente um nome enganoso, porque app.app_context() é um contexto por solicitação , exatamente o mesmo que "contexto de solicitação" . Pense nisso como uma "lista de contexto de solicitação", necessária apenas no caso em que você precisa de algumas das variáveis ​​que normalmente requerem um contexto de solicitação, mas não precisa de acesso a nenhum objeto de solicitação (por exemplo, ao executar operações de banco de dados em lote em um shell script). Se você tentar estender o contexto do aplicativo para abranger mais de um contexto de solicitação, estará solicitando problemas. Portanto, em vez do meu teste acima, você deve escrever um código como este nos contextos do Flask:

from flask import Flask, g
app = Flask(__name__)

with app.app_context():
    print('in app context, before first request context')
    print('setting g.foo to abc')
    g.foo = 'abc'
    print('g.foo should be abc, is: {0}'.format(g.foo))

with app.test_request_context():
    print('in first request context')
    print('g.foo should be None, is: {0}'.format(g.get('foo')))
    print('setting g.foo to xyz')
    g.foo = 'xyz'
    print('g.foo should be xyz, is: {0}'.format(g.foo))

with app.test_request_context():
    print('in second request context')
    print('g.foo should be None, is: {0}'.format(g.get('foo')))
    print('setting g.foo to pqr')
    g.foo = 'pqr'
    print('g.foo should be pqr, is: {0}'.format(g.foo))

O que dará os resultados esperados:

in app context, before first request context
setting g.foo to abc
g.foo should be abc, is: abc  

in first request context
g.foo should be None, is: None
setting g.foo to xyz
g.foo should be xyz, is: xyz  

in second request context
g.foo should be None, is: None
setting g.foo to pqr
g.foo should be pqr, is: pqr
Jaza
fonte
7
Promovido por causa do último parágrafo, os contextos do frasco são bastante confusos para serem entendidos no início. Pelo nome, você tem a sensação de que o contexto da solicitação é por solicitação e que o contexto do aplicativo existe mesmo após uma solicitação ou não é afetado por sua vida útil.
simanacci