Сообщить об ошибке.

Пример структуры приложения Flask как пакета Python

Создание больших веб-приложений на Flask

Представьте себе простую структуру веб-приложения 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

Что мы от этого выиграли? Теперь можно реструктурировать приложение на несколько модулей. Единственное, что надо запомнить, это следующий контрольный список:

  • Создание объекта приложения Flask должно быть в файле __init__.py. Таким образом, каждый модуль может безопасно импортировать его, а переменная __name__ будет преобразована в правильный пакет.
  • Все функции-представления (те, что с декоратором @app.route() наверху) должны быть импортированы в файл __init__.py. Не сам объект функции, а модуль, в котором они находятся. Модуль с функциями-представлениями импортируется после создания объекта приложения app.

Пример файла пакета приложения Flask __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".