Quando usare Flask.g?


174

Ho visto che gpasserà dal contesto della richiesta al contesto dell'app in Flask 0.10, il che mi ha fatto confondere sull'uso previsto di g.

La mia comprensione (per Flask 0.9) è che:

  • g vive nel contesto della richiesta, ovvero creato di nuovo all'inizio delle richieste e disponibile fino alla fine
  • gè inteso per essere usato come una "lavagna di richiesta", dove posso mettere cose rilevanti per la durata della richiesta (cioè, impostare un flag all'inizio della richiesta e gestirlo alla fine, possibilmente da una before_request/ after_requestcoppia)
  • oltre a contenere lo stato a livello di richiesta, gpuò e deve essere utilizzato per la gestione delle risorse, ad esempio per contenere le connessioni al database, ecc.

Quale di queste frasi non è più vera in Flask 0.10? Qualcuno può indicarmi una risorsa che discute le ragioni del cambiamento? Cosa dovrei usare come "richiesta lavagna" in Flask 0.10 - dovrei creare il mio proxy locale thread / app specifico per l'estensione / e spingerlo nello stack di contesto before_request? Qual è lo scopo della gestione delle risorse nel contesto dell'applicazione, se la mia applicazione vive a lungo (non come una richiesta) e quindi le risorse non vengono mai liberate?


Sono d'accordo, è un cambiamento abbastanza strano. Eventualmente mitsuhiko implementa un tipo di oggetto contestuale di richiesta da sostituire gin 0.10, altrimenti sembra che un sacco di codice potrebbe iniziare a sviluppare alcuni bug subdoli.
Anorov,

11
FWIW, Armin Ronacher (autore di Flask) ha pubblicato un sequel di "Advanced Flask Patterns" che mostra alcuni esempi di codice su come utilizzare il nuovo flask.g. speakerdeck.com/mitsuhiko/advanced-flask-patterns-1
Markus Unterwaditzer

1
anche un nuovo contesto di richiesta implica un nuovo contesto di app, quindi dovrebbe funzionare bene in condizioni normali
Ronny,

Risposte:


120

I modelli di pallone avanzati , collegati da Markus , spiegano alcune delle modifiche apportate a g0.10:

  • g ora vive nel contesto dell'applicazione.
  • Ogni richiesta inserisce un nuovo contesto applicativo , cancellando quello vecchio, quindi gpuò ancora essere utilizzato per impostare flag per richiesta senza modificare il codice.
  • Il contesto dell'applicazione viene visualizzato dopo la teardown_request chiamata. (La presentazione di Armin spiega questo perché cose come la creazione di connessioni DB sono attività che impostano l'ambiente per la richiesta e non devono essere gestite all'interno before_requeste after_request)

Nel codice sorgente a cui ti sei collegato, quando app_ctx is None or app_ctx.app != self.appè False, il vecchio contesto dell'applicazione sembra essere riutilizzato? Questo non sembra essere corretto, dal momento che il contesto dell'applicazione "non sarà condiviso tra le richieste" ...
nalzok,

2
Ti riferisci alla spinta diapp.app_context() ? In tal caso, va notato che app_context()crea un'istanza per un nuovo contesto di applicazione ogni chiamata: non riutilizza mai un contesto.
theY4Kman,

1
Sì, è vero, ma quando app_ctx is not None and app_ctx.app == self.appla app_ctx = self.app.app_context()linea non viene eseguita; self._implicit_app_ctx_stack.append(None)viene eseguito solo in questo caso.
nalzok,

1
Oh, scusa, ho letto male! In un'impostazione di produzione, viene fornita una sola richiesta per thread (o greenlet). Solo uno RequestContextè spinto, in modo che solo uno AppContextè spinto. Ma se la modalità debug è attiva e una richiesta fallisce, Flask salva il contesto , quindi può essere usato con il debugger . Noneviene aggiunto a _app_ctx_stack, quindi quando la richiesta viene demolita, sa di non far apparire il AppContextgiusto ancora. La stessa cosa si verifica con il client di test, che mantiene il contesto, quindi può essere ispezionato.
theY4Kman,

Quindi l'ambito di g è per richiesta (thread) e non manterrà il valore nella richiesta successiva.
variabile

83

Come addendum alle informazioni in questo thread: sono stato un po 'confuso anche dal comportamento di flask.g, ma alcuni test rapidi mi hanno aiutato a chiarirle. Ecco cosa ho provato:

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

Ed ecco l'output che dà:

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

Come detto sopra da Y4Kman, "Ogni richiesta introduce un nuovo contesto applicativo". E come dicono i documenti di Flask , il contesto dell'applicazione "non sarà condiviso tra le richieste". Ora, ciò che non è stato esplicitamente dichiarato (anche se immagino sia implicito da queste affermazioni), e ciò che i miei test mostrano chiaramente, è che non dovresti mai creare esplicitamente contesti di richieste multiple nidificati all'interno di un contesto di applicazione, perché flask.g(e co) non lo fanno Non ha alcuna magia per cui funzioni nei due diversi "livelli" di contesto, con stati diversi esistenti indipendentemente ai livelli di applicazione e richiesta.

La realtà è che "contesto applicativo" è potenzialmente un nome abbastanza fuorviante, perché app.app_context() è un contesto per richiesta , esattamente lo stesso del "contesto richiesta" . Consideralo come un "lite di contesto di richiesta", richiesto solo nel caso in cui hai bisogno di alcune delle variabili che normalmente richiedono un contesto di richiesta, ma non hai bisogno di accedere a nessun oggetto di richiesta (ad es. Quando esegui operazioni DB batch in un shell script). Se si tenta di estendere il contesto dell'applicazione per includere più di un contesto di richiesta, si stanno verificando problemi. Quindi, piuttosto che il mio test sopra, dovresti invece scrivere codice come questo con i contesti di 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))

Che darà i risultati attesi:

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

7
Votato a causa dell'ultimo paragrafo, i contesti di pallone sono piuttosto confusi da capire all'inizio. Dal nome, si ha la sensazione che il contesto della richiesta sia per richiesta e che il contesto dell'app esista anche dopo una richiesta o che non sia influenzato dalla sua durata.
simanacci,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.