Quand Flask.g doit-il être utilisé?

174

J'ai vu que gcela passerait du contexte de la requête au contexte de l'application dans Flask 0.10, ce qui m'a rendu confus quant à l'utilisation prévue de g.

Ma compréhension (pour Flask 0.9) est que:

  • g vit dans le contexte de la requête, c'est-à-dire, créé à nouveau lorsque les requêtes commencent, et disponible jusqu'à la fin
  • gest destiné à être utilisé comme un "tableau noir de demande", où je peux mettre des éléments pertinents pour la durée de la demande (c'est-à-dire définir un drapeau au début de la demande et le gérer à la fin, éventuellement à partir d'une paire before_request/ after_request)
  • en plus de conserver l'état au niveau de la demande, gpeut et doit être utilisé pour la gestion des ressources, c'est-à-dire pour la conservation des connexions à la base de données, etc.

Laquelle de ces phrases n'est plus vraie dans Flask 0.10? Quelqu'un peut-il me diriger vers une ressource discutant des raisons du changement? Que dois-je utiliser comme «tableau noir de demande» dans Flask 0.10 - dois-je créer mon propre proxy thread-local spécifique à une application / extension et le pousser vers la pile de contexte before_request? Quel est l'intérêt de la gestion des ressources au niveau du contexte applicatif, si mon application vit longtemps (pas comme une requête) et donc les ressources ne sont jamais libérées?

Yaniv Aknin
la source
Je suis d'accord, c'est un changement assez étrange. Espérons que mitsuhiko implémente une sorte d'objet de contexte de requête à remplacer gdans 0.10, sinon il semble que beaucoup de code pourrait commencer à développer des bogues sournois.
Anorov
11
FWIW, Armin Ronacher (auteur de Flask) a publié une suite de "Advanced Flask Patterns" qui montre un exemple de code sur la façon d'utiliser le nouveau flask.g. speakerdeck.com/mitsuhiko/advanced-flask-patterns-1
Markus Unterwaditzer
1
également un nouveau contexte de demande implique un nouveau contexte d'application, donc cela devrait fonctionner correctement en utilisation normale
Ronny

Réponses:

120

Advanced Flask Patterns , tel que lié par Markus , explique certains des changements apportés à la version g0.10:

  • g vit maintenant dans le contexte de l'application.
  • Chaque demande pousse un nouveau contexte d'application , effaçant l'ancien, donc gpeut toujours être utilisé pour définir des indicateurs par demande sans changement de code.
  • Le contexte de l'application est affiché après avoir teardown_request été appelé. (La présentation d'Armin explique que cela est dû au fait que des choses comme la création de connexions à la base de données sont des tâches qui configurent l'environnement pour la requête, et ne doivent pas être gérées à l'intérieur before_requestet after_request)
theY4Kman
la source
Dans le code source auquel vous vous êtes lié, quand app_ctx is None or app_ctx.app != self.appvaut False, l'ancien contexte d'application semble être réutilisé? Cela ne semble pas correct, car le contexte de l'application "ne sera pas partagé entre les demandes" ...
nalzok
2
Faites-vous référence à la poussée deapp.app_context() ? Si tel est le cas, il convient de noter qu'il app_context()instancie un nouveau contexte d'application à chaque appel - il ne réutilise jamais un contexte.
theY4Kman
1
Oui c'est vrai, mais quand app_ctx is not None and app_ctx.app == self.app, la app_ctx = self.app.app_context()ligne n'est pas exécutée; seul self._implicit_app_ctx_stack.append(None)est exécuté dans ce cas.
nalzok
1
Oh, désolé, j'ai mal lu! Dans un environnement de production, il n'y a qu'une seule demande servie par thread (ou greenlet). Un seul RequestContextest poussé, donc un seul AppContextest poussé. Mais si le mode de débogage est activé et qu'une requête échoue, Flask enregistre le contexte afin qu'il puisse être utilisé avec le débogueur . Noneest ajouté au _app_ctx_stack, donc lorsque la demande est supprimée, il sait ne pas encore apparaître AppContext. La même chose se produit avec le client de test, qui conserve le contexte, donc il peut être inspecté.
theY4Kman
Ainsi, la portée de g est par requête (thread) et il ne conservera pas la valeur dans la requête suivante.
variable
83

En complément aux informations de ce fil: j'ai été un peu confus par le comportement de flask.gmoi aussi, mais quelques tests rapides m'ont aidé à le clarifier. Voici ce que j'ai essayé:

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

Et voici le résultat qu'il donne:

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

Comme l'a dit theY4Kman ci-dessus, "Chaque requête pousse un nouveau contexte d'application". Et comme le dit la documentation Flask , le contexte de l'application "ne sera pas partagé entre les requêtes". Maintenant, ce qui n'a pas été explicitement déclaré (bien que je suppose que cela soit implicite de ces déclarations), et ce que mes tests montrent clairement, c'est que vous ne devriez jamais créer explicitement plusieurs contextes de demande imbriqués dans un contexte d'application, car flask.g(and co) doesn ' t avoir une magie par laquelle il fonctionne dans les deux différents "niveaux" de contexte, avec différents états existant indépendamment au niveau de l'application et de la demande.

La réalité est que "contexte d'application" est potentiellement un nom assez trompeur, car app.app_context() c'est un contexte par demande , exactement le même que le "contexte de demande" . Considérez-le comme un «contexte de requête allégé», uniquement requis dans le cas où vous avez besoin de certaines des variables qui nécessitent normalement un contexte de requête, mais vous n'avez pas besoin d'accéder à un objet de requête (par exemple, lors de l'exécution d'opérations DB par lots dans un script shell). Si vous essayez d'étendre le contexte de l'application pour englober plus d'un contexte de demande, vous demandez des problèmes. Donc, plutôt que mon test ci-dessus, vous devriez plutôt écrire du code comme celui-ci avec les contextes de 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))

Ce qui donnera les résultats attendus:

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
la source
7
Évalué à cause du dernier paragraphe, les contextes de flask sont assez déroutants à comprendre au début. À partir du nom, vous avez le sentiment que le contexte de la demande est par demande et que le contexte de l'application existe même après une demande ou n'est pas affecté par sa durée de vie.
simanacci