Django DRF ModelSerializer外键返回ID而非名称问题排查
DRF(Django REST Framework)的ModelSerializer默认对ForeignKey类型字段的序列化规则是:输出时返回关联对象的主键ID,输入时接收主键ID完成关联。你当前使用的POLLSerializer完全采用默认配置,没有自定义外键字段的输出逻辑,所以产品列表接口返回的分类、子分类、颜色、尺寸字段都是主键ID,而非你需要的名称值。
你现有的表单提交ID、插入逻辑接收ID的写法完全符合序列化器的写入要求,不需要调整,只需要修改序列化器的输出逻辑即可。
方案1:重写to_representation(零额外改动,最适配当前场景)
直接在POLLSerializer中重写序列化输出方法,将外键ID替换为对应名称,写入逻辑完全保留原有规则,不需要修改前端表单、视图的任何代码:
class POLLSerializer(serializers.ModelSerializer): class Meta: model = Products fields = "__all__" def to_representation(self, instance): # 调用默认序列化逻辑拿到原始返回值 rep = super().to_representation(instance) # 替换外键字段值为对应名称 rep['categories'] = instance.categories.category_name rep['sub_categories'] = instance.sub_categories.sub_categories_name rep['color'] = instance.color.color_name rep['size'] = instance.size.size_name return rep
这个方案修改后,序列化输出的四个外键字段直接是名称字符串,写入时仍然支持接收前端表单传的ID,和你现有代码100%兼容,改完序列化器直接生效。
方案2:分离读写字段(适合需要同时返回ID和名称的场景)
如果你需要在接口中同时返回外键的ID和名称,可以显式定义读写分离的字段:
class POLLSerializer(serializers.ModelSerializer): # 只读字段,返回对应名称 categories_name = serializers.CharField(source='categories.category_name', read_only=True) sub_categories_name = serializers.CharField(source='sub_categories.sub_categories_name', read_only=True) color_name = serializers.CharField(source='color.color_name', read_only=True) size_name = serializers.CharField(source='size.size_name', read_only=True) class Meta: model = Products fields = "__all__" # 外键原字段(ID)保留,新增名称字段
这个方案的返回结果会保留原categories字段为ID,同时新增categories_name字段返回名称,前端列表页直接取xxx_name字段渲染即可,写入逻辑仍然不受影响。
方案3:嵌套序列化(适合需要返回关联表多字段的场景)
如果后续需要返回分类描述、颜色编码等更多关联表字段,可以使用嵌套序列化:
# 先定义各关联表的简化序列化器 class CategoriesSimpleSerializer(serializers.ModelSerializer): class Meta: model = Categories fields = ['id', 'category_name', 'category_description'] class SUBCategoriesSimpleSerializer(serializers.ModelSerializer): class Meta: model = SUBCategories fields = ['id', 'sub_categories_name', 'sub_categories_description'] class ColorsSimpleSerializer(serializers.ModelSerializer): class Meta: model = Colors fields = ['id', 'color_name', 'color_description'] class SizeSimpleSerializer(serializers.ModelSerializer): class Meta: model = Size fields = ['id', 'size_name', 'size_description'] class POLLSerializer(serializers.ModelSerializer): # 嵌套关联序列化器设为只读,用于输出 categories = CategoriesSimpleSerializer(read_only=True) sub_categories = SUBCategoriesSimpleSerializer(read_only=True) color = ColorsSimpleSerializer(read_only=True) size = SizeSimpleSerializer(read_only=True) # 定义只写字段,用于接收前端传的ID完成写入 categories_id = serializers.PrimaryKeyRelatedField( queryset=Categories.objects.filter(isactive=True), source='categories', write_only=True ) sub_categories_id = serializers.PrimaryKeyRelatedField( queryset=SUBCategories.objects.filter(isactive=True), source='sub_categories', write_only=True ) color_id = serializers.PrimaryKeyRelatedField( queryset=Colors.objects.filter(isactive=True), source='color', write_only=True ) size_id = serializers.PrimaryKeyRelatedField( queryset=Size.objects.filter(isactive=True), source='size', write_only=True ) class Meta: model = Products fields = "__all__"
这个方案输出的外键字段是嵌套对象,比如
"categories": {"id":1, "category_name":"上衣", "category_description":"服装分类"},使用时需要把前端表单提交的字段名从categories改成categories_id,改动稍大,适合复杂业务场景。
- 模型字段命名不规范:
SUBCategories模型中关联Categories的外键字段命名为category_name,和实际存储的外键关联关系不符,建议后续重构时改名为category,避免歧义。 - 调试残留代码:
insert视图中print('POST',id)里的id是Python内置函数,无实际业务意义,可以删除。
内容的提问来源于stack exchange,提问作者Ameya Potdar

