Django REST框架:使序列化器对请求有效载荷和响应体的行为不同?



我试图想出一种简单的方法,使我的序列化器和视图按我想要的方式工作,同时防止一堆手动样板。

我的简化模型:
class Character(models.Model):
name = models.CharField(max_length=255)
location = models.ForeignKey(Location, on_delete=models.SET_NULL, blank=True, null=True)
factions = models.ManyToManyField(Faction, blank=True)

所以:角色可以有一个位置和多个派系。

现在,当我得到一个字符时,我希望这些子模型(LocationFaction)被扩展为它们的完整表示,所以我的序列化器看起来像这样:

class CharacterSerializer(serializers.ModelSerializer):
location = LocationSerializer(read_only=True)
factions = FactionSerializer(many=True, read_only=True)
class Meta:
model = Character
fields = "__all__"

到目前为止,这工作得很好。事情是,当我POST或PUT一个字符,我想只是发送id(s)..但仍然得到完整的位置和阵营对象返回的响应。

换句话说,当我用这样的有效载荷创建一个字符时:

{"name":"Saga","location":1,"factions":[1]}

我希望响应看起来像这样:

{
"id": 1,
"location": {
"id": 1,
"name": "Location 1"
},
"factions": [
{
"id": 1,
"name": "Faction 1"
}
],
"name": "Saga"
}

这是可能的,在没有覆盖ModelViewSet子类的createupdate方法吗?我希望我能稍微修改一下序列化器本身,只在响应上应用那些locationfactions字段序列化器,而不是在请求上。

您可以覆盖序列化器的createupdate方法来使用主键。

这意味着我们首先从validated_data弹出数据,然后使用剩余的数据来创建/更新对象。然后我们可以使用.set(…).add(…)来更新多对多关系:

class CharacterSerializer(serializers.ModelSerializer):
location = LocationSerializer(read_only=True)
factions = FactionSerializer(many=True, read_only=True)
def create(self, validated_data):
location_data = validated_data.pop('location')
factions_data = validated_data.pop('factions')
character = Character.objects.create(
**validated_data,
location_id=location_data
)
character.factions.add(*factions_data)
defupdate(self, instance, validated_data):
location_data = validated_data.pop('location')
factions_data = validated_data.pop('factions')
instance.location_id = location_data
result = super().update(instance, validated_data)
instance.factions.set(faction_data)
return result
class Meta:
model = Character
fields = '__all__'

到目前为止,我所做的是创建我自己的ModelViewSet子类:

class DualSerializerModelViewSet(viewsets.ModelViewSet):
def create(self, request, *args, **kwargs):
write_serializer = self.write_serializer_class(data=request.data)
write_serializer.is_valid(raise_exception=True)
created_object = self.perform_create(write_serializer)
headers = self.get_success_headers(write_serializer.data)
read_serializer = self.serializer_class(created_object)
return Response(read_serializer.data, status=status.HTTP_201_CREATED, headers=headers)
def update(self, request, *args, **kwargs):
partial = kwargs.pop("partial", False)
instance = self.get_object()
write_serializer = self.write_serializer_class(instance, data=request.data, partial=partial)
write_serializer.is_valid(raise_exception=True)
self.perform_update(write_serializer)
if getattr(instance, "_prefetched_objects_cache", None):
# If 'prefetch_related' has been applied to a queryset, we need to
# forcibly invalidate the prefetch cache on the instance.
instance._prefetched_objects_cache = {}
read_serializer = self.serializer_class(instance)
return Response(read_serializer.data)

可以这样使用:

class WriteCharacterSerializer(serializers.ModelSerializer):
class Meta:
model = Character
fields = "__all__"

class CharacterSerializer(WriteCharacterSerializer):
location = LocationSerializer(read_only=True)
factions = FactionSerializer(many=True, read_only=True)

class CharacterController(DualSerializerModelViewSet):
serializer_class = CharacterSerializer
write_serializer_class = WriteCharacterSerializer
queryset = Character.objects.all()

这对于我的用例来说非常有效,也许对其他人也有帮助。

最新更新