eagleye

DRF 中 PUT 与 PATCH 请求的企业级实践指南

DRF 中 PUT 与 PATCH 请求的企业级实践指南

一、PUT 与 PATCH 的核心差异

特性

PUT(全量更新)

PATCH(增量更新)

数据传输

需提供完整资源字段

仅传输需修改的字段

幂等性

是(多次调用结果一致)

通常设计为是

适用场景

简单资源全量替换、数据重置

复杂资源部分更新、移动端省流量

带宽消耗

高(完整数据)

低(仅变更字段)

失败影响

可能导致资源整体不一致

仅影响修改字段

二、DRF 处理机制

1. 视图层默认行为

o ModelViewSet中,PUT请求映射至update()方法,PATCH请求映射至partial_update()方法。

class UserViewSet(viewsets.ModelViewSet):

queryset = User.objects.all()

serializer_class = UserSerializer

# PUT → update(),PATCH → partial_update()

2. 序列化器关键参数partial

o partial=True(PATCH 请求):仅验证提供的字段,未提供字段不参与验证。

o partial=False(PUT 请求):需验证所有必填字段,缺失则报错。

def update(self, instance, validated_data):

# PUT:validated_data 包含所有字段

# PATCH:validated_data 仅包含修改字段

return super().update(instance, validated_data)

三、企业级实现策略

1. 安全增强的视图层控制

class SecureUserViewSet(viewsets.ModelViewSet):

def update(self, request, *args, **kwargs):

# PUT 请求:全量更新前验证权限与敏感字段

instance = self.get_object()

if not request.user.is_superuser:

return Response({"error": "无全量更新权限"}, status=403)

# 禁止修改关键字段(如 security_level)

if 'security_level' in request.data:

return Response({"error": "禁止修改敏感字段"}, status=400)

return super().update(request, *args, **kwargs)

def partial_update(self, request, *args, **kwargs):

# PATCH 请求:记录审计日志

UpdateLog.objects.create(

user=request.user,

target_id=kwargs['pk'],

changed_fields=list(request.data.keys()),

ip=request.META.get('REMOTE_ADDR')

)

return super().partial_update(request, *args, **kwargs)

2. 智能序列化器设计

class SecureUserSerializer(serializers.ModelSerializer):

class Meta:

model = User

fields = ['id', 'username', 'email', 'avatar']

read_only_fields = ['id']

def validate(self, data):

# PUT 请求强制验证必填字段

if not self.partial: # 非 partial → PUT 请求

if 'email' not in data:

raise serializers.ValidationError({"email": "全量更新时必填"})

return data

def update(self, instance, validated_data):

# 密码加密处理

if 'password' in validated_data:

instance.set_password(validated_data.pop('password'))

return super().update(instance, validated_data)

3. 并发控制与数据一致性
  • 乐观锁:通过版本号防止并发冲突class User(models.Model):

version = models.IntegerField(default=0) # 版本号字段

class UserSerializer(serializers.ModelSerializer):

def validate(self, data):

if self.instance.version != self.context['request'].data.get('version'):

raise serializers.ValidationError({"version": "数据已更新,请重试"})

return data

def update(self, instance, validated_data):

instance.version += 1 # 更新版本号

return super().update(instance, validated_data)

四、最佳实践总结

1. 安全层面

字段级权限控制(如 HR 仅可修改部门字段)。

敏感字段保护(禁止非管理员修改is_superuser)。

审计日志记录所有更新操作(用户、IP、修改字段)。

2. 性能优化

o PATCH 请求减少数据传输,降低带宽消耗。

预加载关联数据(如prefetch_related)避免 N+1 查询。

3. 场景选择

优先用 PATCH:移动端、复杂资源、少量字段更新。

必须用 PUT:需全量覆盖、数据重置、简单资源场景。

4. 测试策略

验证 PUT 全量字段必填性、PATCH 部分更新逻辑。

o 模拟并发冲突,测试版本号控制有效性。

通过以上策略,可构建安全、高效、符合企业级标准的 DRF 更新接口,兼顾数据一致性与用户体验。

 

posted on 2025-07-25 11:36  GoGrid  阅读(12)  评论(0)    收藏  举报

导航