✅ Решения: Flask-проект
⚡ Краткие ответы
- Идемпотентны: GET, PUT, DELETE (не POST, не PATCH)
- Stateless: каждый запрос несёт всю необходимую информацию
- init_app vs SQLAlchemy(app): init_app поддерживает несколько экземпляров приложения
- from_attributes=True: позволяет Pydantic читать поля из ORM-объекта через атрибуты
1.1 — Идемпотентные HTTP-методы
Ответ: GET, PUT, DELETE — идемпотентны.
- GET — повторный запрос возвращает те же данные, не изменяет состояние.
- PUT — замена ресурса; повторный PUT с теми же данными не меняет результат.
- DELETE — после удаления ресурс отсутствует; повторный DELETE не изменяет это.
- POST — каждый POST может создавать новый ресурс → не идемпотентен.
- PATCH — частичное обновление; зависит от реализации, часто не идемпотентен.
1.2 — Stateless в REST
Ответ: Сервер не хранит информацию о предыдущих запросах клиента. Каждый запрос самодостаточен.
Пример: для авторизованных запросов клиент должен передавать токен в каждом запросе (в заголовке Authorization), а не рассчитывать, что сервер «помнит» сессию.
# Каждый запрос несёт токен — сервер не хранит состояние
GET /questions/
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR...
1.3 — CRUD маршруты для Article
| URL | Метод | Действие |
|---|---|---|
/articles | GET | Список всех статей |
/articles | POST | Создать статью |
/articles/<id> | GET | Получить статью |
/articles/<id> | PUT | Обновить статью полностью |
/articles/<id> | PATCH | Частично обновить статью |
/articles/<id> | DELETE | Удалить статью |
2.1 — Конфигурационный файл
# config.py
class Config:
DEBUG = False
TESTING = False
SQLALCHEMY_DATABASE_URI = 'sqlite:///app.db'
SQLALCHEMY_TRACK_MODIFICATIONS = False
class DevelopmentConfig(Config):
DEBUG = True
class TestingConfig(Config):
TESTING = True
SQLALCHEMY_DATABASE_URI = 'sqlite:///:memory:'
class ProductionConfig(Config):
pass
config_map = {
'development': DevelopmentConfig,
'testing': TestingConfig,
'production': ProductionConfig,
}
2.2 — FLASK_ENV устарела
Ответ:
FLASK_ENV удалена в Flask 2.3+. В Flask 3.x используйте:
FLASK_DEBUG=1— для включения дебаг-режима.- Кастомную переменную
APP_ENV=developmentдля выбора конфигурации в application factory.
3.1 — Blueprint users_bp
# app/routers/users.py
from flask import Blueprint, jsonify, request
users_bp = Blueprint('users', __name__)
@users_bp.route('/', methods=['GET'])
def list_users():
return jsonify({"users": []})
@users_bp.route('/', methods=['POST'])
def create_user():
data = request.get_json()
return jsonify({"created": data}), 201
@users_bp.route('/<int:user_id>', methods=['GET'])
def get_user(user_id):
return jsonify({"id": user_id, "name": "Example"})
# Регистрация в create_app():
# app.register_blueprint(users_bp, url_prefix='/users')
3.2 — Два Blueprint с одинаковым именем
Flask вызовет ошибку
ValueError: The name 'name' is already registered for a different blueprint.
Решение: задать уникальные имена при создании Blueprint:
questions_bp = Blueprint('questions', __name__)
responses_bp = Blueprint('responses', __name__) # разные имена!
3.3 — Application Factory Pattern
Application Factory — функция
create_app(config), которая создаёт и настраивает приложение Flask, не привязывая его к глобальной переменной.
Преимущества:
- Можно создать несколько экземпляров (например, один для тестов с тестовой БД).
- Расширения инициализируются через
init_app(app)— нет циклических импортов. - Легко менять конфигурацию без изменения кода.
4.1 — SQLAlchemy(app) vs init_app()
| Способ | Когда использовать |
|---|---|
db = SQLAlchemy(app) |
Только в простых скриптах / обучении. Привязывает db к одному app. |
db.init_app(app) |
В Application Factory и любом реальном проекте. Позволяет иметь несколько экземпляров приложения. |
4.2 — Эндпоинт GET /questions/<id>
@questions_bp.route('/<int:question_id>', methods=['GET'])
def get_question(question_id):
question = db.session.get(Question, question_id)
if not question:
return jsonify({"error": "Not found"}), 404
return jsonify(QuestionOut.model_validate(question).model_dump())
5.1 — from_attributes в Pydantic v2
Без
from_attributes=True Pydantic пытается читать данные из словаря / mapping-объекта. ORM-объекты SQLAlchemy — это не словари, а Python-объекты с атрибутами. При попытке QuestionOut.model_validate(orm_obj) без настройки возникнет ошибка PydanticUserError: You must set the config attribute 'from_attributes=True'.
class QuestionOut(BaseModel):
id: int
text: str
model_config = {"from_attributes": True} # обязательно для ORM!
5.2 — Blueprint responses_bp с Pydantic
# app/schemas/response.py
from pydantic import BaseModel, Field
class ResponseCreate(BaseModel):
question_id: int
agree: bool
# app/routers/responses.py
from flask import Blueprint, jsonify, request
from app import db
from app.models.responses import Response
from app.models.questions import Question
from app.schemas.response import ResponseCreate
from pydantic import ValidationError
from sqlalchemy import select
responses_bp = Blueprint('responses', __name__)
@responses_bp.route('/', methods=['GET'])
def list_responses():
responses = db.session.scalars(select(Response)).all()
result = [{"id": r.id, "question_id": r.question_id, "agree": r.agree}
for r in responses]
return jsonify(result)
@responses_bp.route('/', methods=['POST'])
def create_response():
data = request.get_json()
try:
schema = ResponseCreate(**data)
except ValidationError as e:
return jsonify({"error": e.errors()}), 422
question = db.session.get(Question, schema.question_id)
if not question:
return jsonify({"error": "Question not found"}), 404
response = Response(question_id=schema.question_id, agree=schema.agree)
db.session.add(response)
db.session.commit()
return jsonify({"id": response.id, "question_id": response.question_id,
"agree": response.agree}), 201