Django序列化器验证阻止更新已有对象的问题解决
问题
我有一个Django项目,用于获取用户数据并存入数据库。希望用同一方法处理Create和Update操作,因此创建了继承自UpdateAPIView的视图。代码如下:
class UserList(generics.UpdateAPIView): queryset = RawUser.objects.all() serializer_class = RawUserSerializer def put(self, request, *args, **kwargs): raw_user_serializer: RawUserSerializer = self.get_serializer(data=request.data) raw_user_serializer.is_valid(raise_exception=True) # 用户已存在时失败 raw_user: RawUser = RawUser(**raw_user_serializer.validated_data) # 一些自定义计算 # 创建或更新raw_user self.perform_update(raw_user_serializer) return JsonResponse({'user_id':raw_user.id}, status = 201) class UserDetail(generics.RetrieveAPIView): queryset = RawUser.objects.all() serializer_class = RawUserSerializer
该视图在用户不存在时可正常工作,但尝试用已存在的主键创建用户时,会返回Bad Request,错误信息为:
{ "id": [ "raw user with this id already exists." ] }
问题出在raw_user_serializer.is_valid(raise_exception=True)这一步,如何在保留对象验证的前提下解决该问题?
解决方案
核心问题是默认序列化器会校验主键的唯一性,导致更新场景触发错误。可以通过以下两种方式解决:
方法一:动态调整序列化器的唯一性验证
在序列化器中根据操作类型(新建/更新)决定是否启用主键的唯一性校验:
class RawUserSerializer(serializers.ModelSerializer): class Meta: model = RawUser fields = '__all__' def validate_id(self, value): # 如果是更新操作(已有实例),跳过id的唯一性校验 if self.instance is not None: return value # 新建时仍保留唯一性校验 if RawUser.objects.filter(id=value).exists(): raise serializers.ValidationError("raw user with this id already exists.") return value
同时修改视图逻辑,初始化序列化器时传入实例(如果存在):
def put(self, request, *args, **kwargs): # 从请求数据中获取主键,尝试查询实例 user_id = request.data.get('id') instance = None if user_id: try: instance = self.get_queryset().get(id=user_id) except RawUser.DoesNotExist: pass # 传入instance告知序列化器当前操作类型 raw_user_serializer = self.get_serializer(instance=instance, data=request.data) raw_user_serializer.is_valid(raise_exception=True) # 执行自定义计算逻辑 # 保存数据,自动判断创建或更新 raw_user = raw_user_serializer.save() return JsonResponse({'user_id': raw_user.id}, status=201 if not instance else 200)
方法二:改用GenericAPIView实现混合逻辑
直接继承GenericAPIView,手动处理创建和更新的分支逻辑,更灵活可控:
class UserList(generics.GenericAPIView): queryset = RawUser.objects.all() serializer_class = RawUserSerializer def put(self, request, *args, **kwargs): user_id = request.data.get('id') instance = None # 检查用户是否存在 if user_id: instance = self.get_queryset().filter(id=user_id).first() # 初始化序列化器,传入实例区分操作类型 raw_user_serializer = self.get_serializer(instance=instance, data=request.data) raw_user_serializer.is_valid(raise_exception=True) # 执行自定义计算 # 保存数据 raw_user = raw_user_serializer.save() # 根据操作类型返回对应状态码 return JsonResponse({'user_id': raw_user.id}, status=201 if not instance else 200)
以上两种方式都能保留序列化器的字段验证逻辑,同时支持同一接口处理创建和更新操作。
内容的提问来源于stack exchange,提问作者Nakeuh
相关产品推荐
相关产品推荐

