Django REST Framework 保存嵌套序列化器而不创建它们
我有一个序列化器,长这样:
class DataSetColumnSerializer(serializers.ModelSerializer):
custom_target = target_serializers.CustomTargetSerializer()
class Meta:
model = dataset_models.DataSetColumn
custom_target
使用了下面这个序列化器:
class CustomTargetSerializer(serializers.ModelSerializer):
class Meta:
model = target_models.CustomTarget
我正在返回一个对象,这个对象包含了我想要为那个 DataSetColumn
设置的 custom_target
。当我验证这个序列化器并保存它的时候:
serializer = serializers.DataSetColumnSerializer(column, data=request.DATA)
if serializer.is_valid():
serializer.save()
这个 custom_target
的序列化器会创建一个新的 CustomTarget
,而不是直接把传入的对象设置为那个 DataSetColumn
的 custom_target
。我试着把 CustomTargetSerializer
设置为只读。这样做的确不会创建新的 CustomTarget
,但它也没有把 custom_target
设置为传入的对象。
我该如何在序列化的 DataSetColumn
对象上使用 serializer.save()
,让 custom_target
设置为传入的嵌套对象,而不是从嵌套对象创建一个新的?
2 个回答
0
在我看来,最符合REST框架的解决方案是使用嵌套序列化器和一个单独的PrimaryKeyRelatedField
。这样,你可以接收CustomTarget
的嵌套对象,称为custom_target
,并且在创建时通过提供custom_target_id
作为POST参数来关联CustomTarget
对象的ID。
class DataSetColumnSerializer(serializers.ModelSerializer):
custom_target = target_serializers.CustomTargetSerializer(read_only=True)
custom_target_id = serializers.PrimaryKeyRelatedField(source='custom_target')
class Meta:
model = dataset_models.DataSetColumn
不过,这样你会得到两个属性custom_target
和custom_target_id
,这可能不是最理想的选择。作为一个通用的解决方案,可以实现一个自定义字段。
通用方法:自定义关联字段
class NestedRelatedField(serializers.PrimaryKeyRelatedField):
def __init__(self, serializer_class, *args, **kwargs):
self.serializer = serializer_class()
super(NestedRelatedField, self).__init__(queryset=serializer_class.Meta.model.objects.all(), *args, **kwargs)
def to_representation(self, instance):
return self.serializer.to_representation(instance)
使用方法如下:
class DataSetColumnSerializer(serializers.ModelSerializer):
custom_target = NestedRelatedField(CustomTargetSerializer)
0
这里有一种比较“粗糙”的方法来解决这个问题:
在 pre_save 这个处理函数里,你可以把 custom_target 重置为只包含 id。大概是这样的:
def pre_save(self, obj):
if 'custom_target' in self.request.DATA:
custom_target = self.request.DATA['custom_target']
self.request.DATA['custom_target'] = custom_target['id']
现在想不出更好或更干净的解决方案,不过如果我找到的话,会再写出来的 :)