JavaRush /Курсы /Модуль 4: FastAPI /Взаимодействие с базой данных через SQLAlchemy в FastAPI

Взаимодействие с базой данных через SQLAlchemy в FastAPI

Модуль 4: FastAPI
6 уровень , 5 лекция
Открыта

Сегодня мы сделаем ещё один шаг вперёд и углубимся в то, как интегрировать SQLAlchemy с FastAPI для выполнения CRUD-операций через API.

Готовы? Наливайте кофе, мы начинаем!

Как только наши модели данных настроены в SQLAlchemy, нам нужно предоставить интерфейс для взаимодействия с этими данными.

И здесь FastAPI показывает себя во всей красе: он позволяет легко создавать маршруты (endpoints) для выполнения CRUD операций.


Пример модели данных

Начнём с базовой модели данных, которую мы уже создавали ранее.

Предположим, у нас есть таблица User, содержащая информацию о пользователях.


from sqlalchemy import Column, Integer, String
from sqlalchemy.ext.declarative import declarative_base

Base = declarative_base()

class User(Base):
    __tablename__ = 'users'

    id = Column(Integer, primary_key=True, index=True)
    name = Column(String, index=True)
    email = Column(String, unique=True, index=True)

Настройка подключения и сессии

Для начала нам нужно настроить подключение к базе данных. Это сердце взаимодействия с базой данных через FastAPI.


from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker

DATABASE_URL = "sqlite:///./test.db"  # Используем SQLite для примера

engine = create_engine(DATABASE_URL, connect_args={"check_same_thread": False})
SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)

# Создаём таблицы
Base.metadata.create_all(bind=engine)

Мы снова используем SQLite для простоты, но вы можете легко заменить это на PostgreSQL или любую другую реляционную базу данных.

Создание маршрутов (endpoints)

Теперь, когда подключение настроено, создадим API-маршруты для выполнения CRUD-операций. Начнём с простого FastAPI-приложения.


from fastapi import FastAPI, Depends, HTTPException
from sqlalchemy.orm import Session

app = FastAPI()

# Зависимость для получения сессии базы данных
def get_db():
    db = SessionLocal()
    try:
        yield db
    finally:
        db.close()

Функции для операций

Создадим функции для каждой CRUD-операции: создание, чтение, обновление и удаление.

Создание пользователя


from pydantic import BaseModel

class UserCreate(BaseModel):
    name: str
    email: str

@app.post("/users/", response_model=UserCreate)
def create_user(user: UserCreate, db: Session = Depends(get_db)):
    db_user = User(name=user.name, email=user.email)
    db.add(db_user)
    db.commit()
    db.refresh(db_user)
    return db_user

Когда пользователь отправляет запрос на создание (POST), мы добавляем новый объект в базу данных, фиксируем изменения с помощью db.commit() и возвращаем созданный объект.

Чтение пользователей


from typing import List

@app.get("/users/", response_model=List[UserCreate])
def read_users(skip: int = 0, limit: int = 10, db: Session = Depends(get_db)):
    users = db.query(User).offset(skip).limit(limit).all()
    return users

Мы используем skip и limit в качестве параметров запроса, чтобы реализовать пагинацию. Это полезно при больших объёмах данных.

Обновление пользователя


@app.put("/users/{user_id}", response_model=UserCreate)
def update_user(user_id: int, updated_user: UserCreate, db: Session = Depends(get_db)):
    db_user = db.query(User).filter(User.id == user_id).first()
    if not db_user:
        raise HTTPException(status_code=404, detail="User not found")
    db_user.name = updated_user.name
    db_user.email = updated_user.email
    db.commit()
    db.refresh(db_user)
    return db_user

Мы выполняем поиск пользователя по id, обновляем его данные, фиксируем изменения и возвращаем обновлённый объект.

Удаление пользователя


@app.delete("/users/{user_id}")
def delete_user(user_id: int, db: Session = Depends(get_db)):
    db_user = db.query(User).filter(User.id == user_id).first()
    if not db_user:
        raise HTTPException(status_code=404, detail="User not found")
    db.delete(db_user)
    db.commit()
    return {"message": "User deleted successfully"}

Здесь мы ищем пользователя, удаляем его и фиксируем изменения в базе данных. Если пользователь не найден, возвращаем ошибку 404.


Пример выполнения асинхронных операций

FastAPI поддерживает асинхронные функции, но важно понимать, что пока SQLAlchemy (в своём стандартном виде) работает синхронно.

Для асинхронных операций нам потребуется использовать библиотеку asyncpg, вместе с databases.

Установка необходимых библиотек


pip install databases asyncpg

Асинхронная настройка подключения

Используем библиотеку databases для асинхронного взаимодействия.


from databases import Database

DATABASE_URL = "postgresql+asyncpg://user:password@localhost/testdb"

database = Database(DATABASE_URL)

@app.on_event("startup")
async def startup():
    await database.connect()

@app.on_event("shutdown")
async def shutdown():
    await database.disconnect()

Асинхронные CRUD-операции реализуются практически так же, как и синхронные. Например:

Создание записи


@app.post("/async_users/")
async def create_async_user(user: UserCreate):
    query = "INSERT INTO users(name, email) VALUES (:name, :email)"
    await database.execute(query=query, values={"name": user.name, "email": user.email})
    return {"message": "User created!"}

Чтение записей


@app.get("/async_users/")
async def read_async_users():
    query = "SELECT * FROM users"
    return await database.fetch_all(query=query)

Асинхронные операции особенно полезны при высоких нагрузках или при взаимодействии с другими API.


Тестирование CRUD операций через API

Для тестирования мы можем использовать инструменты вроде Postman или curl.

Но автоматическое тестирование — это must-have для любого серьёзного проекта.

Давайте создадим тесты с использованием Pytest и TestClient.

FastAPI предоставляет TestClient для тестирования.

Установка необходимых библиотек


pip install pytest httpx

Пример теста


from fastapi.testclient import TestClient

client = TestClient(app)

def test_create_user():
    response = client.post("/users/", json={"name": "John", "email": "john@example.com"})
    assert response.status_code == 200
    assert response.json()["email"] == "john@example.com"

Этот тест проверяет маршруты POST для создания пользователя. Аналогично можно тестировать чтение, обновление и удаление.


Теперь наше приложение полноценно взаимодействует с базой данных через SQLAlchemy и FastAPI.

Мы реализовали основные CRUD операции, изучили асинхронные возможности и автоматическое тестирование.

Это не только полезно для реальных проектов, но и впечатлит любого интервьюера на собеседовании! 🚀

1
Задача
Модуль 4: FastAPI, 6 уровень, 5 лекция
Недоступна
Создание эндпоинта для добавления данных
Создание эндпоинта для добавления данных
1
Задача
Модуль 4: FastAPI, 6 уровень, 5 лекция
Недоступна
Чтение и фильтрация данных
Чтение и фильтрация данных
Комментарии
ЧТОБЫ ПОСМОТРЕТЬ ВСЕ КОММЕНТАРИИ ИЛИ ОСТАВИТЬ КОММЕНТАРИЙ,
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ