Контекст приложения Flask¶
Одно из проектных решений Flask заключается в том, что есть два разных “состояния”, в которых выполняется код. Состояние настройки приложения, которое подразумевается на уровне модуля. Оно наступает в момент, когда создаётся экземпляр объекта Flask , и заканчивается, когда поступает первый запрос. Пока приложение находится в этом состоянии, верно следующее:
- программист может безопасно менять объект приложения.
- запросы ещё не обрабатывались.
- у вас имеется ссылка на объект приложения, чтобы изменить его, нет необходимости пользоваться каким-либо посредником для того, чтобы получить ссылку на созданный или изменяемый объект приложения.
Напротив, во время обработки запроса верны следующие правила:
- пока активен запрос, объекты локального контекста ( flask.request и другие) указывают на текущий запрос.
- любой код может в любое время может заполучить эти объекты.
Есть и третье состояние, которое располагается между ними. Иногда можно работать с приложением так же, как и во время обработки запроса, просто в этот момент нет активного запроса. Например, вы можете работать с интерактивной оболочкой Python и взаимодействовать с приложением или запустить приложение из командной строки.
Контекст приложения — это то, чем управляет локальный контекст current_app .
Назначение контекста приложения¶
Основная причина существования контекста приложений состоит в том, что в прошлом большая доля функциональности была привязана к контексту запроса за неимением лучшего решения. Тогда одной из целей, учитываемых при проектировании Flask, было обеспечение возможности иметь несколько приложений в рамках одного процесса Python.
Каким образом код находит “правильное” приложение? В прошлом мы рекомендовали явную передачу приложений, но появились проблемы с библиотеками, которые не были спроектированы с учётом этого.
Обходной путь решения этой проблемы заключался в том, чтобы использовать посредника current_app , привязанного ссылкой к текущему запросу приложения. Но поскольку создание такого контекста запроса является не оправданно дорогостоящим в случае отсутствия запроса, был введён контекст приложения.
Создание контекста приложения¶
Для создания контекста приложения есть два способа. Первый из них — неявный: когда поступает контекст запроса, при необходимости также создаётся и контекст приложения. В результате вы можете игнорировать существование контекста приложения до тех пор, пока он вам не понадобится.
Второй способ — это явное создание контекста при помощи метода app_context() :
from flask import Flask, current_app app = Flask(__name__) with app.app_context(): # within this block, current_app points to app. print current_app.name
Контекст приложения также используется функцией url_for() в случае, если было настроено значение параметра конфигурации SERVER_NAME . Это позволяет вам генерировать URL’ы даже при отсутствии запроса.
Локальность контекста¶
Контекст приложения создаётся и уничтожается при необходимости. Он никогда не перемещается между потоками и не является общим для разных запросов. Поэтому — это идеальное место для хранения информации о подключении к базе данных и т.п. Внутренний объект стека называется flask._app_ctx_stack . Расширения могут хранить дополнительную информацию на самом верхнем уровне, если предполагается, что они выбрали достаточно уникальное имя и если они помещают свою информацию сюда, а не в объект flask.g , который зарезервирован для пользовательского кода.
За дополнительной информацией по теме обратитесь к разделу extension-dev .
Использование контекста¶
Обычно контекст используют для кэширования в нём ресурсов, которые необходимо создавать в случае отдельных запросов или для постоянного использования. Например, соединения к базе данных предназначены для хранения именно там. Для сохранения чего-либо в контексте приложения необходимо выбирать уникальные имена, так как это то место, которое является общим для приложений и расширений Flask.
Наиболее распространённым использованием является разделение управления ресурсами на две части:
- неявное кэширование ресурсов в контексте.
- освобождение ресурса, основанное на демонтировании контекста.
В обычном случае, должна присутствовать функция get_X() , которая создаёт ресурс X , если он ещё не существует, и, если это не так, возвращает тот же самый ресурс, а также функция teardown_X() , которая регистрирует обработчик демонтирования.
Вот пример соединения с базой данных:
import sqlite3 from flask import g def get_db(): db = getattr(g, '_database', None) if db is None: db = g._database = connect_to_database() return db @app.teardown_appcontext def teardown_db(exception): db = getattr(g, '_database', None) if db is not None: db.close()
Соединение будет установлено, когда get_db() вызывается в первый раз. Для того, чтобы сделать это неявно, можно использовать класс LocalProxy :
from werkzeug.local import LocalProxy db = LocalProxy(get_db)
С использованием этого способа пользователь может иметь прямой доступ к db , которая сама внутри себя вызовет get_db() .
#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 существует два вида контекстов:
Контекст приложения используется для хранения общих переменных приложения, таких как подключение к базе данных, настройки и т. д. А контекст запроса используется для хранения переменных конкретного запроса.
Контекст приложения предлагает такие объекты как 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.