Шаг 01. Структура и настройка
⚡ Кратко: что делаем на этом шаге
Цель: Создать venv, установить зависимости, описать структуру директорий проекта, написать application factory (create_app()) и config-классы без устаревшего FLASK_ENV.
- Файлы:
config.py,app/__init__.py,run.py,requirements.txt - Команды:
python -m venv venv,pip install flask flask-sqlalchemy flask-migrate pydantic - Результат: приложение стартует командой
python run.py, отвечает 200 наGET /health
🎯 Цель этапа
На этом шаге мы закладываем фундамент проекта: правильную структуру директорий,
виртуальное окружение, зависимости и application factory с config-классами.
Закрываем callout-verify из урока 09: убираем устаревший FLASK_ENV
и показываем правильный подход к конфигурации во Flask 3.x.
app = Flask(__name__)),
мы оборачиваем его в функцию create_app(). Это позволяет создавать несколько
экземпляров с разными конфигурациями (dev / test / prod) и правильно тестировать приложение.
После этого шага у нас будет
- Виртуальное окружение и
requirements.txtс закреплёнными версиями - Структура директорий:
app/,app/models/,app/schemas/,app/api/ - Три config-класса:
DevelopmentConfig,TestingConfig,ProductionConfig - Application factory
create_app()вapp/__init__.py - Точка входа
run.py - Health-check эндпоинт
GET /health
📄 Затрагиваемые файлы
| Файл | Действие | Описание |
|---|---|---|
requirements.txt | Создать | Список зависимостей с версиями |
config.py | Создать | Config-классы для dev/test/prod |
app/__init__.py | Создать | Application factory create_app() |
app/models/__init__.py | Создать | Инициализация пакета models (пустой) |
app/schemas/__init__.py | Создать | Инициализация пакета schemas (пустой) |
app/api/__init__.py | Создать | Инициализация пакета api (пустой) |
run.py | Создать | Точка входа: запуск Flask dev-сервера |
🔨 Шаги
1. Создаём директорию и виртуальное окружение
# Создаём папку проекта (выберите удобное место)
mkdir community_pulse
cd community_pulse
# Создаём виртуальное окружение
python -m venv venv
# Активируем (Windows PowerShell)
venv\Scripts\Activate.ps1
# Проверяем: в начале строки должно появиться (venv)
python --version
2. Устанавливаем зависимости
pip install flask==3.1.0 flask-sqlalchemy==3.1.1 flask-migrate==4.0.7 pydantic==2.8.2 python-dotenv==1.0.1
# requirements.txt
flask==3.1.0
flask-sqlalchemy==3.1.1
flask-migrate==4.0.7
pydantic==2.8.2
python-dotenv==1.0.1
==), а не диапазоны.
Это гарантирует воспроизводимость: коллега установит ровно то, что вы тестировали.
3. Создаём структуру директорий
# Создаём пакеты (папки с __init__.py)
mkdir app
mkdir app\models
mkdir app\schemas
mkdir app\api
# Создаём пустые __init__.py
type nul > app\models\__init__.py
type nul > app\schemas\__init__.py
type nul > app\api\__init__.py
Итоговая структура проекта:
community_pulse/
├── venv/ # виртуальное окружение (в git не попадает)
├── app/
│ ├── __init__.py # application factory
│ ├── models/
│ │ └── __init__.py # (пока пустой, модели — шаг 03)
│ ├── schemas/
│ │ └── __init__.py # (пока пустой, схемы — шаг 04)
│ └── api/
│ └── __init__.py # (пока пустой, blueprints — шаг 05)
├── migrations/ # создаётся flask-migrate на шаге 02
├── config.py # конфигурация
├── requirements.txt # зависимости
├── run.py # точка входа
└── .gitignore
4. Пишем конфигурацию
# config.py
import os
from dotenv import load_dotenv
load_dotenv() # читает .env если он есть
class Config:
"""Базовая конфигурация — общие настройки для всех окружений."""
SECRET_KEY: str = os.environ.get("SECRET_KEY", "dev-secret-key-change-in-prod")
SQLALCHEMY_TRACK_MODIFICATIONS: bool = False
# DEBUG и TESTING НЕ ставим здесь — их переопределяют дочерние классы
class DevelopmentConfig(Config):
"""Конфигурация для разработки."""
DEBUG: bool = True
SQLALCHEMY_DATABASE_URI: str = os.environ.get(
"DEV_DATABASE_URL",
"sqlite:///community_pulse_dev.db"
)
class TestingConfig(Config):
"""Конфигурация для тестов — БД в памяти, не пишет на диск."""
TESTING: bool = True
SQLALCHEMY_DATABASE_URI: str = "sqlite:///:memory:"
# Отключаем CSRF и другие защиты для удобства тестирования
WTF_CSRF_ENABLED: bool = False
class ProductionConfig(Config):
"""Конфигурация для продакшна — DATABASE_URL обязателен."""
DEBUG: bool = False
SQLALCHEMY_DATABASE_URI: str = os.environ.get("DATABASE_URL", "")
def __init__(self) -> None:
if not self.SQLALCHEMY_DATABASE_URI:
raise RuntimeError("DATABASE_URL environment variable is not set")
# Словарь для выбора конфига по строке
config_map: dict[str, type[Config]] = {
"development": DevelopmentConfig,
"testing": TestingConfig,
"production": ProductionConfig,
}
FLASK_ENV убрана (deprecated в 2.2, удалена в 3.0).
Правильный подход — управлять окружением через собственную переменную (APP_ENV)
и передавать нужный конфиг-класс в create_app().
Это закрывает callout-verify из урока 09.
5. Создаём application factory
# app/__init__.py
from flask import Flask, jsonify
from flask_sqlalchemy import SQLAlchemy
from flask_migrate import Migrate
from config import config_map, Config
# Создаём расширения БЕЗ привязки к приложению.
# Они будут инициализированы в create_app() через .init_app()
db: SQLAlchemy = SQLAlchemy()
migrate: Migrate = Migrate()
def create_app(config_name: str = "development") -> Flask:
"""
Application factory — создаёт и настраивает экземпляр Flask.
Args:
config_name: ключ из config_map ('development', 'testing', 'production')
Returns:
Настроенный экземпляр Flask
"""
app = Flask(__name__)
# Загружаем конфиг из соответствующего класса
config_class: type[Config] = config_map[config_name]
app.config.from_object(config_class)
# Инициализируем расширения с app
db.init_app(app)
migrate.init_app(app, db)
# Health-check эндпоинт — проверяем, что сервер живой
@app.route("/health")
def health_check():
return jsonify({"status": "ok", "env": config_name}), 200
# Здесь будем регистрировать blueprints (шаг 05)
# from app.api import register_blueprints
# register_blueprints(app)
return app
db и migrate
существуют без привязки к конкретному приложению — их можно импортировать из моделей.
Привязка к приложению происходит в create_app() через init_app().
6. Пишем точку входа
# run.py
import os
from app import create_app
# Читаем окружение из переменной APP_ENV, по умолчанию 'development'
config_name: str = os.environ.get("APP_ENV", "development")
app = create_app(config_name)
if __name__ == "__main__":
app.run(host="0.0.0.0", port=5000)
7. Создаём .gitignore
# .gitignore
venv/
__pycache__/
*.pyc
*.pyo
.env
*.db
*.sqlite3
instance/
.pytest_cache/
.mypy_cache/
8. Создаём .env.example
# .env.example — скопируйте в .env и заполните своими значениями
APP_ENV=development
SECRET_KEY=your-secret-key-here
# DEV_DATABASE_URL=sqlite:///community_pulse_dev.db
# DATABASE_URL=postgresql://user:password@localhost/community_pulse
🧠 Объяснение логики
Application Factory vs глобальный app
В учебных примерах часто пишут app = Flask(__name__) прямо в модуле.
Это работает, но создаёт проблемы при тестировании и когда нужны разные конфиги.
Application factory решает это: вызвал create_app("testing") — получил
тестовое приложение с БД в памяти; вызвал create_app("production") — получил
продакшн-конфиг.
Почему config-классы, а не словарь
Классы позволяют использовать наследование: DevelopmentConfig и
ProductionConfig наследуют общие настройки из Config.
Переопределяем только то, что меняется. Словарь не умеет в наследование.
init_app() — паттерн расширений Flask
Расширения (SQLAlchemy, Migrate) создаются один раз на уровне модуля, без app.
Привязываются к конкретному приложению через init_app(app).
Это позволяет импортировать db из моделей, не создавая цикличных импортов.
db = SQLAlchemy(app) прямо
в create_app(). Тогда db привязан к одному экземпляру приложения
и его нельзя импортировать в моделях до создания app. Всегда используйте
db = SQLAlchemy() + db.init_app(app).
✅ Проверка
1. Запускаем приложение
python run.py
2. Проверяем health-check
curl http://localhost:5000/health
3. Ожидаемый результат
* Running on http://0.0.0.0:5000
* Debug mode: on
Успех — ответ на /health:
{"env": "development", "status": "ok"}
Диагностика: если что-то пошло не так
- Ошибка:
ModuleNotFoundError: No module named 'flask'— venv не активирован. Запуститеvenv\Scripts\Activate.ps1 - Ошибка:
Address already in use— порт 5000 занят другим процессом. Остановите другой сервер или смените порт вrun.py - Ошибка:
KeyError: 'development'— проверьте, чтоconfig_mapсодержит ключ"development"
➡️ Что дальше
На следующем шаге мы подключим Flask-SQLAlchemy и Flask-Migrate, инициализируем БД и создадим первую миграцию. После шага 02 у нас будет настроенная база данных под управлением Alembic.
- Готово: venv, зависимости, структура, config-классы, application factory, health-check
- Далее (шаг 02): Flask-SQLAlchemy + Flask-Migrate, команды
flask db init/migrate/upgrade - Связь с уроком 09: application factory здесь — правильная версия того, что было в уроке