🐛 Типичные ошибки Flask-блока (Уроки 09–11)

⚡ Топ-3 ошибки блока Flask

  1. Не вызвать db.session.commit() после add/delete — изменения не сохраняются.
  2. Не обработать ValidationError от Pydantic — сервер падает с 500 вместо 400.
  3. Использовать orm_mode = True (Pydantic v1) вместо ConfigDict(from_attributes=True) (v2).

1. Забыть db.session.commit()

Добавление объекта через db.session.add() переводит его в состояние Pending. Без commit() изменения не попадут в базу данных.

# Неправильно
question = Question(text="Ваш вопрос?")
db.session.add(question)
# Вопрос НЕ сохранён — нет commit!

# Правильно
question = Question(text="Ваш вопрос?")
db.session.add(question)
db.session.commit()  # теперь сохранён в БД
return jsonify({'id': question.id}), 201

2. Не обработать ValidationError

Если клиент прислал некорректные данные, Pydantic выбросит ValidationError. Без обработки сервер вернёт 500 Internal Server Error вместо понятного 400.

# Неправильно
@questions_bp.route('/', methods=['POST'])
def create_question():
    data = request.get_json()
    question_data = QuestionCreate(**data)  # ValidationError не поймана!
    ...

# Правильно
from pydantic import ValidationError

@questions_bp.route('/', methods=['POST'])
def create_question():
    data = request.get_json()
    try:
        question_data = QuestionCreate(**data)
    except (ValidationError, TypeError) as e:
        return jsonify({'error': str(e)}), 400
    ...

3. Использование Pydantic v1 API в v2

В Pydantic v2 изменились имена методов. Использование старого API приведёт к AttributeError или DeprecationWarning.

# Неправильно (Pydantic v1 / из лекции)
class QuestionResponse(BaseModel):
    class Config:
        orm_mode = True

result = QuestionResponse.from_orm(question)  # AttributeError в v2
result_dict = result.dict()  # deprecated в v2

# Правильно (Pydantic v2)
from pydantic import ConfigDict

class QuestionResponse(BaseModel):
    model_config = ConfigDict(from_attributes=True)

result = QuestionResponse.model_validate(question)  # OK
result_dict = result.model_dump()  # OK

4. Blueprint не зарегистрирован в create_app

Созданный Blueprint не работает, пока не зарегистрирован в приложении. Маршруты из незарегистрированного Blueprint вернут 404.

# Неправильно — Blueprint создан, но не зарегистрирован
def create_app():
    app = Flask(__name__)
    # app.register_blueprint(questions_bp)  — забыли!
    return app

# Правильно
def create_app(config=None):
    app = Flask(__name__)
    if config:
        app.config.from_object(config)
    db.init_app(app)
    app.register_blueprint(questions_bp)   # обязательно!
    app.register_blueprint(response_bp)    # обязательно!
    return app

5. Отсутствие проверки None при поиске по id

Если объект не найден, db.session.get() вернёт None. Обращение к атрибутам None вызовет AttributeError.

# Неправильно
question = db.session.get(Question, id)
return jsonify({'text': question.text})  # AttributeError если None!

# Правильно
question = db.session.get(Question, id)
if question is None:
    return jsonify({'message': 'Вопрос не найден'}), 404
return jsonify({'text': question.text}), 200

6. Неправильная инициализация db (прямая вместо init_app)

При использовании Application Factory нельзя привязывать db к app напрямую на уровне модуля. Это ломает тесты и использование нескольких экземпляров приложения.

# Неправильно (из лекции — для простых случаев)
app = Flask(__name__)
db = SQLAlchemy(app)  # прямая привязка

# Правильно (Application Factory)
# app/models/__init__.py
db = SQLAlchemy()  # без app

# app/__init__.py
def create_app():
    app = Flask(__name__)
    db.init_app(app)  # отложенная привязка
    return app

7. Доступ к db.session вне контекста приложения

Flask-SQLAlchemy требует активного контекста приложения. Запрос вне контекста вызовет RuntimeError.

# Неправильно (вне запроса или контекста)
with app.app_context():  # обязательно при работе вне запроса
    questions = Question.query.all()

# В обычных запросах контекст создаётся автоматически.
# Но при тестах или CLI-скриптах нужно явно:
with app.app_context():
    db.create_all()

8. Забыть flask db init перед migrate

Команда flask db migrate завершится ошибкой, если папка migrations/ ещё не создана.

# Правильный порядок (один раз для нового проекта)
flask db init              # Создаёт папку migrations/
flask db migrate -m "msg"  # Создаёт файл миграции
flask db upgrade           # Применяет к БД