Перейти к содержанию

Отчёт по лабораторной работе №1: Реализация серверного приложения FastAPI

1. Цель работы

Целью данной работы является разработка асинхронного серверного приложения на фреймворке FastAPI для автоматизации организации хакатонов. В ходе работы решались задачи проектирования реляционной базы данных (6 таблиц), настройки асинхронного ORM-взаимодействия через SQLModel, реализации системы миграций Alembic, а также создания защищенного модуля аутентификации на базе JWT и хэширования паролей.


2. Стек технологий и структура проекта

При разработке использовались следующие инструменты: - Python 3.11+ и FastAPI. - SQLModel — гибрид SQLAlchemy и Pydantic для работы с БД. - PostgreSQL 15 + asyncpg (асинхронный драйвер). - Alembic — управление версиями схемы БД. - Adminer — веб-интерфейс для администрирования PostgreSQL. - Passlib (bcrypt) — хэширование паролей. - PyJWT (jose) — создание и валидация токенов. - Docker & Docker Compose — контейнеризация всей инфраструктуры.


3. Инфраструктура: Docker и Adminer

Для удобства разработки и соблюдения идентичности сред используется Docker Compose. В систему добавлен Adminer — легковесный инструмент для визуального контроля таблиц БД.

Фрагмент конфигурации docker-compose.yml:

services:
  db:
    image: postgres:15
    container_name: hackathon_db
    restart: always
    environment:
      POSTGRES_USER: ${POSTGRES_USER}
      POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
      POSTGRES_DB: ${POSTGRES_DB}
    ports:
      - "5432:5432"
    volumes:
      - postgres_data:/var/lib/postgresql/data

  app:
    build: .
    container_name: hackathon_app
    volumes:
      - .:/code
    ports:
      - "8000:8000"
    environment:
      DATABASE_URL: postgresql+asyncpg://${POSTGRES_USER}:${POSTGRES_PASSWORD}@db:5432/${POSTGRES_DB}
    depends_on:
      - db

  adminer:
    container_name: adminer
    image: adminer
    restart: always
    ports:
      - "1000:8080"

С помощью Adminer производился визуальный контроль структуры таблиц и проверка корректности записи данных при выполнении CRUD-операций.

Визуализация таблиц в Adminer


4. Модели данных (SQLModel)

Реализовано 6 взаимосвязанных таблиц. Выполнено требование по реализации ассоциативной сущности с дополнительным полем (role_in_team).

Ассоциативная сущность (M2M связь Участник <-> Команда):

class TeamUserLink(SQLModel, table=True):
    team_id: Optional[int] = Field(default=None, foreign_key="team.id", primary_key=True)
    user_id: Optional[int] = Field(default=None, foreign_key="user.id", primary_key=True)

    # Дополнительное поле связи (Requirement)
    role_in_team: str = Field(default="developer") 

Связи One-to-Many (Хакатон <-> Задачи): В моделях настроены обратные ссылки Relationship для автоматической навигации по объектам.

class Hackathon(HackathonBase, table=True):
    id: Optional[int] = Field(default=None, primary_key=True)
    tasks: List["Task"] = Relationship(back_populates="hackathon")

class Task(TaskBase, table=True):
    id: Optional[int] = Field(default=None, primary_key=True)
    hackathon_id: int = Field(foreign_key="hackathon.id")
    hackathon: Hackathon = Relationship(back_populates="tasks")

5. Миграции Alembic

Для асинхронной работы Alembic был настроен файл env.py, импортирующий метаданные SQLModel. Это позволило использовать команду --autogenerate для автоматического отслеживания изменений в классах Python.

История выполненных миграций: Применение миграций фиксируется в системной таблице alembic_version, что позволяет откатывать базу к любому состоянию.

Версии миграций в БД


6. Аутентификация (Задание на 15 баллов)

Реализована полноценная система безопасности на базе JWT (JSON Web Tokens).

  1. Хэширование: Пароли не хранятся в открытом виде. Используется алгоритм bcrypt.
  2. JWT: Генерируется токен доступа, содержащий идентификатор пользователя (sub) и время истечения (exp).
  3. Защита эндпоинтов: Создана функция get_current_user, которая выступает в роли зависимости (Dependency Injection) для защиты маршрутов.

Листинг создания токена (app/core/security.py):

def create_access_token(data: dict):
    to_encode = data.copy()
    expire = datetime.utcnow() + timedelta(minutes=settings.ACCESS_TOKEN_EXPIRE_MINUTES)
    to_encode.update({"exp": expire})
    return jwt.encode(to_encode, settings.SECRET_KEY, algorithm=settings.ALGORITHM)


7. API и вложенные объекты

Все CRUD-операции реализованы в асинхронном режиме с использованием await session.execute(). Реализована функциональность возвращения вложенных объектов (например, Хакатон со всеми его Задачами).

Запрос с подгрузкой зависимостей (app/api/hackathons.py):

@router.get("/{hackathon_id}", response_model=HackathonReadWithTasks)
async def get_hackathon(hackathon_id: int, session: AsyncSession = Depends(get_async_session)):
    # selectinload используется для эффективной загрузки связанных данных
    result = await session.execute(
        select(Hackathon)
        .where(Hackathon.id == hackathon_id)
        .options(selectinload(Hackathon.tasks))
    )
    return result.scalar_one_or_none()

Автоматически сгенерированная документация Swagger позволяет тестировать все методы API, включая авторизацию через заголовок Bearer.

Интерфейс Swagger


8. Выводы

В результате выполнения лабораторной работы создано масштабируемое серверное приложение. Основные итоги: - Спроектирована БД из 6 таблиц с поддержкой Many-to-Many и One-to-Many связей. - Настроена ассоциативная сущность с дополнительным полем роли. - Реализована надежная аутентификация через JWT и Bcrypt. - Обеспечено асинхронное взаимодействие с СУБД PostgreSQL. - Налажена система автоматических миграций Alembic и визуальный контроль через Adminer. - Весь проект упакован в Docker-контейнеры для быстрой развертки.