JavaRush /Курсы /Модуль 4: FastAPI /Работа с PUT-запросами: обновление данных

Работа с PUT-запросами: обновление данных

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

Ключевые отличия между PUT и POST:

  • PUT: используется для обновления ресурса. Предполагается, что данные, которые мы отправляем, полностью заменяют существующий объект.
  • POST: используется для создания нового объекта или выполнения сложных операций на сервере.

Представьте, что PUT — это словно "переписать полностью", а POST — это "добавить что-то новое".

Пример сценария:

У нас есть пользователь с ID 123, и мы хотим изменить его имя на "Иван Иванов". Это типичная задача для PUT-запроса, так как мы обновляем уже существующую запись.


Асинхронная обработка PUT-запросов в FastAPI

FastAPI предоставляет нативную поддержку обработки PUT-запросов с помощью декоратора @app.put. Давайте разберём всё на практике.

Пример базового PUT-запроса

Для начала создадим эндпоинт, который позволяет обновлять данные о пользователе. Предположим, у нас есть простой словарь, хранящий информацию о пользователях:


from fastapi import FastAPI

app = FastAPI()

# Пример данных о пользователях
users = {
    1: {"name": "Анна", "age": 25},
    2: {"name": "Борис", "age": 30},
}

@app.put("/users/{user_id}")
async def update_user(user_id: int, updated_data: dict):
    if user_id not in users:
        return {"error": "User not found"}  # Простая обработка отсутствующего пользователя
    
    # Обновляем данные пользователя
    users[user_id] = updated_data
    return {"message": "User updated successfully", "data": users[user_id]}

Разберём кусочек кода:

  1. Маршрут PUT-запроса:
    Мы указали @app.put("/users/{user_id}"). Это означает, что запросы с методом PUT и указанным user_id будут обрабатываться этим эндпоинтом.
  2. Параметры пути:
    user_id извлекается из URL как параметр пути.
  3. Тело запроса:
    updated_data — это тело запроса, которое передаётся клиентом (например, JSON). Оно содержит данные для обновления.
  4. Обновляем данные:
    Мы просто заменяем значение в словаре users.

Добавляем валидацию данных

Использовать "сырой" dict — это как жить без тестов: возможно, но сложно и не очень безопасно. К счастью, FastAPI поддерживает Pydantic для валидации данных. Давайте создадим модель для пользователя:


from pydantic import BaseModel

class User(BaseModel):
    name: str
    age: int

Теперь изменим эндпоинт, чтобы использовать эту модель:


@app.put("/users/{user_id}")
async def update_user(user_id: int, updated_data: User):
    if user_id not in users:
        return {"error": "User not found"}
    
    # Обновляем данные пользователя, используя Pydantic-модель
    users[user_id] = updated_data.dict()
    return {"message": "User updated successfully", "data": users[user_id]}
  • Теперь updated_data автоматически проверяется на соответствие модели User: имя должно быть строкой, возраст — целым числом.
  • В случае некорректных данных (например, если возраст указан строкой), FastAPI автоматически вернёт ошибку 422 с подробным описанием.

Обновление данных частично: когда нужен PATCH?

Может возникнуть вопрос: "А что если я хочу изменить только имя, не трогая возраст?". Для этого лучше подходит метод PATCH, который позволяет частично обновить данные. Мы разберём PATCH позже в курсе, а пока обратите внимание, что стандартный PUT-запрос предполагает полное замещение данных.


Работа с базой данных: асинхронный пример

Давайте теперь представим, что мы используем базу данных для хранения данных. В качестве примера возьмём SQLite с библиотекой databases.

Установка зависимостей:


pip install databases

Пример асинхронного обновления в базе данных:


from databases import Database

DATABASE_URL = "sqlite:///./test.db"

database = Database(DATABASE_URL)

# Таблица пользователей
users_table = """
CREATE TABLE IF NOT EXISTS users (
    id INTEGER PRIMARY KEY,
    name TEXT NOT NULL,
    age INTEGER NOT NULL
)
"""

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

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

@app.put("/users/{user_id}")
async def update_user(user_id: int, updated_data: User):
    query = "UPDATE users SET name = :name, age = :age WHERE id = :id"
    values = {"name": updated_data.name, "age": updated_data.age, "id": user_id}

    # Выполняем асинхронный запрос к базе данных
    result = await database.execute(query=query, values=values)
    if result == 0:
        return {"error": "User not found"}
    
    return {"message": "User updated successfully"}
  1. База данных:
    Мы используем SQLite в качестве базы данных и управляем ей через библиотеку databases.
  2. Асинхронные функции:
    Запросы к базе данных выполняются асинхронно (await database.execute()).
  3. Обновление записи:
    Мы обновляем данные пользователя по user_id с помощью SQL-запроса UPDATE.

Ошибки и исключения при работе с PUT-запросами

При попытке обновить несуществующий ресурс (например, user_id = 999), сервер должен вернуть соответствующую ошибку. В FastAPI это можно сделать с помощью HTTPException:


from fastapi import HTTPException

if user_id not in users:
    raise HTTPException(status_code=404, detail="User not found")

Чрезмерное обновление

Использование PUT для "частичного" обновления (например, только имени) может привести к непредсказуемым результатам, если не указать остальные поля. Всегда проверяйте, что передаваемые данные соответствуют модели.


Практическое применение

PUT-запросы широко используются в реальных проектах. Например:

  • Редактирование профиля пользователя: обновление имени, электронной почты и других полей.
  • Изменение информации о продукте: обновление цены, описания или характеристик.
  • Обновление настроек приложения: сохранение пользовательских предпочтений.

Теперь вы знаете, как работать с PUT-запросами, использовать Pydantic для валидации данных и взаимодействовать с базой данных асинхронно. В следующей лекции мы перейдём к удалению данных через DELETE-запросы, а пока можете потренироваться обновлять свои данные (без нарушений GDPR, конечно 😄).

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

# В актуальной версии Pydantic (v2.x) метод .dict() считается устаревшим, вместо него используется .model_dump()
# Обновляем данные продукта в словаре
    products[product_id] = item.model_dump()
Артём Васенин Уровень 82
20 ноября 2025
Инфо устаревшее from databases import Database - не работает без пакета aiosqlite @app.on_event("startup/shutdown") - деприкейтед Теперь рабоиает через @asynccontextmanager