Django REST Framework(DRF)PATCH 方法部分更新全解析与实战

在 RESTful API 设计中,部分更新是一种非常常见的需求。Django REST Framework(DRF)对 HTTP 的 PATCH 方法提供了原生支持,但在实际开发中,很多开发者会遇到 validated_data 为空、数据库没有更新等问题。本文将从 HTTP 原理、DRF 支持、请求方式、Serializer 与 ViewSet 使用方法、以及实际开发中常见问题的排查方法进行全面解析,并给出可直接使用的示例。


一、HTTP PATCH 方法简介

HTTP 协议提供了多种请求方法,其中:

  • GET:查询资源
  • POST:新建资源
  • PUT:整体替换资源
  • PATCH:部分更新资源
  • DELETE:删除资源

PATCH 方法与 PUT 最大的区别在于,它只修改资源中指定的字段,而不影响未指定的字段,非常适合在前端只需要更新部分数据的场景。

例如,对于某个材料清单资源:

http 复制代码
PATCH /materials/1
Content-Type: application/json

{
  "material_count": 20
}

表示仅修改 material_count 字段,其余字段保持不变。相比之下,PUT 请求需要提交完整资源数据,否则未提交的字段可能会被置空。


二、DRF 对 PATCH 的支持

在 DRF 中,只要使用 UpdateModelMixin 或者继承 ModelViewSet,就自动支持 PUT 与 PATCH 请求。例如:

python 复制代码
class MaterialViewSet(mixins.UpdateModelMixin,
                      mixins.ListModelMixin,
                      viewsets.GenericViewSet):
    queryset = IcEngineeringChecklistMaterials.objects.all()
    serializer_class = ChecklistMaterialSerializer

默认路由情况下:

URL HTTP 方法 功能
/materials/{id}/ PUT 全量更新
/materials/{id}/ PATCH 部分更新

无需额外编写 action,就可以直接使用 PATCH 实现部分更新。


三、PATCH 请求的正确使用方式

DRF 默认只从请求体(body)读取 PATCH 数据,而不会从查询参数(params)中读取。这是许多开发者在实际开发中遇到 validated_data 为空的根本原因。

正确示例

前端使用 Axios:

js 复制代码
axios.patch("/api/materials/1/", {
    unit: "kg",
    material_count: 5
}, {
    headers: { "Content-Type": "application/json" }
});

或者使用 fetch:

js 复制代码
fetch("/api/materials/1/", {
    method: "PATCH",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify({
        unit: "kg",
        material_count: 5
    })
});

错误示例(不可用):

http 复制代码
PATCH /materials/1?unit=kg

因为 DRF 不会把 query 参数作为 request.data 处理,Serializer 无法接收到数据,validated_data 将为空。


四、Serializer 部分更新写法

对于部分更新,通常会定义一个专门的 Serializer,只包含可更新字段:

python 复制代码
class ChecklistMaterialPartialUpdateSerializer(serializers.ModelSerializer):
    class Meta:
        model = IcEngineeringChecklistMaterials
        fields = ['material_count', 'deadline_arrive', 'brand', 'unit']

如果某些字段修改需要额外逻辑(例如单位转换),可以在 update() 方法中实现:

python 复制代码
def update(self, instance, validated_data):
    if 'unit' in validated_data:
        new_unit = validated_data['unit']
        old_unit = instance.unit
        if old_unit != new_unit:
            # 示例:单位转换逻辑
            instance.material_count = convert_count(instance.material_count, old_unit, new_unit)
        instance.unit = new_unit

    for field in ['material_count', 'brand', 'deadline_arrive']:
        if field in validated_data:
            setattr(instance, field, validated_data[field])

    instance.save()
    return instance

注意

  • validated_data 不会自动写入 Model,必须通过 setattrsuper().update() 显式赋值。
  • return instance 返回的是修改后的对象(前提是你已经修改并保存了字段)。

五、ViewSet 中 get_serializer_class 写法

如果你的 ViewSet 中不同操作使用不同的 Serializer,可以通过 get_serializer_class 动态返回:

python 复制代码
def get_serializer_class(self):
    if self.action == 'partial_update':
        return ChecklistMaterialPartialUpdateSerializer
    return ChecklistSerializer

六、PATCH 常见问题排查

当发现 PATCH 不生效时,可以按照以下步骤排查:

  1. 检查 request.data 是否有内容

    打印 request.data,确保前端将数据放在请求体 JSON 中,而不是 params。

  2. 检查 serializer 校验是否通过

    使用 serializer.is_valid(raise_exception=True),确保字段验证通过。

  3. 检查 validated_data 是否为空

    打印 serializer.validated_data

  4. 检查 update 是否真正写入数据库

    可以通过 Django Admin 或直接查询数据库验证。


七、最终推荐模板

Serializer

python 复制代码
class ChecklistMaterialPartialUpdateSerializer(serializers.ModelSerializer):
    class Meta:
        model = IcEngineeringChecklistMaterials
        fields = ['material_count', 'deadline_arrive', 'brand', 'unit']

    def update(self, instance, validated_data):
        for attr, value in validated_data.items():
            setattr(instance, attr, value)
        instance.save()
        return instance

ViewSet

python 复制代码
class MaterialViewSet(mixins.UpdateModelMixin,
                      mixins.ListModelMixin,
                      viewsets.GenericViewSet):
    queryset = IcEngineeringChecklistMaterials.objects.all()

    def get_serializer_class(self):
        if self.action == 'partial_update':
            return ChecklistMaterialPartialUpdateSerializer
        return ChecklistSerializer

八、总结

  1. PATCH 用于部分更新,只修改指定字段,不影响未指定字段。
  2. DRF 自动支持 PATCH,只需继承 UpdateModelMixin 或 ModelViewSet。
  3. 请求体必须使用 JSON,参数不要放在 query params 中,否则 validated_data 会为空。
  4. Serializer.update 必须显式赋值 ,返回的 instance 是修改后的对象。
  5. 对于涉及逻辑转换的字段(如 unit),在 update 方法中处理即可。
相关推荐
cnxy1889 小时前
围棋对弈Python程序开发完整指南:步骤4 - 提子逻辑和劫争规则实现
开发语言·python·机器学习
NAGNIP9 小时前
多个 GitHub 账户SSH 密钥配置全攻略
后端
NAGNIP9 小时前
Windows命令行代码自动补全详细步骤
后端
TheSumSt9 小时前
Python丨课程笔记Part3:语法进阶部分(控制结构与基础数据结构)
数据结构·笔记·python
追逐时光者9 小时前
精选 8 款 .NET 开源、前后端分离的快速开发框架,提高开发生产效率!
后端·.net
ha_lydms9 小时前
5、Spark函数_s/t
java·大数据·python·spark·数据处理·maxcompute·spark 函数
用户479492835691510 小时前
性能提升 4000%!我是如何解决 运营看板 不能跨库&跨库查询慢这个难题的
数据库·后端·postgresql
电商API&Tina10 小时前
跨境电商 API 对接指南:亚马逊 + 速卖通接口调用全流程
大数据·服务器·数据库·python·算法·json·图搜索算法
Yyyyy123jsjs10 小时前
外汇Tick数据交易时段详解与Python实战分析
人工智能·python·区块链
老华带你飞10 小时前
农产品销售管理|基于java + vue农产品销售管理系统(源码+数据库+文档)
java·开发语言·前端·数据库·vue.js·spring boot·后端