DRF中使用StringRelatedField导致interest_category传值为null的问题
DRF中外键字段传值为null的问题及解决方案
问题场景
在Django Rest Framework中设置interest_category外键字段时,前端发送interest_category: 2,但数据库中该字段被存为null。移除序列化器中的serializers.StringRelatedField()后,传值恢复正常。
发送的数据
{ "project_title": "Test", "project_description": "Test1", "interest_category": 2 }
数据库存储结果
(35, Test, Test1, 2022-09-22 02:11:54.342202+00, 2022-09-22 02:11:54.342202+00, 1, null)
相关代码
Serializer
class ProjectsSerializer(serializers.ModelSerializer): interest_category = serializers.StringRelatedField() class Meta: model = Project read_only_fields = ( 'created_by', 'created_at', 'updated_at', ), fields = ( 'project_title', 'project_description', 'interest_category', )
Model
class Project(models.Model): project_title = models.CharField(max_length=255) project_description = models.TextField(max_length=1500) interest_category = models.ForeignKey(Interests, on_delete=models.CASCADE) created_by = models.ForeignKey(User, related_name='projects', on_delete=models.CASCADE) created_at = models.DateTimeField(auto_now_add=True) updated_at = models.DateTimeField(auto_now_add=True)
Views
class ProjectViewSet(viewsets.ModelViewSet): serializer_class = ProjectsSerializer queryset = Project.objects.order_by('-created_at') def perform_create(self, serializer): serializer.save(created_by=self.request.user)
问题原因
serializers.StringRelatedField()是只读字段,它仅负责将外键关联的对象序列化为字符串(比如Interests模型__str__方法的返回值),完全不支持反序列化操作——DRF会直接忽略前端传入的该字段值,导致数据库中该字段被设为null。
解决方案:同时支持ID提交与名称返回
如果需要既能通过ID提交数据,又能在返回结果中拿到外键字段的名称,推荐以下几种方法:
方法1:新增只读字段返回名称
保留默认外键字段用于接收ID,新增一个只读的StringRelatedField返回名称:
class ProjectsSerializer(serializers.ModelSerializer): # 新增只读字段,返回interest_category的名称 interest_category_name = serializers.StringRelatedField(source='interest_category', read_only=True) class Meta: model = Project read_only_fields = ('created_by', 'created_at', 'updated_at') fields = ('project_title', 'project_description', 'interest_category', 'interest_category_name')
- 前端提交时依然传
interest_category: 2 - 接口返回时会同时包含
interest_category(ID)和interest_category_name(对应名称)
方法2:重写to_representation方法
不想新增字段的话,可重写序列化器的to_representation方法,在返回数据中注入名称:
class ProjectsSerializer(serializers.ModelSerializer): class Meta: model = Project read_only_fields = ('created_by', 'created_at', 'updated_at') fields = ('project_title', 'project_description', 'interest_category') def to_representation(self, instance): # 获取默认序列化数据 data = super().to_representation(instance) # 把外键名称添加到返回结果里 data['interest_category_name'] = str(instance.interest_category) return data
效果与方法1一致,无需单独声明字段。
方法3:用SlugRelatedField支持名称提交(可选)
如果需求是前端通过名称而非ID提交数据,同时返回名称,可使用SlugRelatedField(假设Interests模型有name字段):
class ProjectsSerializer(serializers.ModelSerializer): interest_category = serializers.SlugRelatedField( queryset=Interests.objects.all(), slug_field='name' # 指定用Interests的name字段匹配 ) class Meta: model = Project read_only_fields = ('created_by', 'created_at', 'updated_at') fields = ('project_title', 'project_description', 'interest_category')
这种场景下前端需要传interest_category: "分类名称",接口返回的也是该名称。
内容的提问来源于stack exchange,提问作者ckcAdmin
相关产品推荐
相关产品推荐

