Структура приложения на Flask
Инициализация
Любое приложение на основе Flask должно создать экземпляр приложения. Веб-сервер будет передавать все запросы, принимаемые от клиентов, этому объекту через протокол, который называется Web Server Gateway Interface (WSGI). Экземпляр приложения – это объект класса Flask, который обычно создается так:
from flask import Flask
app = Flask(__name__)
Единственным обязательным аргументом конструктора класса Flask является имя главного модуля или пакета приложения. Для большинства
приложений на Python это имя хранится в переменной __name__
.
Маршруты и функции представлений
Клиенты, такие как веб-браузеры, отправляют запросы веб-серверу, который, в свою очередь, отправляет их экземпляру приложения на основе
Flask. Экземпляр приложения должен определить, какой код должен быть выполнен для обработки обращения к адресу URL в запросе, поэтому он
должен хранить отображение адресов URL в функции на языке Python. Ассоциацию между адресом URL и функцией называют маршрутом (route)
.
Проще всего определить маршрут в приложении на основе Flask с помощью декоратора app.route
, экспортируемого экземпляром приложения.
Этот декоратор регистрирует декорируемую функцию как маршрут. Ниже показан пример объявления маршрута с помощью декоратора:
@app.route('/')
def index():
return '<h1>Hello World!</h1>'
В предыдущем примере функция index()
регистрируется как обработчик запросов к корневому адресу URL приложения. Если бы это приложение
было развернуто на сервере с доменным именем www.example.com, тогда попытка открыть страницу с адресом http://www.example.com в браузере
привела бы к вызову index()
на сервере. Возвращаемое значение этой функции называется ответом (response)
– это то, что получит
клиент.
Если клиентом является веб-браузер, ответ будет интерпретироваться как документ, который требуется отобразить на экране.
Функции, такие как index()
, называют функциями представления (view functions)
. Ответ, возвращаемый функцией представления, может
быть простой строкой с разметкой HTML или иметь более сложную форму, как будет показано позднее.
Если вы внимательно рассмотрите структуру некоторых URL-служб, которыми пользуетесь ежедневно, вы заметите, что многие из них имеют переменные разделы . Например, URL к странице профиля на Facebook имеет вид: **http://www.facebook.com/<имя-пользователя>**, то есть имя пользователя является частью URL. Flask поддерживает подобные адреса URL с помощью специального синтаксиса в декораторе маршрута. Например, ниже определяется маршрут, включающий переменный компонент `name`:имя-пользователя>
@app.route('/user/<name>')
def user(name):
return '<h1>Hello, %s!</h1>' % name
Фрагмент в угловых скобках – это переменная часть, то есть любой URL, совпадающий с постоянной частью, будет отображен в этот маршрут. При вызове функции представления Flask передаст ей динамический компонент в виде аргумента. В предыдущем примере аргумент name используется функцией представления для создания персонализированного ответа.
Переменные элементы в маршрутах по умолчанию интерпретируются как строки, но могут также иметь другие типы. Например, маршрут
/user/<int:id>
будет соответствовать адресам URL, содержащим целое число в переменном сегменте id. Flask поддерживает в маршрутах
типы int, float и path. Тип path также является строковым, но при его интерпретации символы косой черты не рассматриваются как
разделители и включаются в переменный компонент.
Запуск сервера
Экземпляр приложения имеет метод run
, который запускает интегрированный веб-сервер, используемый для разработки:
if __name__ == '__main__':
app.run(debug=True)
Идиома __name__ == '__main__'
, широко используемая в языке Python, гарантирует, что веб-сервер для разработки будет запускаться только
при непосредственном выполнении сценария. Когда сценарий импортируется другим сценарием, предполагается, что родительский сценарий
запустит другой сервер, поэтому вызов app.run() пропускается.
После запуска сервер входит в цикл ожидания и обработки запросов. Этот цикл продолжается, пока приложение не будет остановлено, например нажатием комбинации Ctrl-C.
Контексты приложения и запроса
Когда фреймворк Flask принимает запрос от клиента, он должен обеспечить доступ к нескольким объектам из функции представления, которая будет обрабатывать запрос. Хорошим примером может служить объект запроса, содержащий HTTP-запрос, отправленный клиентом.
Очевидный способ обеспечить доступ к объекту запроса – передать его в виде аргумента, но для этого потребовалось бы, чтобы каждая функция представления в приложении принимала дополнительный аргумент. Ситуация становится еще более сложной, если принять во внимание, что объект запроса – не единственный объект, доступ к которому должна иметь функция представления, чтобы суметь обработать запрос.
Чтобы избежать захламления функций представления большим числом параметров, которые могут потребоваться или нет, и временно обеспечить глобальный доступ к отдельным объектам, Flask использует контексты. Благодаря контекстам имеется возможность писать функции представления, такие как ниже:
from flask import request
@app.route('/')
def index():
user_agent = request.headers.get('User-Agent')
return '<p>Your browser is %s</p>' % user_agent
Обработчики событий жизненного цикла
Иногда бывает желательно выполнить некоторые операции перед обработкой каждого запроса. Например, перед обработкой каждого запроса может быть необходимо создать соединение с базой данных или аутентифицировать пользователя, выполнившего запрос. Вместо дублирования кода в начале каждой функции представления Flask дает возможность зарегистрировать функции для вызова до или после передачи запроса функции представления.
Регистрация обработчиков событий жизненного цикла выполняется с помощью декораторов. Ниже перечислены четыре декоратора, поддерживаемых фреймворком Flask:
before_first_request
: регистрирует функцию для вызова перед обработкой первого запроса;before_request
: регистрирует функцию для вызова перед обработкой каждого запроса;after_request
: регистрирует функцию для вызова после обработки каждого запроса, если не возникло необработанных исключений;teardown_request
: регистрирует функцию для вызова после обработки каждого запроса, если возникло необработанное исключение.
Ответы
Вызывая функцию представления, фреймворк Flask ожидает, что она вернет ответ на запрос. В большинстве случаев ответ – это простая строка, отправляемая обратно клиенту в виде HTML-страницы.
Но протокол HTTP требует, чтобы в ответ на запрос возвращалась не только строка. Очень важной частью HTTP-ответа является код состояния. По умолчанию Flask устанавливает код состояния равным 200, который указывает, что запрос был обработан благополучно.
Когда необходимо вернуть иной код состояния, функция представления может установить его во втором возвращаемом значении, после строки ответа. Например, следующая функция представления возвращает код состояния 400, соответствующий недопустимому запросу:
@app.route('/')
def index():
return '<h1>Bad Request</h1>', 400
Вместо кортежа с одним, двумя или тремя значениями функции представления могут возвращать объект Response. Функция make_response() принимает один, два или три аргумента – те же значения, которые может вернуть функция представления, – и возвращает объект Response. Иногда полезно выполнять данное преобразование внут ри функции представления и затем использовать методы объекта ответа для дальнейшей его настройки. Следующий пример создает объект ответа и затем устанавливает в нем cookie:
from flask import make_response
@app.route('/')
def index():
response = make_response('<h1>This document carries a cookie!</h1>')
response.set_cookie('answer', '42')
return response
Существует специальный тип ответа, который называется переадресацией (redirect). Этот ответ не включает документ страницы; он просто определяет новый адрес URL для браузера, откуда следует загрузить новую страницу.
Обычно ответ-переадресация включает код состояния 302 и адрес URL в заголовке Location
. Такой ответ можно сформировать, вернув из
функции представления три значения или объект Response
, но из-за того, что необходимость переадресации возникает достаточно часто,
в состав фреймворка Flask была включена вспомогательная функция redirect()
, создающая такой ответ:
from flask import redirect
@app.route('/')
def index():
return redirect('http://www.example.com')
Другой специальный ответ можно сформировать с помощью функции abort
, используемой для обработки ошибок. Следующий пример возвращает
код состояния 404, если динамический аргумент id
, переданный в URL, не представляет известного пользователя:
from flask import abort
@app.route('/user/<id>')
def get_user(id):
user = load_user(id)
if not user:
abort(404)
return '<h1>Hello, %s</h1>' % user.name
Расширения Flask
Фреймворк Flask изначально проектировался как расширяемый. В него преднамеренно не включались такие важные функциональные особенности, как поддержка баз данных и аутентификации пользователей, оставляя за вами свободу выбирать пакеты, лучше соответствующие вашим потребностям, или писать свои.
Существует огромное разнообразие расширений для самых разных целей, созданных сообществом, а если их окажется недостаточно, можно использовать любые стандартные пакеты или библиотеки для Python.
Поддержка параметров командной строки с помощью Flask-Script
Flask-Script – это расширение для Flask, реализующее парсер команд ной строки. Оно включает поддержку ряда универсальных параметров,
а также нескольких нестандартных команд. Установить расширение можно с помощью утилиты pip
:
(venv) $ pip install flask-script
В примере ниже демонстрируются изменения, необходимые, чтобы добавить анализ командной строки в приложение:
from flask.ext.script import Manager
manager = Manager(app)
# ...
if __name__ == '__main__':
manager.run()
Метод инициализации этого расширения является типичным для большинства расширений: экземпляр главного класса инициализируется передачей
экземпляра приложения конструктору. Затем созданный объект используется там, где необходимы функциональные возможности расширения.
В данном случае для запуска сервера используется метод manager.run()
объекта расширения, поддерживающего парсинг командной строки.
Вопросы для самопроверки
- Как создается экземпляр приложения Flask?
- Как зарегестрировать функцию в качестве обработчика пути?
- Перечислите типы, которые поддерживаются для переменных элементов в маршрутах.
- Назовите переменную, которая используется в качестве объекта запроса.
- С помощью какого метода можно создать объект ответа?
Задания
- Напишите функцию для обработки динамического маршрута, которая будет возвращать переменный параметр маршрута.
- Измените функцию так, чтобы ответ создавался с помощью встроенной функции Flask.
- Получите информацию о браузере пользователя из объекта запроса и верните ее в качестве ответа.