You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

更新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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.07 22:30:39