重写DRF ModelViewSet的create()方法时如何实现get_or_create逻辑?
结论
无法直接替换perform_create实现需求,最优方案是遍历校验通过的提交数据,逐条调用get_or_create完成操作。
原因说明
- DRF 原生的
perform_create底层是调用序列化器的save()方法,在many=True的批量场景下默认执行批量INSERT操作,没有内置去重判断逻辑,直接替换方法不会产生幂等效果。 - 你需要的「重复相同请求仅创建一次实例」的幂等能力,依赖业务层面的重复判断规则,只有逐条处理单条数据才能精准匹配判断规则,确认是否需要生成新实例。
实现示例
首先确认你用来判断实例重复的唯一字段,比如BayAccessToken用user_id+access_scene作为唯一判定条件,可参考如下修改代码:
from rest_framework import status from rest_framework.response import Response class CreateTokenView(viewsets.ModelViewSet): queryset = BayAccessToken.objects.all() serializer_class = accessTokenSerializer def create(self, request, *args, **kwargs): serializer = self.get_serializer(data=request.data, many=True) serializer.is_valid(raise_exception=True) instances = [] # 遍历校验后的有效数据逐条处理 for item in serializer.validated_data: # 第一个参数填去重匹配字段,defaults填剩余需要写入/更新的字段 instance, _ = BayAccessToken.objects.get_or_create( user_id=item.pop("user_id"), access_scene=item.pop("access_scene"), defaults=item ) instances.append(instance) # 对齐原生返回格式 return Response( self.get_serializer(instances, many=True).data, status=status.HTTP_201_CREATED )
优化建议
- 建议先给
BayAccessToken的去重字段加数据库层面的唯一约束(单字段唯一或联合唯一),避免并发请求下的竞态条件产生重复数据。 - 如果单次请求提交的实例数量超过100条,可以先批量查询所有待匹配的去重字段值,过滤出不存在的条目后执行批量插入,减少数据库查询次数,性能优于逐条调用
get_or_create。 - 如果需要完全的接口级幂等(任意完全相同的请求体都仅执行一次),可以额外新增幂等令牌机制:要求请求头携带唯一的幂等ID,接口先校验幂等ID是否已经处理过,再执行业务逻辑,适配更严格的幂等场景。
内容的提问来源于stack exchange,提问作者Josh
相关产品推荐
相关产品推荐

