Django REST Framework向Serializer传递URL中recipe_id的最优方案
Django DRF 嵌套路由下向外键关联Serializer传递参数的最优方案
现有实现的问题
你目前直接修改request.data的方式存在两个明显缺陷:
- Django原生请求的
request.data默认是不可变的QueryDict对象,直接赋值会抛出AttributeError: This QueryDict instance is immutable错误,只有部分DRF解析器返回的可变字典支持该操作,兼容性很差。 - 逻辑分层不清晰:路径参数的补充逻辑和序列化校验逻辑耦合,不符合DRF的设计规范。
最优改造方案
第一步:序列化器调整
把外键recipe字段设为只读,避免用户提交的请求篡改关联关系,也不需要再校验用户传入的recipe参数合法性:
# serializers.py class IngredientSerializer(serializers.ModelSerializer): class Meta: model = Ingredient fields = [ 'id', 'name', 'quantity', 'unit', 'recipe' ] # 新增该行,把recipe设为只读字段,用户提交的参数中该字段会被忽略 read_only_fields = ['recipe']
第二步:视图层调整
不需要重写整个create方法,只要重写DRF提供的perform_create钩子(专门用于保存实例前补充额外字段),同时补充get_queryset方法,符合嵌套路由recipes/{recipe.id}/ingredients的语义,只返回对应食谱下的食材列表:
# views.py class IngredientViewSet(viewsets.ModelViewSet): serializer_class = IngredientSerializer # 只返回当前路径对应食谱的食材列表 def get_queryset(self): return Ingredient.objects.filter(recipe_id=self.kwargs['pk']) # 保存前补充recipe外键参数 def perform_create(self, serializer): serializer.save(recipe_id=self.kwargs['pk'])
方案优势
- 无兼容性问题:不需要修改不可变的
request.data,适配所有请求类型和解析器 - 逻辑清晰:序列化器负责校验用户传入的合法字段,视图层负责处理路径参数注入,符合分层设计规范
- 代码精简:不需要重写整个create方法的冗余逻辑,只用实现必要的自定义逻辑
- 安全性更高:
recipe设为只读字段后,即使用户请求中携带错误的recipe参数也会被序列化器直接忽略,不会出现关联错误
内容的提问来源于stack exchange,提问作者kashubian
相关产品推荐
相关产品推荐

