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-запиту

Для початку створимо endpoint, що дозволяє оновлювати дані про користувача. Припустимо, у нас є простий словник, що зберігає інформацію про користувачів:


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 будуть оброблятися цим endpoint'ом.
  2. Параметри шляху:
    user_id витягується з URL як параметр шляху.
  3. Тіло запиту:
    updated_data — це тіло запиту, яке передається клієнтом (наприклад, JSON). Воно містить дані для оновлення.
  4. Оновлюємо дані:
    Ми просто замінюємо значення в словнику users.

Додаємо валідацію даних

Використовувати «сирий» dict — це як жити без тестів: можливо, але складно й небезпечно. На щастя, FastAPI підтримує Pydantic для валідації даних. Давай створимо модель для користувача:


from pydantic import BaseModel

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

Тепер змінимо endpoint, щоб використати цю модель:


@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, звісно 😄).

Коментарі
ЩОБ ПОДИВИТИСЯ ВСІ КОМЕНТАРІ АБО ЗАЛИШИТИ КОМЕНТАР,
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ