El contexto de la solicitud

El contexto de la solicitud mantiene un seguimiento de los datos a nivel de petición durante una solicitud. En lugar de pasar el objeto de solicitud a cada función que se ejecuta durante una solicitud, se accede a los proxies request y session.

Esto es similar a El contexto de la aplicación, que mantiene un seguimiento de los datos a nivel de aplicación independiente de una solicitud. Un contexto de aplicación correspondiente es empujado cuando un contexto de solicitud es empujado.

Objetivo del Contexto

Cuando la aplicación Flask maneja una petición, crea un objeto Request basado en el entorno que recibió del servidor WSGI. Dado que un worker (hilo, proceso o coroutina, dependiendo del servidor) maneja sólo una petición a la vez, los datos de la petición pueden considerarse globales para ese worker durante esa petición. Flask utiliza el término contexto local para esto.

Flask automáticamente empuja un contexto de solicitud cuando maneja una solicitud. Las funciones de vista, los manejadores de errores y otras funciones que se ejecutan durante una solicitud tendrán acceso al proxy request, que apunta al objeto de solicitud para la solicitud actual.

La duración del contexto

Cuando una aplicación Flask comienza a manejar una solicitud, empuja un contexto de solicitud, que también empuja un contexto de aplicación. Cuando la solicitud finaliza, el contexto de la solicitud y el contexto de la aplicación aparecen.

El contexto es único para cada hilo (u otro tipo de trabajador). request no puede ser pasado a otro hilo, el otro hilo tiene un espacio de contexto diferente y no sabrá sobre la solicitud que el hilo padre estaba apuntando.

Los locales de contexto se implementan utilizando el contextvars de Python y el LocalProxy de Werkzeug. Python gestiona el tiempo de vida de las variables de contexto automáticamente, y el proxy local envuelve esa interfaz de bajo nivel para facilitar el trabajo con los datos.

Empujar manualmente un contexto

Si intentas acceder a request, o a cualquier cosa que lo utilice, fuera de un contexto de petición, obtendrás este mensaje de error:

RuntimeError: Working outside of request context.

This typically means that you attempted to use functionality that
needed an active HTTP request. Consult the documentation on testing
for information about how to avoid this problem.

Esto debería ocurrir típicamente sólo cuando se prueba el código que espera una solicitud activa. Una opción es utilizar el cliente de prueba para simular una petición completa. O puedes usar test_request_context() en un bloque with, y todo lo que se ejecute en el bloque tendrá acceso a request, poblado con tus datos de prueba.

def generate_report(year):
    format = request.args.get("format")
    ...

with app.test_request_context(
    "/make_report/2017", query_string={"format": "short"}
):
    generate_report()

Si ves ese error en algún otro lugar de tu código no relacionado con las pruebas, lo más probable es que indique que debes mover ese código a una función de la vista.

Para obtener información sobre cómo utilizar el contexto de petición desde el shell interactivo de Python, consulte Trabajar con el Shell.

Cómo funciona el contexto

El método Flask.wsgi_app() es llamado para manejar cada solicitud. Gestiona los contextos durante la solicitud. Internamente, los contextos de solicitud y aplicación funcionan como pilas. Cuando los contextos son empujados, los proxies que dependen de ellos están disponibles y apuntan a la información del elemento superior.

Cuando se inicia la solicitud, se crea y empuja un RequestContext, que crea y empuja un AppContext primero si un contexto para esa aplicación no es ya el contexto superior. Mientras estos contextos son enviados, los proxies current_app, g, request, y session están disponibles para el hilo original que maneja la solicitud.

Otros contextos pueden ser empujados para cambiar los proxies durante una solicitud. Aunque no es un patrón común, se puede utilizar en aplicaciones avanzadas para, por ejemplo, hacer redirecciones internas o encadenar diferentes aplicaciones.

Después de que la petición sea despachada y una respuesta sea generada y enviada, el contexto de la petición es poppeado, que luego poppea el contexto de la aplicación. Inmediatamente antes de que se vacíen, se ejecutan las funciones teardown_request() y teardown_appcontext(). Éstas se ejecutan incluso si se ha producido una excepción no controlada durante el envío.

Callbacks y errores

Flask despacha una petición en múltiples etapas que pueden afectar a la petición, a la respuesta y a cómo se manejan los errores. Los contextos están activos durante todas estas etapas.

Un Blueprint puede añadir manejadores para estos eventos que son específicos del blueprint. Los manejadores de un blueprint se ejecutarán si el blueprint posee la ruta que coincide con la solicitud.

  1. Antes de cada petición, se llaman las funciones before_request(). Si una de estas funciones devuelve un valor, se saltan las demás funciones. El valor devuelto se trata como la respuesta y no se llama a la función de vista.

  2. Si las funciones before_request() no devuelven una respuesta, se llama a la función de vista de la ruta coincidente y devuelve una respuesta.

  3. El valor de retorno de la vista se convierte en un objeto de respuesta real y se pasa a las funciones after_request(). Cada función devuelve un objeto de respuesta modificado o nuevo.

  4. Después de que la respuesta es devuelta, los contextos son vaciados, lo que llama a las funciones teardown_request() y teardown_appcontext(). Estas funciones son llamadas incluso si una excepción no manejada fue lanzada en cualquier punto anterior.

Si se lanza una excepción antes de las funciones de desmontaje, Flask intenta hacerla coincidir con una función errorhandler() para manejar la excepción y devolver una respuesta. Si no se encuentra ningún gestor de errores, o el propio gestor lanza una excepción, Flask devuelve una respuesta genérica 500 Internal Server Error. Las funciones de desmontaje siguen siendo llamadas, y se les pasa el objeto de excepción.

Si el modo de depuración está habilitado, las excepciones no manejadas no se convierten en una respuesta 500 y en su lugar se propagan al servidor WSGI. Esto permite al servidor de desarrollo presentar el depurador interactivo con el rastreo.

Teardown Callbacks

Las Teardown Callbacks son independientes del envío de la solicitud, y son llamadas por los contextos cuando son expulsados. Las funciones son llamadas incluso si hay una excepción no manejada durante el envío, y para los contextos empujados manualmente. Esto significa que no hay garantía de que cualquier otra parte del envío de la solicitud se haya ejecutado primero. Asegúrate de escribir estas funciones de forma que no dependan de otras llamadas de retorno y no fallen.

Durante las pruebas, puede ser útil aplazar la salida de los contextos después de que la solicitud termine, para que sus datos puedan ser accedidos en la función de prueba. Utiliza el test_client() como un bloque with para conservar los contextos hasta que el bloque with salga.

from flask import Flask, request

app = Flask(__name__)

@app.route('/')
def hello():
    print('during view')
    return 'Hello, World!'

@app.teardown_request
def show_teardown(exception):
    print('after with block')

with app.test_request_context():
    print('during with block')

# teardown functions are called after the context with block exits

with app.test_client() as client:
    client.get('/')
    # the contexts are not popped even though the request ended
    print(request.path)

# the contexts are popped and teardown functions are called after
# the client with block exits

Señales

The following signals are sent:

  1. request_started se envía antes de llamar a las funciones before_request().

  2. request_finished se envía después de llamar a las funciones after_request().

  3. got_request_exception se envía cuando se empieza a gestionar una excepción, pero antes de que se busque o llame a un errorhandler().

  4. request_tearing_down se envía después de llamar a las funciones teardown_request()..

Notas sobre los apoderados

Algunos de los objetos proporcionados por Flask son proxies de otros objetos. Los proxies se acceden de la misma manera para cada hilo de trabajo, pero apuntan al objeto único vinculado a cada trabajador detrás de las escenas como se describe en esta página.

La mayoría de las veces no tienes que preocuparte por esto, pero hay algunas excepciones en las que es bueno saber que este objeto es realmente un proxy:

  • Los objetos proxy no pueden fingir su tipo como los tipos de objetos reales. Si quieres realizar comprobaciones de instancia, tienes que hacerlo en el objeto proxy.

  • La referencia al objeto proxy es necesaria en algunas situaciones, como el envío de Señales o el paso de datos a un hilo de fondo.

Si necesita acceder al objeto subyacente que se proxy, utilice el método _get_current_object():

app = current_app._get_current_object()
my_signal.send(app)