← Назад к вопросам

Какой нужно использовать запрос если нужно изменить не все поля данных в записи?

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}

Таблица отличий

АспектPATCHPUT
НазначениеЧастичное обновлениеПолная замена
Обязательные поляНетВсе
Неопределённые поляОстаются неизменныУдаляются/обнуляются
ИдемпотентностьМожет быть не идемпотентнаВсегда идемпотентна
Статус код200 OK или 204 No Content200 OK или 204 No Content

Лучшие практики

  1. Используй PATCH для частичного обновления
  2. Делай поля опциональными в Pydantic моделях для PATCH
  3. Проверяй только переданные поля через exclude_unset=True
  4. Логируй изменения для аудита
  5. Возвращай обновленный объект с новыми значениями

Сокращенный ответ: PATCH — для изменения части полей, PUT — для полной замены. Большинство современных API используют PATCH для гибкого обновления.

Какой нужно использовать запрос если нужно изменить не все поля данных в записи? | PrepBro