Hello World во фреймворке Flask
Начать знакомство с Flask можно с создания простого приложения, которое выводит “Hello World”. Создаем новый файл main.py
и вводим следующий код.
from flask import Flask
app = Flask(__name__)
@app.route('/')
def index():
return 'Hello World'
if __name__ == "__main__":
app.run()
Это приложение “Hello World”, созданное с помощью фреймворка Flask. Если код в main.py
не понятен, это нормально. В следующих разделах все будет разбираться подробно. Чтобы запустить main.py
, нужно ввести следующую команду в виртуальную среду Python.
(env) gvido@vm:~/flask_app$ python main.py
* Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)
Запуск файла main.py
запускает локальный сервер для разработки на порте 5000. Осталось открыть любимый браузер и зайти на https://127.0.0.1:5000/
, чтобы увидеть приложение Hello World в действии.
Остановить сервер можно с помощью комбинации CTRL+C.
Создание приложения Flask
У каждого Flask-приложения должен быть экземпляр класса. Экземпляр — это WSGI-приложение (WSGI – это интерфейс для взаимодействия сервера с фреймворком), которое показывает, что сервер передает все полученные запросы экземпляру для дальнейшей обработки. Объект класса Flask
создается следующим образом:
from flask import Flask
app = Flask(__name__)
В первой строке класс Flask
импортируется из пакета flask
.
Во второй строке создается объект Flask
. Для этого конструктору Flask
назначается аргумент __name__
. Конструктор Flask
должен иметь один обязательный аргумент. Им служит название пакета. В большинстве случаев значение __name__
подходит. Название пакета приложения используется фреймворком Flask, чтобы находить статические файлы, шаблоны и т. д.
Создание route (путей)
Маршрут (или путь) используется во фреймворке Flask для привязки URL к функции представления. Эта функция отвечает на запрос. Во Flask декоратор route
используется, чтобы связать URL адрес с функций. Вот как маршрут создается.
@app.route('/')
def index():
return 'Hello World'
Этот код назначает функцию index()
обработчиком корневого URL в приложении. Другими словами, каждый раз, когда приложение будет получать запрос, где путь — /
, вызывается функция index()
, и на этом запрос завершается.
Как вариант можно использовать метод add_url_rule()
вместо декоратора route
для маршрутизации. add_url_rule()
— это простой метод, а не декоратор. Помимо URL он принимает конечную точку и название функции представления. Конечная точка относится к уникальному имени маршрута. Обычно, название функции представления — это и есть конечная точка. Flask может генерировать URL из конечной точки, но об этом позже. Предыдущий код аналогичен следующему:
def index():
return 'Hello World'
app.add_url_rule('/', 'index', index)
Декоратор route
используется в большинстве случаев, но у add_url_rule()
есть свои преимущества.
Функция представления должна вернуть строку. Если пытаться вернуть что-то другое, сервер ответит ошибкой 500 Internal Sever Error.
Можно создать столько столько, сколько нужно приложению. Например, в следующем списке 3 пути.
@app.route('/')
def index():
return 'Home Page'
@app.route('/career/')
def career():
return 'Career Page'
@app.route('/feedback/')
def feedback():
return 'Feedback Page'
Когда URL в маршруте заканчивается завершающим слешем (/
), Flask перенаправляет запрос без слеша на URL со слешем. Так, запрос к /career
будет перенаправлен на /career/
.
Для одной функции представления может быть использовано несколько URL. Например:
@app.route('/contact/')
@app.route('/feedback/')
def feedback():
return 'Feedback Page'
В этом случае в ответ на запросы /contact/
или /feedback/
, будет вызвана функция feedback()
.
Если перейти по адресу, для которого нет соответствующей функции представления, появится ошибка 404 Not Found.
Эти маршруты статичны. Большая часть современных приложений имеют динамичные URL. Динамичный URL – это адрес, который состоит из одной или нескольких изменяемых частей, влияющих на вывод страницы. Например, при создании веб-приложения со страницами профилей, у каждого пользователя будет уникальный id. Профиль первого пользователя будет на странице /user/1
, второго — на /user/2
и так далее. Очень неудобный способ добиться такого результата — создавать маршруты для каждого пользователя отдельно.
Вместе этого можно отметить динамические части URL как <variable_name>
(переменные). Эти части потом будут передавать ключевые слова функции отображения. Следующий код демонстрирует путь с динамическим элементом.
@app.route('/user/<id>/')
def user_profile(id):
return "Profile page of user #{}".format(id)
В этом примере на месте <id>
будет указываться часть URI, которая идет после /user/
. Например, если зайти на /user/100/
, ответ будет следующим.
Profile page of user #100
Этот элемент не ограничен числовыми id. Адрес может быть /user/cowboy/
, /user/foobar10/
, /user/@@##/
и так далее. Но он не будет работать со следующими URI: /user/
, /user/12/post/
. Можно ограничить маршрут, чтобы он работал только с числовыми id после /user/
. Это делается с помощью конвертера.
По умолчанию динамические части URL передаются в функцию в виде строк. Это можно изменить с помощью конвертера, который указывается перед динамическими элементами URL с помощью <converter:variable_name>
. Например, /user/<int:id>/
будет работать с адресами /user/1/
, /user/200/
и другими. Но /user/cowboy/
, /user/foobar10/
и /user/@@##/
не подойдут.
В этом списке все конвертеры, доступные во Flask:
Конвертер | Описание |
---|---|
string | принимает любые строки (значение по умолчанию). |
int | принимает целые числа. |
float | принимает числа с плавающей точкой. |
path | принимает полный путь включая слеши и завершающий слеш. |
uuid | принимает строки uuid (символьные id). |
Запуск сервера
Для запуска сервера разработки нужно использовать метод run()
объекта Flask
.
if __name__ == "__main__":
app.run()
Условие __name__ == "__main__"
гарантирует, что метод run()
будет вызван только в том случае, если main.py
будет запущен, как основная программа. Если попытаться использовать метод run()
при импорте main.py
в другой модуль Python, он не вызовется.
Важно: сервер разработки Flask используется исключительно для тестирования, поэтому его производительность невысокая.
Теперь должно быть понятнее, как работает main.py
.
Режим отладки (Debug)
Баги неизбежны в программировании. Поэтому так важно знать, как находить ошибки в программе и исправлять их. Во Flask есть мощный интерактивный отладчик, который по умолчанию отключен. Когда он выключен, а в программе обнаруживается ошибка, функция показывает 500 Internal Sever Error. Чтобы увидеть это поведение наглядно, можно специально добавить баг в файл main.py
.
from flask import Flask
app = Flask(__name__)
@app.route('/')
def index():
print(i)
return 'Hello World'
if __name__ == "__main__":
app.run()
В этом случае программа пытается вывести значение неопределенной переменной i, что приводит к ошибке. Если открыть https://127.0.0.1:5000/
, то появится ошибка 500 Internal Sever Error:
Тем не менее сам браузер не сообщает о типе ошибки. Если посмотреть в консоль, то можно увидеть отчет об ошибке. В данном случае он выглядит вот так:
File "/home/gvido/flask_app/env/lib/python3.5/site-packages/flask/app.py", line 1612, in full_dispatch_request
rv = self.dispatch_request()
File "/home/gvido/flask_app/env/lib/python3.5/site-packages/flask/app.py", line 1598, in dispatch_request
return self.view_functions[rule.endpoint](**req.view_args)
File "main.py", line 13, in index
print(i)
NameError: name 'i' is not defined
Когда режим отладки выключен, после изменения кода нужно каждый раз вручную запускать сервер, чтобы увидеть изменения. Но режим отладки будет перезапускать его после любых изменений в коде.
Чтобы включить режим, нужно передать аргумент debug=True
методу run()
:
if __name__ == "__main__":
app.run(debug=True)
Еще один способ — указать значение True
для атрибута debug
.
from flask import Flask
app = Flask(__name__)
app.debug = True
После обновления файл main.py
следующим образом его можно запускать.
from flask import Flask
app = Flask(__name__)
@app.route('/')
def index():
print(i)
return 'Hello World'
if __name__ == "__main__":
app.run(debug=True) # add debug mode
Теперь при открытии https://127.0.0.1:5000/
на странице будет отладчик.
Теперь, когда отладчик включен, вместо ошибки 500 Internal Server на странице будет отображаться отчет об ошибке. Он в полной мере описывает, какая ошибка случилась. Внизу страницы видно, что оператор print
пытался вывести значение неопределенной переменной i
. Там же указан тип ошибки, NameError
, что подтверждает то, что ошибка заключается в том, что имя i
не определено.
Кликнув на строчку кода на странице вывода ошибки, можно получить исходный код, где эта ошибка обнаружена, а также предыдущие и следующие строчки. Это помогает сразу понять контекст ошибки.
При наведении на строчку кода отображается иконка терминала. Нажав на нее, откроется консоль, где можно ввести любой код Python.
В ней можно проверить локальные переменные.
Если консоль открывается первый раз, то нужно ввести PIN-код.
Это мера безопасности, призванная ограничить доступ к консоли неавторизованным пользователям. Посмотреть код можно в консоли при запуске сервера. Он будет указан в начале вывода.
Завершить урок стоит созданием еще одного приложения Flask с применением всех имеющихся знаний.
Создаем еще один файл main2.py
со следующим кодом:
from flask import Flask
app = Flask(__name__)
@app.route('/')
def index():
return 'Hello Flask'
@app.route('/user/<int:user_id>/')
def user_profile(user_id):
return "Profile page of user #{}".format(user_id)
@app.route('/books/<genre>/')
def books(genre):
return "All Books in {} category".format(genre)
if __name__ == "__main__":
app.run(debug=True)
Если запустить файл и зайти на https://127.0.0.1:5000/
, браузер поприветствует выражением «Hello Flask»:
В этой новой версии приложения есть два динамических пути. Если в браузере ввести https://127.0.0.1:5000/user/123/
, откроется страница со следующим содержанием:
Стоит заметить, что путь /user/<int:user_id>/
будет работать только с теми URL, где динамическая часть (user_id
) представлена числом.
Чтобы проверить второй динамический путь, нужно открыть https://127.0.0.1:5000/books/sci-fi/
. В этот раз получится следующее:
Если сейчас попробовать открыть URL, который не определен в путях, выйдет ошибка 404 Not Found. Например, такой ответ получите при попытке перейти на https://127.0.0.1:5000/products
.
Как Flask обрабатывает запрос?
Откуда Flask знает, какую функцию выводить, когда он получает запрос от клиента?
Flask сопоставляет URL и функции отображения, которые будут выводиться. Определение соответствий (маршрутизация) создается с помощью декоратора route
или метода add_url_rule()
в экземпляре Flask. Получить доступ к этим соответствиям можно с помощью атрибута url_map
у экземпляра Flask
.
>>>
>>> from main2 import app
>>> app.url_map
Map([<Rule '/' (OPTIONS, GET, HEAD) -> index>,
<Rule '/static/<filename>' (OPTIONS, GET, HEAD) -> static>,
<Rule '/books/<genre>' (OPTIONS, GET, HEAD) -> books>,
<Rule '/user/<user_id>' (OPTIONS, GET, HEAD) -> user_profile>])
>>>
Как видно, есть 4 правила. Flask определяет соответствия URL в следующем формате:
url pattern, (comma separated list of HTTP methods handled by the route) -> view function to execute
Путь /static/<filename>
автоматически добавляется для статических файлов Flask. О работе со статическими файлами речь пойдет в отдельном уроке «Обслуживание статических файлов во Flask».