更新Django模型Choice字段时触发RecursionError问题排查
问题分析与解决
错误根源
出现RecursionError: maximum recursion depth exceeded while calling a Python object的核心原因有三个:
1. 序列化器字段定义错误
你的TaskUpdateSerializer中:
fields = ("status")
这不是元组,Python会将其解析为字符串"status",而非包含单个元素的元组。DRF遇到这种情况会默认序列化模型的所有字段,如果Task模型和User模型存在关联(比如外键),序列化时会触发循环引用(序列化Task时要序列化User,序列化User时又要序列化关联的Task),最终导致递归深度超出限制。
修复方法:给字段元组添加逗号,明确是元组类型:
class TaskUpdateSerializer(serializers.ModelSerializer): class Meta: model = Task fields = ("status",) # 注意末尾的逗号
2. 视图中冗余且错误的手动赋值
视图里你已经通过serializer.is_valid()验证了请求数据,却手动修改task.status再保存:
task.status = request.data.get("status", task.status) task.save()
这种操作完全冗余,且会绕过序列化器的字段验证逻辑,同时可能和序列化器内部的实例处理产生冲突,加剧递归问题。
修复方法:删除手动赋值代码,直接调用序列化器的save()方法:
if serializer.is_valid(): serializer.save() # 替换手动赋值+save的逻辑 return Response(serializer.data, status=status.HTTP_200_OK)
3. 模型默认值设置错误
模型中status字段的default=StatusChoices[0],这里StatusChoices[0]是元组("TODO", "todo"),但CharField需要的是存储的键值(即字符串"TODO"),传入元组会导致字段赋值时出现类型错误,间接引发后续的递归问题。
修复方法:将默认值改为元组的第一个元素:
status = models.CharField( choices=StatusChoices, default=StatusChoices[0][0], # 取元组的第一个元素"TODO" max_length=5, )
完整修正后的代码片段
模型
StatusChoices = ( ("TODO", "todo"), ("DOING", "doing"), ("DONE", "done"), ) class Task(models.Model): status = models.CharField( choices=StatusChoices, default=StatusChoices[0][0], max_length=5, ) # 其他字段(content、creationDate、user等)
序列化器
class TaskUpdateSerializer(serializers.ModelSerializer): class Meta: model = Task fields = ("status",)
视图
class TaskUpdateAPIView(APIView): def patch(self, request, pk): try: task = Task.objects.get(pk=pk) except Task.DoesNotExist: return Response(status=status.HTTP_404_NOT_FOUND) serializer = TaskUpdateSerializer(task, data=request.data, partial=True) if serializer.is_valid(): serializer.save() return Response(serializer.data, status=status.HTTP_200_OK) else: return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
内容的提问来源于stack exchange,提问作者Alex Ironside
相关产品推荐
相关产品推荐

