210 votes

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

J'ai vu que g passera du contexte de la demande 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 demande, c'est-à-dire qu'il est créé à chaque démarrage de la demande et est disponible jusqu'à sa fin
  • g est destiné à être utilisé comme un "tableau noir de la demande", où je peux mettre des choses pertinentes pour la durée de la demande (c'est-à-dire définir un indicateur au début de la demande et le gérer à la fin, éventuellement à partir d'une paire before_request/after_request)
  • en plus de contenir l'état au niveau de la demande, g peut et doit être utilisé pour la gestion des ressources, c'est-à-dire tenir des connexions de base de données, etc.

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

0 votes

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 pour remplacer g dans la version 0.10, sinon il semble que beaucoup de code pourraient commencer à développer des bugs sournois.

13 votes

FWIW, Armin Ronacher (auteur de Flask) a sorti une suite de "Schémas avancés de Flask" qui montre du code d'exemple sur comment utiliser le nouveau flask.g. speakerdeck.com/mitsuhiko/advanced-flask-patterns-1

1 votes

Également une nouvelle demande de contexte implique un nouveau contexte d'application, donc cela devrait simplement fonctionner correctement en utilisation normale

143voto

theY4Kman Points 441

Modèles Flask avancés, comme mentionné par Markus, explique certains des changements apportés à g dans la version 0.10 :

  • g vit désormais dans le contexte de l'application.
  • Chaque requête crée un nouveau contexte d'application, effaçant l'ancien, donc g peut toujours être utilisé pour définir des drapeaux par requête sans modification du code.
  • Le contexte de l'application est retiré après l'appel de teardown_request. (La présentation d'Armin explique que des tâches comme la création de connexions à la base de données sont des actions qui préparent l'environnement de la requête, et ne doivent pas être gérées à l'intérieur de before_request et after_request)

0 votes

Dans le code source auquel vous avez fait référence, lorsque app_ctx est None ou app_ctx.app != self.app est False, l'ancien contexte d'application semble être réutilisé ? Cela ne semble pas correct, car le contexte d'application "ne sera pas partagé entre les requêtes"...

2 votes

Faites-vous référence au push de app.app_context()? Si c'est le cas, il convient de noter que app_context() instancie un nouveau contexte d'application à chaque appel - il ne réutilise jamais de contexte.

1 votes

Oui, c'est vrai, mais lorsque app_ctx n'est pas None et app_ctx.app == self.app, la ligne app_ctx = self.app.app_context() n'est pas exécutée; seule self._implicit_app_ctx_stack.append(None) est exécutée dans ce cas.

125voto

Jaza Points 1371

En complément des informations de ce fil : j'ai été un peu confus par le comportement de flask.g également, 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('dans le contexte de l'application, avant le premier contexte de requête')
    print('définition de g.foo à abc')
    g.foo = 'abc'
    print('g.foo devrait être abc, est : {0}'.format(g.foo))

    with app.test_request_context():
        print('dans le premier contexte de requête')
        print('g.foo devrait être abc, est : {0}'.format(g.foo))
        print('définition de g.foo à xyz')
        g.foo = 'xyz'
        print('g.foo devrait être xyz, est : {0}'.format(g.foo))

    print('dans le contexte de l'application, après le premier contexte de requête')
    print('g.foo devrait être abc, est : {0}'.format(g.foo))

    with app.test_request_context():
        print('dans le second contexte de requête')
        print('g.foo devrait être abc, est : {0}'.format(g.foo))
        print('définition de g.foo à pqr')
        g.foo = 'pqr'
        print('g.foo devrait être pqr, est : {0}'.format(g.foo))

    print('dans le contexte de l'application, après le second contexte de requête')
    print('g.foo devrait être abc, est : {0}'.format(g.foo))

Et voici le résultat obtenu :

dans le contexte de l'application, avant le premier contexte de requête
définition de g.foo à abc
g.foo devrait être abc, est : abc  

dans le premier contexte de requête
g.foo devrait être abc, est : abc
définition de g.foo à xyz
g.foo devrait être xyz, est : xyz  

dans le contexte de l'application, après le premier contexte de requête
g.foo devrait être abc, est : xyz  

dans le second contexte de requête
g.foo devrait être abc, est : xyz
définition de g.foo à pqr
g.foo devrait être pqr, est : pqr  

dans le contexte de l'application, après le second contexte de requête
g.foo devrait être abc, est : pqr

Comme l'a dit Y4Kman ci-dessus, "Chaque requête pousse un nouveau contexte d'application". Et comme le disent les docs de Flask, le contexte d'application "ne sera pas partagé entre les requêtes". Maintenant, ce qui n'a pas été explicitement dit (bien que je suppose que cela découle de ces déclarations), et ce que mes tests montrent clairement, c'est que vous ne devriez jamais créer explicitement plusieurs contextes de requête imbriqués dans un contexte d'application, car flask.g (et compagnie) n'a pas de magie permettant de fonctionner dans les deux "niveaux" de contexte, avec des états différents existant indépendamment aux niveaux de l'application et de la requête.

La réalité est que le "contexte de l'application" est potentiellement un nom assez trompeur, car app.app_context() est un contexte par requête, exactement le même que le "contexte de requête". Pensez-y comme un "contexte de requête allégé", uniquement nécessaire 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 de base de données en lot dans un script shell). Si vous essayez d'étendre le contexte de l'application pour englober plus d'un contexte de requête, vous cherchez des ennuis. Donc, plutôt que mon test ci-dessus, vous devriez plutôt écrire du code comme ceci avec les contextes de Flask :

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

with app.app_context():
    print('dans le contexte de l'application, avant le premier contexte de requête')
    print('définition de g.foo à abc')
    g.foo = 'abc'
    print('g.foo devrait être abc, est : {0}'.format(g.foo))

with app.test_request_context():
    print('dans le premier contexte de requête')
    print('g.foo devrait être None, est : {0}'.format(g.get('foo')))
    print('définition de g.foo à xyz')
    g.foo = 'xyz'
    print('g.foo devrait être xyz, est : {0}'.format(g.foo))

with app.test_request_context():
    print('dans le second contexte de requête')
    print('g.foo devrait être None, est : {0}'.format(g.get('foo')))
    print('définition de g.foo à pqr')
    g.foo = 'pqr'
    print('g.foo devrait être pqr, est : {0}'.format(g.foo))

Ce qui donnera les résultats attendus :

dans le contexte de l'application, avant le premier contexte de requête
définition de g.foo à abc
g.foo devrait être abc, est : abc  

dans le premier contexte de requête
g.foo devrait être None, est : None
définition de g.foo à xyz
g.foo devrait être xyz, est : xyz  

dans le second contexte de requête
g.foo devrait être None, est : None
définition de g.foo à pqr
g.foo devrait être pqr, est : pqr

20 votes

Upvoted à cause du dernier paragraphe, les contextes de Flask sont assez confus à comprendre au début. D'après le nom, on a l'impression que le contexte de la requête est par requête et que le contexte de l'application existe même après une requête ou n'est pas affecté par sa durée de vie.

0 votes

Mata, c'est une belle explication. Merci

0 votes

Il peut être utile de penser à app_context comme "contexte d'application WSGI", car les applications WSGI sont des appelables invoquées pour gérer chaque requête. Ainsi, le app_context ne persiste pas pendant toute la durée de l'application Flask, mais plutôt pendant la durée de l'application WSGI.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X