Представьте себе простую структуру веб-приложения Flask, которая выглядит следующим образом:
/yourapplication yourapplication.py /static style.css /templates layout.html index.html login.html ...
Хотя это нормально для небольших приложений, но для больших приложений рекомендуется использовать пакет Python вместо модуля.
Чтобы преобразовать это приложение-модуль в более крупный веб-проект на Flask, необходимо создать новую папку для приложения, внутри существующей и все переместить в неё. Затем переименовать файл yourapplication.py
в __init__.py
. Обязательно сначала удалите все файлы .pyc
, иначе все сломается.
Должно получиться что-то вроде этого:
/yourapplication /yourapplication __init__.py /static style.css /templates layout.html index.html login.html ...
Как теперь запустить приложение? Если запустить python3 yourapplication/__init__.py
, то работать ничего не будет. Скажем так, Python не хочет, чтобы модули пакета запускались как обычные Python файлы. Но это не проблема, добавим новый файл с именем setup.py
рядом с внутренней папкой приложения со следующим содержимым:
# файл с именем `setup.py` from setuptools import setup setup( name='yourapplication', packages=['yourapplication'], include_package_data=True, install_requires=[ 'flask', ], )
С версии Flask 2.3.0 рекомендуется использовать современные метаданные упаковки с pyproject.toml
вместо setup.py
:
[project] name = "yourapplication" dependencies = [ "flask", ] [build-system] requires = ["setuptools"] build-backend = "setuptools.build_meta"
Теперь перейдем в папку с приложением и запустим его командой:
$ cd yourapplication ~/yourapplication$ flask --app yourapplication run
Переменная среды
FLASK_ENV
и атрибутapp.env
устарели с версии Flask 2.2.0 и удалены с версии Flask 2.3.0, что устраняет различие между режимами разработки и отладки. Режим отладки следует контролировать напрямую с помощью параметра CLI--debug
илиapp.run(debug=True)
.
Через командную строку так же можно включить функции разработки, добавив параметра --debug
:
$ cd yourapplication ~/yourapplication$ flask --app yourapplication run --debug
Чтобы установить и запустить приложение, необходимо выполнить следующие команды:
$ pip install -e . $ flask run
Что мы от этого выиграли? Теперь можно реструктурировать приложение на несколько модулей. Единственное, что надо запомнить, это следующий контрольный список:
__init__.py
. Таким образом, каждый модуль может безопасно импортировать его, а переменная __name__
будет преобразована в правильный пакет.@app.route()
наверху) должны быть импортированы в файл __init__.py
. Не сам объект функции, а модуль, в котором они находятся. Модуль с функциями-представлениями импортируется после создания объекта приложения app
.__init__.py
:Файл пакета __init__.py
выполняется только один раз, во время запуска приложения Flask. В этом файле можно задать конфигурацию Flask, подключить и настроить расширения, определить функции пользовательских страниц ошибок и т.д.. Здесь также регистрируются схемы приложения blueprint
. Так как файл выполняется только один раз, то здесь например можно инициализировать базу данных (если она не существует).
# файл пакета приложения `__init__.py` from flask import Flask, render_template # создаем приложение Flask app = Flask(__name__) # добавляем параметры конфигурации приложения Flask app.config["параметр"] = значение # далее можно подключить и настроить какие-то расширения, # например, подключим кэширование страниц `flask-caching` from flask_caching import Cache cache = Cache(app, config={'CACHE_TYPE':..., 'CACHE_DIR':..., ...}) # можно переопределить стандартные страницы ошибок, # которые будут вызываться функцией `abort(N_ошибки)` @app.errorhandler(404) def page_not_found(e): return render_template('404.html'), 404 # можно определить функции, которые будут # выполнять какие-то действия до и после запроса @app.before_request def db_connect(): # соединяемся с БД ... @app.after_request def add_header(resp): # добавляем какие-то заголовки resp.headers["Cache-Control"] = "max-age=3600" ... return resp @app.teardown_appcontext def close_connection(exception): # закрываем соединения с базой ... # в нижней части файла `__init__.py` # импортируем функции-представления from yourapplication.views import view_1, view_2, view_3 # если пишите очень крупное приложения на схемах `blueprint`, то вместо # импорта функций-представлений регистрируем схемы. Например, файл # схемы с именем `simple_blueprint.py` регистрируются следующим образом: # app.register_blueprint(simple_blueprint)
А вот как будет выглядеть один из модулей с функциями-представлениями, например view_1.py
(это грубый пример):
from flask import render_template, abort from yourapplication import app, cache @app.route('/') # кэшируем на 1 час @cache.cached(timeout=60*60) def index(): text = 'Hello World!' return render_template('index.html', content=text)
Теперь структура проекта будет выглядеть как-то так:
/yourapplication pyproject.toml /yourapplication __init__.py /views view_1.py view_2.py view_3.py /static style.css /templates layout.html index.html 404.html ...
Циклический импорт, это когда два модуля зависят друг от друга, любой программист Python ненавидит такое поведение, но он здесь присутствует. В разбираемом случае представления view_*.py
зависят от __init__.py
. Имейте в виду, что в целом - это плохая идея, но в данном случае это нормально. Причина в том, что приложение не использует функции-представления в __init__.py
, а просто обеспечивает импорт модуля и делает это в нижней части файла.
С таким подходом все еще есть некоторые проблемы, но если не использовать декораторы @app.route()
в __init__.py
, то их нет.
blueprint
.Если пишите еще более крупные приложения, то рекомендуется разделить их на более мелкие группы, где каждая группа реализуется с помощью схемы blueprint
. Для введения в эту тему обратитесь к разделу "Модульные приложения на схемах blueprint
во Flask".