← Назад к вопросам
Какой нужно использовать запрос если нужно изменить не все поля данных в записи?
2.0 Middle🔥 141 комментариев
#REST API и HTTP
Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
# HTTP метод для частичного обновления данных
Если нужно изменить только некоторые поля записи без обновления остальных, правильный выбор HTTP метода критичен для соответствия REST принципам.
PATCH vs PUT
Это два разных метода с разной семантикой:
PATCH — частичное обновление
PATCH (RFC 5789) предназначен именно для обновления части ресурса:
# FastAPI пример
from fastapi import FastAPI, HTTPException
from pydantic import BaseModel
app = FastAPI()
class UserUpdate(BaseModel):
name: str = None # Опциональное поле
email: str = None
age: int = None
@app.patch("/users/{user_id}")
async def update_user(user_id: int, user_update: UserUpdate):
db_user = get_user(user_id) # Получаем текущего пользователя
# Обновляем только переданные поля
if user_update.name is not None:
db_user.name = user_update.name
if user_update.email is not None:
db_user.email = user_update.email
if user_update.age is not None:
db_user.age = user_update.age
db.commit()
return db_user
Запрос клиента:
PATCH /api/users/123
Content-Type: application/json
{
"name": "John"
}
В БД обновится только name, остальные поля останутся неизменны.
PUT — полное замещение
PUT требует передать полный объект, иначе остальные поля обнулятся:
@app.put("/users/{user_id}")
async def replace_user(user_id: int, user_data: User):
# user_data должен содержать ВСЕ поля
db_user = User(**user_data.dict())
db.merge(db_user)
db.commit()
return db_user
Если клиент отправит неполные данные:
PUT /api/users/123
Content-Type: application/json
{
"name": "John"
}
Результат: потеря данных в других полях!
Практические примеры
Django ORM
from django.shortcuts import get_object_or_404
from django.http import JsonResponse
from rest_framework.decorators import api_view
from rest_framework.response import Response
@api_view(['PATCH'])
def update_product(request, pk):
product = get_object_or_404(Product, pk=pk)
# Обновляем только переданные поля
for field, value in request.data.items():
if hasattr(product, field) and value is not None:
setattr(product, field, value)
product.save()
return Response(ProductSerializer(product).data)
SQLAlchemy
from sqlalchemy import update
# Частичное обновление через PATCH
def patch_user(user_id: int, updates: dict, session):
stmt = update(User).where(User.id == user_id).values(**updates)
session.execute(stmt)
session.commit()
# Использование
patch_user(123, {"name": "John", "age": 30}, session)
# В БД обновятся только name и age
SQL запрос напрямую
-- Правильный PATCH запрос (частичное обновление)
UPDATE users SET name = 'John' WHERE id = 123;
-- Остальные поля не изменятся
-- Неправильно в контексте PATCH
UPDATE users SET name = 'John', email = NULL WHERE id = 123;
-- Это перезаписало бы email!
RESTful API дизайн
from fastapi import FastAPI
from typing import Optional
app = FastAPI()
class UserPartialUpdate(BaseModel):
"""Только опциональные поля для PATCH"""
name: Optional[str] = None
email: Optional[str] = None
phone: Optional[str] = None
class UserComplete(BaseModel):
"""Все поля обязательны для PUT"""
name: str
email: str
phone: str
age: int
@app.patch("/users/{user_id}")
async def partial_update(user_id: int, data: UserPartialUpdate):
"""Обновляем переданные поля"""
updates = data.dict(exclude_unset=True) # Только переданные поля
db.execute(
"UPDATE users SET {} WHERE id = {}".format(
", ".join(f"{k}=%s" for k in updates.keys()),
user_id
),
list(updates.values())
)
return get_user(user_id)
@app.put("/users/{user_id}")
async def full_replace(user_id: int, data: UserComplete):
"""Полностью заменяем пользователя"""
db.execute(
"UPDATE users SET name=%s, email=%s, phone=%s, age=%s WHERE id=%s",
(data.name, data.email, data.phone, data.age, user_id)
)
return get_user(user_id)
JSON Merge Patch (RFC 7386)
Стандарт для операций PATCH:
import json
from jsonschema import Draft7Validator
# Клиент отправляет
request_body = {
"email": "new@example.com"
# Поля, которых нет в теле — НЕ обновляются
}
# Сервер мержит
current_user = {"name": "John", "email": "old@example.com", "age": 30}
updated_user = {**current_user, **request_body}
# Результат: {"name": "John", "email": "new@example.com", "age": 30}
Таблица отличий
| Аспект | PATCH | PUT |
|---|---|---|
| Назначение | Частичное обновление | Полная замена |
| Обязательные поля | Нет | Все |
| Неопределённые поля | Остаются неизменны | Удаляются/обнуляются |
| Идемпотентность | Может быть не идемпотентна | Всегда идемпотентна |
| Статус код | 200 OK или 204 No Content | 200 OK или 204 No Content |
Лучшие практики
- Используй PATCH для частичного обновления
- Делай поля опциональными в Pydantic моделях для PATCH
- Проверяй только переданные поля через
exclude_unset=True - Логируй изменения для аудита
- Возвращай обновленный объект с новыми значениями
Сокращенный ответ: PATCH — для изменения части полей, PUT — для полной замены. Большинство современных API используют PATCH для гибкого обновления.