Django REST Framework中Serializer的to_representation方法报错排查
问题分析:Django REST Framework Serializer覆写create后to_representation报错
问题背景
将原本正常工作的ModelSerializer改为serializers.Serializer并覆写create方法后,TestModel对象能成功存入数据库,但调用serializer.data时触发to_representation报错。
Serializer代码
class AdddTestModel(serializers.Serializer): name = serializers.CharField() score = serializers.FloatField() score2 = serializers.FloatField() song = serializers.IntegerField() def create(self, validated_data): print(validated_data["song"]) song = Song.objects.get(id=validated_data["song"]) print(song) instance = TestModel.objects.create(name=validated_data["name"], score = validated_data["score"], score2 = validated_data["score2"], song = song) return instance def to_representation(self, instance): representation = super().to_representation(instance) return representation
视图代码
@api_view(["POST"]) def addTestModel(request): serializer = AdddTestModel(data=request.data) if serializer.is_valid(): serializer.save() print(serializer.data) return Response("hello", status=status.HTTP_201_CREATED) return Response("Error", status=status.HTTP_401_UNAUTHORIZED)
报错信息
Internal Server Error: /api/addTest/ Traceback (most recent call last): File "C:\Users\thoma\Documents\venv\lib\site-packages\django\core\handlers\exception.py", line 55, in inner response = get_response(request) File "C:\Users\thoma\Documents\venv\lib\site-packages\django\core\handlers\base.py", line 197, in _get_response response = wrapped_callback(request, *callback_args, **callback_kwargs) File "C:\Users\thoma\Documents\venv\lib\site-packages\django\views\decorators\csrf.py", line 56, in wrapper_view return view_func(*args, **kwargs) File "C:\Users\thoma\Documents\venv\lib\site-packages\django\views\generic\base.py", line 104, in view return self.dispatch(request, *args, **kwargs) File "C:\Users\thoma\Documents\venv\lib\site-packages\rest_framework\views.py", line 509, in dispatch response = self.handle_exception(exc) File "C:\Users\thoma\Documents\venv\lib\site-packages\rest_framework\views.py", line 469, in handle_exception self.raise_uncaught_exception(exc) File "C:\Users\thoma\Documents\venv\lib\site-packages\rest_framework\views.py", line 480, in raise_uncaught_exception raise exc File "C:\Users\thoma\Documents\venv\lib\site-packages\rest_framework\views.py", line 506, in dispatch response = handler(request, *args, **kwargs) File "C:\Users\thoma\Documents\venv\lib\site-packages\rest_framework\decorators.py", line 50, in handler return func(*args, **kwargs) File "C:\Users\thoma\Documents\realestate\theFirstView\api\views.py", line 53, in addTestModel print(serializer.data) File "C:\Users\thoma\Documents\venv\lib\site-packages\rest_framework\serializers.py", line 555, in data ret = super().data File "C:\Users\thoma\Documents\venv\lib\site-packages\rest_framework\serializers.py", line 253, in data self._data = self.to_representation(self.instance) File "C:\Users\thoma\Documents\venv\lib\site-packages\rest_framework\serializers.py", line 522, in to_representation ret[field.field_name] = field.to_representation(attribute) File "C:\Users\thoma\Documents\venv\lib\site-packages\rest_framework\fields.py", line 915, in to_representation return int(value) TypeError: int() argument must be a string, a bytes-like object or a real number, not 'Song'
原因解析
- ModelSerializer的自动适配:ModelSerializer会根据模型字段类型自动匹配序列化字段。比如TestModel中的
song是外键,ModelSerializer会自动使用PrimaryKeyRelatedField,该字段在反序列化时能把传入的ID转为Song实例,序列化时会自动提取实例ID作为返回值,因此不会出现类型错误。 - Serializer的手动字段缺陷:手动将
song定义为IntegerField后,该字段仅能在反序列化阶段处理数值型ID,但序列化阶段(生成serializer.data)会尝试直接将TestModel实例中的song字段(实际是Song模型对象)转成int,而IntegerField的to_representation方法无法处理模型对象,因此抛出TypeError。
解决方案
方案1:使用PrimaryKeyRelatedField替换IntegerField
替换字段类型后,它会自动处理序列化/反序列化的类型转换,同时简化create方法代码:
class AdddTestModel(serializers.Serializer): name = serializers.CharField() score = serializers.FloatField() score2 = serializers.FloatField() song = serializers.PrimaryKeyRelatedField(queryset=Song.objects.all()) def create(self, validated_data): # validated_data["song"]已自动转为Song实例,无需手动查询 instance = TestModel.objects.create(**validated_data) return instance
方案2:手动在to_representation中处理song字段
若坚持使用IntegerField,可在序列化阶段手动将Song对象转为ID:
def to_representation(self, instance): representation = super().to_representation(instance) # 将Song对象替换为对应ID representation['song'] = instance.song.id return representation
内容的提问来源于stack exchange,提问作者Thomas Michaelides
相关产品推荐
相关产品推荐

