我试图想出一种简单的方法,使我的序列化器和视图按我想要的方式工作,同时防止一堆手动样板。
我的简化模型: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)
所以:角色可以有一个位置和多个派系。
现在,当我得到一个字符时,我希望这些子模型(Location
和Faction
)被扩展为它们的完整表示,所以我的序列化器看起来像这样:
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
子类的create
和update
方法吗?我希望我能稍微修改一下序列化器本身,只在响应上应用那些location
和factions
字段序列化器,而不是在请求上。
您可以覆盖序列化器的create
和update
方法来使用主键。
这意味着我们首先从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()
这对于我的用例来说非常有效,也许对其他人也有帮助。