¿Cuándo se debe usar Flask.g?

174

Me sierra que gva a pasar del contexto de la petición al contexto de la aplicación en el frasco 0,10, lo que me hizo confundir sobre el uso previsto de g.

Tengo entendido (para Flask 0.9) es que:

  • g vive en el contexto de la solicitud, es decir, se crea de nuevo cuando las solicitudes comienzan y está disponible hasta que finaliza
  • gestá destinado a ser utilizado como un "pizarrón de solicitud", donde puedo poner cosas relevantes para la duración de la solicitud (es decir, establecer una bandera al comienzo de la solicitud y manejarla al final, posiblemente desde un before_request/ after_requestpar)
  • Además de mantener el estado de nivel de solicitud, se gpuede y se debe usar para la gestión de recursos, es decir, mantener conexiones de bases de datos, etc.

¿Cuál de estas oraciones ya no es verdadera en Flask 0.10? ¿Alguien puede señalarme un recurso que discuta las razones del cambio? ¿Qué debo usar como un "pizarrón de solicitud" en el Frasco 0.10? ¿Debería crear mi propio proxy local de subproceso específico de aplicación / extensión y llevarlo a la pila de contexto before_request? ¿Cuál es el punto de la gestión de recursos en el contexto de la aplicación, si mi aplicación vive durante un tiempo prolongado (no como una solicitud) y, por lo tanto, los recursos nunca se liberan?

Yaniv Aknin
fuente
Estoy de acuerdo, ese es un cambio bastante extraño. Con suerte, mitsuhiko implementa algún tipo de objeto de contexto de solicitud para reemplazar gen 0.10, de lo contrario, parece que mucho código podría comenzar a desarrollar algunos errores tortuosos.
Anorov
11
FWIW, Armin Ronacher (autor de Flask) ha lanzado una secuela de "Patrones avanzados de frascos" que muestra un código de ejemplo sobre cómo usar el nuevo flask.g. speakerdeck.com/mitsuhiko/advanced-flask-patterns-1
Markus Unterwaditzer
1
también un nuevo contexto de solicitud implica un nuevo contexto de aplicación, por lo que debería funcionar bien en uso normal
Ronny

Respuestas:

120

Los patrones de matraz avanzados , como los vincula Markus , explica algunos de los cambios gen 0.10:

  • g ahora vive en el contexto de la aplicación.
  • Cada solicitud empuja un nuevo contexto de aplicación , borrando el anterior, por glo que aún se puede usar para establecer banderas por solicitud sin cambiar el código.
  • El contexto de la aplicación aparece después de que teardown_request se llama. (La presentación de Armin explica que esto se debe a que cosas como crear conexiones de base de datos son tareas que configuran el entorno para la solicitud, y no deben manejarse dentro before_requesty after_request)
theY4Kman
fuente
En el código fuente al que se vinculó, ¿cuándo app_ctx is None or app_ctx.app != self.appes falso, el antiguo contexto de la aplicación parece reutilizarse? Esto no parece ser correcto, ya que el contexto de la aplicación "no se compartirá entre solicitudes" ...
nalzok
2
¿Te refieres al empuje deapp.app_context() ? Si es así, debe tenerse en cuenta que app_context()crea una instancia de un nuevo contexto de aplicación en cada llamada; nunca reutiliza un contexto.
theY4Kman
1
Sí, eso es cierto, pero cuando app_ctx is not None and app_ctx.app == self.appla app_ctx = self.app.app_context()línea no se ejecuta; solo self._implicit_app_ctx_stack.append(None)se ejecuta en este caso.
nalzok
1
¡Oh, lo siento, lo he leído mal! En una configuración de producción, solo se sirve una solicitud por subproceso (o greenlet). Solo uno RequestContextes empujado, entonces solo uno AppContextes empujado. Pero si el modo de depuración está activado y falla una solicitud, Flask guarda el contexto , por lo que puede usarse con el depurador . Nonese adjunta a _app_ctx_stack, por lo que cuando la solicitud se está desglosando, sabe que aún no debe aparecer AppContext. Lo mismo ocurre con el cliente de prueba, que retiene el contexto, por lo que puede ser inspeccionado.
theY4Kman
Por lo tanto, el alcance de g es por solicitud (hilo) y no retendrá el valor en la solicitud posterior.
variable
83

Como una adición a la información en este hilo: también me ha confundido un poco el comportamiento flask.g, pero algunas pruebas rápidas me han ayudado a aclararlo. Esto es lo que probé:

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

Y aquí está la salida que da:

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 dijo el Y4Kman arriba, "Cada solicitud empuja un nuevo contexto de aplicación". Y como dicen los documentos de Flask , el contexto de la aplicación "no se compartirá entre las solicitudes". Ahora, lo que no se ha declarado explícitamente (aunque supongo que está implícito en estas declaraciones), y lo que mis pruebas muestran claramente, es que nunca debes crear explícitamente múltiples contextos de solicitud anidados dentro de un contexto de aplicación, porque flask.g(y co) no No tiene ninguna magia por la que funcione en los dos diferentes "niveles" de contexto, con diferentes estados existentes independientemente en los niveles de solicitud y solicitud.

La realidad es que "contexto de aplicación" es potencialmente un nombre bastante engañoso, porque app.app_context() es un contexto por solicitud , exactamente igual al "contexto de solicitud" . Piense en ello como un "contexto de solicitud lite", solo requerido en el caso de que necesite algunas de las variables que normalmente requieren un contexto de solicitud, pero no necesita acceso a ningún objeto de solicitud (por ejemplo, cuando se ejecutan operaciones de base de datos por lotes en un script de shell). Si intenta extender el contexto de la aplicación para abarcar más de un contexto de solicitud, está buscando problemas. Entonces, en lugar de mi prueba anterior, debería escribir código como este con los contextos 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))

Lo que dará los 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
fuente
77
Votados por el último párrafo, los contextos del matraz son bastante confusos para ser entendidos al principio. Por el nombre, tiene la sensación de que el contexto de la solicitud es por solicitud y que el contexto de la aplicación existe incluso después de una solicitud o no se ve afectado por su vida útil.
simanacci