DRF序列化带url的django-mptt树结构Section模型时报OrderedDict无uuid错误
报错根源定位
报错的触发链路和你观察到的现象完全对应:
- DRF 的超链接
url字段生成接口地址时,必须从关联的模型实例上读取lookup_field对应的属性值,比如你配置的uuid - 你当前手写的
children = serializers.ListField(source='get_children', child=RecursiveField())实现,没有将原始的Section模型实例正确透传到每一层递归序列化的上下文里 - 当递归处理到子节点的
url字段时,字段拿到的待处理对象已经是被部分序列化后的collections.OrderedDict结构,而非Section模型实例,自然无法读取到uuid或pk属性,直接抛出AttributeError - 只要移除序列化器里的
url字段,字段处理时不需要读取模型实例的主键值生成链接,报错就会完全消失,这也和你验证过的现象一致。
修复步骤
1. 替换手写嵌套字段的实现
不要用ListField包裹RecursiveField,这种写法会切断模型实例到递归序列化器的传递链路。直接调整嵌套字段的定义,适配drf-writable-nested和django-mptt的处理规则:
# serializers.py class SectionSerializer(UniqueFieldsMixin, WritableNestedModelSerializer): # 直接将RecursiveField绑定到mptt的get_children方法,移除外层ListField包裹 children = RecursiveField(many=True, source='get_children') class Meta: model = Section fields = ['url', 'uuid', 'name', 'children'] extra_kwargs = { 'url': { 'lookup_field': 'uuid', # 显式指定路由名,避免嵌套层找不到视图配置 'view_name': 'section-detail' }, }
2. 调整视图继承逻辑
你当前的视图同时继承generics.RetrieveUpdateDestroyAPIView和viewsets.GenericViewSet,存在类继承顺序冲突,容易导致序列化器生成url字段时上下文匹配异常。建议调整为标准的视图集写法:
# views.py from rest_framework import viewsets class SectionDetailViewSet(viewsets.RetrieveUpdateDestroyAPIViewSet): queryset = Section.objects.all() serializer_class = SectionSerializer lookup_field = 'uuid'
3. (可选)上下文兼容补丁
如果修复后递归层依然出现实例读取错误,可以在序列化器里增加手动透传逻辑,确保每一层的递归序列化器都能拿到完整的请求上下文:
class SectionSerializer(UniqueFieldsMixin, WritableNestedModelSerializer): # ... 原有字段配置保持不变 def to_representation(self, instance): # 将当前模型实例和上下文透传给递归字段 for field in self.fields.values(): if isinstance(field, RecursiveField): field.context = self.context return super().to_representation(instance)
效果验证
修复完成后,不管是根节点还是所有递归嵌套的子节点,url字段在生成链接时都能拿到对应的Section模型实例,正确读取实例上的uuid属性拼接接口地址,不会再出现将OrderedDict当模型实例读取属性的报错。
内容的提问来源于stack exchange,提问作者vit
相关产品推荐
相关产品推荐

