Django REST Framework 保存嵌套序列化器而不创建它们

2 投票
2 回答
1214 浏览
提问于 2025-04-18 16:54

我有一个序列化器,长这样:

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,而不是直接把传入的对象设置为那个 DataSetColumncustom_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_targetcustom_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']

现在想不出更好或更干净的解决方案,不过如果我找到的话,会再写出来的 :)

撰写回答