#4 Контексты во Flask

Flask использует контексты, чтобы временно делать определенные глобальные доступными в глобальной области видимости.

Знакомые с Django могут обратить внимание на то, что функция представления во Flask не принимает request первым аргументом. Во Flask доступ к данным осуществляется с помощью входящего запроса, используя объект request:

from flask import Flask, request

@app.route('/')
def requestdata():
    return "Hello! Your IP is {} and you are using {}: ".format(request.remote_addr,  
                                                                request.user_agent)

Код выше может создать впечатление, что request — это глобальный объект, но на самом деле это не так. Если бы request был глобальным объектом, тогда в многопоточной программе приложение не смогло бы различать два одновременных процесса, поскольку программа такого типа распределяет все переменные по потокам. Во Flask используется то, что называется “Контекстами”. Они заставляют отдельные переменные вести себя как глобальные. Обращаясь к этим переменным, пользователь получает доступ к объекту в конкретном потоке. Технически такие переменные называются локальными или внутрипоточными.

Согласно документации Flask существует два вида контекстов:

  1. Контекст приложения
  2. Контекст запроса

Контекст приложения используется для хранения общих переменных приложения, таких как подключение к базе данных, настройки и т. д. А контекст запроса используется для хранения переменных конкретного запроса.

Контекст приложения предлагает такие объекты как current_app или g. current_app ссылается на экземпляр, который обрабатывает запрос, а g используется, чтобы временно хранить данные во время обработки запроса. Когда значение установлено, к нему можно получить доступ из любой функции представления. Данные в g сбрасываются после каждого запроса.

Как и контекст приложения, контекст запроса также предоставляет объекты: request и session. request содержит информацию о текущем запросе, а session — это словарь (dict). В нем хранятся значения, которые сохраняются между запросами.

Flask активирует контексты приложения и запроса, когда запрос получен и удаляет их, когда он обработан. Когда используется контекст приложения, все его переменные становятся доступным для потока. То же самое происходит и с контекстом запроса. Когда он активируется, его переменные могут быть использованы в потоке. Внутри функций представления можно получить доступ ко всем объектам контекстов приложения и запроса, так что не стоит волноваться о том, активны ли контексты или нет. Но если попробовать получить к ним доступ вне функции представления или в консоли Python, выйдет ошибка. Следующий пример демонстрирует ее:

>>> from flask import Flask, request, current_app
>>>
>>>  request.method # получаем метод запроса
Traceback (most recent call last):
#...
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.
>>>

request_method возвращает HTTP-метод, используемый в запросе, но поскольку самого HTTP-запроса нет, то и контекст запроса не активируется.

Похожая ошибка возникнет, если попытаться получить доступ к объекту, который предоставляется контекстом приложения.

>>> current_app.name # получим название приложения
Traceback (most recent call last):
#...
RuntimeError: Working outside of application context.

This typically means that you attempted to use functionality that needed to interface with  the current application object in a way. To solve 
this set  up an application context with app.app_context(). See the documentation for more information.
>>>

Чтобы получить доступ к объектам, предоставляемым контекстами приложения и запроса вне функции представления, нужно сперва создать соответствующий контекст.

Создать контекст приложения можно с помощью метода app_context() для экземпляра Flask.

>>> from main2 import app
>>> from flask import Flask, request, current_app
>>>
>>> app_context = app.app_context()
>>> app_context.push()
>>>
>>> current_app.name
'main2'

Предыдущий код можно упростить используя выражение with следующим образом:

>>> from main2 import app
>>> from flask import request, current_app
>>>
>>>
>>> with app.app_context():
...     current_app.name
...
'main2'
>>>

При создании контекстов лучше всего использовать выражение with.

Похожим образом можно создавать контекст запроса с помощью метода test_request_context() в экземпляре Flask. Важно запомнить, что когда активируется контекст запроса, контекст приложения создается, если его не было до этого. Следующий код демонстрирует процесс создания контекста запроса:

>>> from main2 import app
>>> from flask import request, current_app
>>>
>>>
>>> with app.test_request_context('/products'):
...     request.path  # получим полный путь к запрашиваемой странице(без домена).
...      request.method
...      current_app.name
...
'/products'
'GET'
'main2'
>>>

Адрес /products выбран произвольно.
Это все, что нужно знать о контекстах во Flask.