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 方法中处理即可。
相关推荐
软件开发技术深度爱好者3 小时前
Python + Ursina设计3D小游戏
开发语言·python
黑客思维者3 小时前
Python 3.14(2025最新版)的核心语法特性分析
服务器·开发语言·python·多线程
麻辣兔变形记3 小时前
永续合约杠杆逻辑全解析:前端、后端和保证金的关系
前端·后端·区块链·智能合约
每日出拳老爷子3 小时前
[Python自动化] 用 Python + Selenium 做一个“浏览器重复操作录制器”:录一次,自动点一百次(附GUI+源码)
python·selenium·自动化
Aspect of twilight3 小时前
LeetCode华为2025年秋招AI大模型岗刷题(三)
python·算法·leetcode
编程修仙3 小时前
第二篇 SpringBoot项目启动流程
java·spring boot·后端
月亮!3 小时前
智能合约的安全验证实践
网络·人工智能·python·测试工具·安全·自动化·智能合约
C++业余爱好者3 小时前
Springboot中的缓存使用
spring boot·后端·缓存
ULTRA??3 小时前
D*算法初学,python D*路径规划算法实现
python·算法