DRF中SerializerMethodField关联外键的读写实现及可浏览API缺失艺术家下拉框问题咨询
咱们先把核心问题理清楚:SerializerMethodField本身是只读字段,它只能用来处理数据输出,没法直接接收写入请求。要同时实现自定义格式的读取和写入功能,得换个思路来处理,下面分步骤给你解决:
1. 修改序列化器,同时支持读写
这里提供两种实现方式,你可以根据需求选择:
方式一:拆分读写字段(直观易理解)
用一个只读字段处理自定义格式的输出,再加一个可写字段处理输入:
class AlbumSerializer(serializers.ModelSerializer): # 只读字段:返回你需要的自定义艺术家格式 artist = serializers.SerializerMethodField("get_artist", read_only=True) # 只写字段:用来接收艺术家关联,DRF会自动生成下拉选择框 artist_id = serializers.PrimaryKeyRelatedField( queryset=Artist.objects.all(), source='artist', write_only=True ) def get_artist(self, obj): return [obj.artist.pk, obj.artist.name, obj.artist.note] class Meta: model = Album lookup_field = "name" fields = ["name", "artist", "artist_id"] read_only_fields = ["name",]
这样修改后,DRF可浏览API会显示artist_id的下拉选择框,而接口返回的响应里依然是你自定义格式的artist数据。
方式二:自定义支持读写的字段(更优雅)
如果不想用两个字段,可以自定义一个同时处理读写的字段:
class CustomArtistField(serializers.Field): def to_representation(self, value): # 读取时返回自定义格式 return [value.pk, value.name, value.note] def to_internal_value(self, data): # 写入时处理输入数据,这里假设传入的是艺术家ID try: return Artist.objects.get(pk=data) except Artist.DoesNotExist: raise serializers.ValidationError("指定的艺术家不存在") class AlbumSerializer(serializers.ModelSerializer): artist = CustomArtistField() class Meta: model = Album lookup_field = "name" fields = ["name", "artist"] read_only_fields = ["name",]
这个自定义字段既负责输出格式转换,也能处理写入时的验证和关联,序列化器里只需要一个artist字段即可,可浏览API也会生成对应的输入控件。
2. 修复视图集,实现完整的更新逻辑
原来的视图集缺少更新能力,而且retrieve方法的参数逻辑有问题,调整如下:
class AlbumViewSet(RetrieveModelMixin, ListModelMixin, UpdateModelMixin, GenericViewSet): serializer_class = AlbumSerializer lookup_field = "name" queryset = Album.objects.all() # 直接定义查询集,无需重复写get_queryset # 修正retrieve方法:默认根据专辑名称查询单个资源 def retrieve(self, request, name=None): album = self.get_object() serializer = self.get_serializer(album) return Response(serializer.data) # 如果你需要按艺术家查询专辑,添加自定义action(不要覆盖默认retrieve) @action(detail=False, methods=['get'], url_path='by-artist/(?P<artist_id>[0-9]+)') def by_artist(self, request, artist_id=None): albums = Album.objects.filter(artist_id=artist_id) serializer = self.get_serializer(albums, many=True) return Response(serializer.data) # 自定义更新逻辑(可选,用UpdateModelMixin的默认方法也可以) def update(self, request, *args, **kwargs): partial = kwargs.pop('partial', False) instance = self.get_object() serializer = self.get_serializer(instance, data=request.data, partial=partial) serializer.is_valid(raise_exception=True) self.perform_update(serializer) return Response(serializer.data)
注意:要继承UpdateModelMixin才能让视图集支持PUT/PATCH请求,原来的retrieve方法参数错误,因为lookup_field是name,所以参数应该是name而非artist,按艺术家查询的逻辑建议用自定义action实现。
3. 验证效果
修改完重启服务后,访问可浏览API的专辑更新页面(比如api/albums/nevermind/),就能看到艺术家的选择控件,提交PUT请求时传入正确的艺术家信息,即可成功更新关联,同时返回的响应依然是你需要的自定义格式。
额外提示
如果写入请求需要用艺术家名称而非ID,只需在自定义字段的to_internal_value方法里修改查询逻辑,比如Artist.objects.get(name=data)即可。
内容的提问来源于stack exchange,提问作者xtlc

