🏠 Домашнее задание 5
Расширение функциональности Community Pulse: категории вопросов
⚡ Суть задания
- Создать модель
Category(id, name) - Добавить внешний ключ
category_idв модельQuestion - Создать миграцию через Flask-Migrate
- Применить:
flask db upgrade
Задание из LMS (дословно)
Задачи:
-
Создание модели Category: Создайте новую модель
Categoryс использованием SQLAlchemy в модуле models. Модель должна содержать следующие поля:id: первичный ключ, целое число, авто-инкремент.name: строка, название категории, не должно быть пустым.
- Обновление модели Question: Модель Question должна быть обновлена, чтобы включить ссылку на Category через внешний ключ.
- Миграция базы данных: Создайте новую миграцию для добавления таблицы категорий и обновления таблицы вопросов с использованием Flask-Migrate.
Подготовка окружения
1. Создание виртуального окружения
# PowerShell / терминал VS Code
cd E:\projects\community_pulse
python -m venv venv
.\venv\Scripts\activate # Windows PowerShell
# Установка зависимостей
pip install flask flask-sqlalchemy flask-migrate pydantic
2. Структура проекта (должна быть у вас)
/community_pulse/
|-- app/
| |-- __init__.py
| |-- routers/
| | |-- questions.py
| | |-- responses.py
| |-- models/
| | |-- questions.py
| | |-- responses.py
| |-- schemas/
| |-- question.py
|-- config.py
|-- run.py
3. Инициализация git (если ещё не сделано)
git init
git add .
git commit -m "initial: community pulse project"
Пошаговое решение
Шаг 1: Создать модель Category
Создайте файл app/models/category.py:
# app/models/category.py
from app import db
class Category(db.Model):
"""Модель категории вопросов."""
__tablename__ = 'categories'
id = db.Column(db.Integer, primary_key=True, autoincrement=True)
name = db.Column(db.String(100), nullable=False, unique=True)
# обратная связь: список вопросов в этой категории
questions = db.relationship('Question', backref='category', lazy=True)
def __repr__(self):
return f'<Category {self.id}: {self.name}>'
Почему nullable=False: название категории обязательно — без него запись теряет смысл.
Почему unique=True: две категории с одинаковым именем — логическая ошибка данных.
Шаг 2: Обновить модель Question — добавить ForeignKey
Отредактируйте app/models/questions.py:
# app/models/questions.py
from app import db
class Question(db.Model):
__tablename__ = 'questions'
id = db.Column(db.Integer, primary_key=True, autoincrement=True)
text = db.Column(db.String(500), nullable=False)
# НОВОЕ: внешний ключ на таблицу категорий
category_id = db.Column(
db.Integer,
db.ForeignKey('categories.id'),
nullable=True # nullable=True — вопрос может быть без категории
)
responses = db.relationship('Response', backref='question', lazy=True)
def __repr__(self):
return f'<Question {self.id}: {self.text[:30]}>'
Логика nullable=True: существующие вопросы в БД не имеют категории, поэтому при добавлении столбца через миграцию они получат NULL — это допустимо.
Шаг 3: Добавить Flask-Migrate в create_app()
Обновите app/__init__.py:
# app/__init__.py
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
from flask_migrate import Migrate
db = SQLAlchemy()
migrate = Migrate()
def create_app(config_name='development'):
app = Flask(__name__)
from config import config_map
app.config.from_object(config_map[config_name])
db.init_app(app)
migrate.init_app(app, db) # НОВОЕ: инициализация миграций
from app.routers.questions import questions_bp
from app.routers.responses import responses_bp
app.register_blueprint(questions_bp, url_prefix='/questions')
app.register_blueprint(responses_bp, url_prefix='/responses')
return app
Шаг 4: Добавить FLASK_APP в окружение
# PowerShell
$env:FLASK_APP = "run.py"
$env:APP_ENV = "development"
Шаг 5: Инициализировать Flask-Migrate
# Если папка migrations ещё не создана:
flask db init
# Создать новую миграцию
flask db migrate -m "add category table and category_id to questions"
Flask-Migrate автоматически сгенерирует скрипт миграции в папке migrations/versions/. Проверьте его содержимое — там должны быть операции:
op.create_table('categories', ...)— создание таблицы категорийop.add_column('questions', sa.Column('category_id', ...))— добавление столбцаop.create_foreign_key(...)— создание связи
Шаг 6: Применить миграцию
flask db upgrade
Команда применит изменения к базе данных. Если всё прошло без ошибок — таблица categories создана, в questions появился столбец category_id.
Шаг 7: Зафиксировать изменения в git
git add app/models/category.py app/models/questions.py app/__init__.py migrations/
git commit -m "feat: add Category model with FK to Question + migration"
Проверка в VS Code
Терминал
# Запускаем приложение
python run.py
# В другом терминале — тестируем через curl
# Пока нет эндпоинтов для категорий, проверяем существующие:
curl http://localhost:5000/questions/
# должен вернуть []
# Создаём вопрос (category_id необязателен)
curl -X POST http://localhost:5000/questions/ \
-H "Content-Type: application/json" \
-d '{"text": "Тестовый вопрос без категории"}'
Настройка launch.json для F5
Создайте файл .vscode/launch.json:
{
"version": "0.2.0",
"configurations": [
{
"name": "Flask: Community Pulse",
"type": "debugpy",
"request": "launch",
"module": "flask",
"args": ["run", "--debug"],
"env": {
"FLASK_APP": "run.py",
"APP_ENV": "development"
},
"jinja": true,
"justMyCode": false
}
]
}
После этого нажмите F5 — Flask запустится в режиме отладки. Точки останова ставятся кликом на поле слева от номера строки.
Точки останова для проверки
- Откройте
app/routers/questions.py - Поставьте точку останова на строке
db.session.add(question) - Отправьте POST запрос через Postman или curl
- VS Code остановится на точке: вы можете проверить значение
question,schema.text, и т.д.
Связь с теорией урока
- Модели и ForeignKey — Theory: структура моделей и Примеры: шаг 2–3
- Миграции Flask-Migrate — Справочник: команды Flask-Migrate
- Application Factory — Theory: раздел 7
- Почему nullable=True при добавлении нового столбца — стандартная практика при изменении существующей схемы
categories_bp с эндпоинтами GET /categories/ и POST /categories/, чтобы можно было управлять категориями через API. Это хорошая практика для закрепления материала.