Django REST Serializer方法可写字段

时间:2016-11-11 20:10:24

标签: django rest django-rest-framework serializer

我正在阅读Django REST Framework,我有一个使用SerializerMethodField()使用getter序列化的模型。

但是,当我POST到此端点时,我希望能够设置此字段,但这不起作用,因为正如上面的文档所示,您无法写入SerializerMethodField。 Django REST中是否有任何方法可以为您定义自定义getter方法的序列化器字段以及自定义setter方法?

编辑:这是我想要做的事情的来源。客户与用户具有一对一的关系。

class ClientSerializer(serializers.ModelSerializer):
    email = serializers.SerializerMethodField()

    def create(self, validated_data):
        email = validated_data.get("email", None) # This doesn't work because email isn't passed into validated_data because it's a readonly field
        # create the client and associated user here


    def get_email(self, obj):
        return obj.user.email

    class Meta:
        model = Client
        fields = (
            "id",
            "email",
        )

8 个答案:

答案 0 :(得分:4)

您需要使用其他类型的字段:

class ClientSerializer(serializers.ModelSerializer):
    email = serializers.EmailField(source='user.email')

    def create(self, validated_data):
        # DRF will create object {"user": {"email": "inputed_value"}} in validated_date
        email = validated_data.get("user", {}).get('email')

    class Meta:
        model = Client
        fields = (
            "id",
            "email",
        )

答案 1 :(得分:4)

这是读/写序列化器方法字段:

class ReadWriteSerializerMethodField(serializers.SerializerMethodField):
    def __init__(self, method_name=None, **kwargs):
        self.method_name = method_name
        kwargs['source'] = '*'
        super(serializers.SerializerMethodField, self).__init__(**kwargs)

    def to_internal_value(self, data):
        return {self.field_name: data}

答案 2 :(得分:1)

就我而言,我需要get_*方法内部的逻辑,并且无法使用source属性获取值。所以我想出了这个领域。

class WritableSerializerMethodField(serializers.SerializerMethodField):
    def __init__(self, method_name=None, **kwargs):
        super().__init__(**kwargs)

        self.read_only = False

    def get_default(self):
        default = super().get_default()

        return {
            self.field_name: default
        }

    def to_internal_value(self, data):
        return {self.field_name: data}

答案 3 :(得分:0)

为什么不在视图中创建客户端呢?

def post(self, request, *args, **kwargs):
    data = {
        'email': request.data.get('email'),
    }

    serializer = ClientSerializer(data=data)
    if serializer.is_valid():
        email = serializer.data.get('email')
        client = Client.objects.create(email=email)
        # do other stuff

答案 4 :(得分:0)

我遇到了同样的问题,并提出了以下解决方案。

请注意,我确实需要在序列化程序中使用map <- leaflet() %>% addTiles() for( level in rtc$level){ map <- addPolylines(map, data=subset(rtc, level==level)) } # to show the map map ,因为我需要根据SerializerMethodField和某些权限填充字段,这对于request.user来说太复杂了,或其他答案中提出的其他解决方案。

解决方案是“劫持” API视图的SerializerField,并在那一点上执行特定的写操作(在我的情况下,使用普通序列器之上的另一个序列化器)。我只需要在更新中执行此操作,但是如果这是您的用例,则可能需要使用perform_update进行。

它是这样的:

perform_create

我必须承认,按照MVC模式,它并不理想,但它可以工作。

答案 5 :(得分:0)

您可以在序列化程序上覆盖save()方法,并使用self.initial_data。然后,您需要自己对该字段进行验证。

class MySerializer(serializers.ModelSerializer):

    magic_field = serializers.SerializerMethodField()

    def get_magic_field(self, instance):
        return instance.get_magic_value()

    def save(self, **kwargs):

        super().save(**kwargs)  # This creates/updates `self.instance`

        if 'magic_field' in self.initial_data:
            self.instance.update_magic_value(self.initial_data['magic_field'])

        return self.instance

答案 6 :(得分:0)

我尝试使用Guilherme Nakayama da SilvaJulio Marins的答案来解决我写给SerializerMethodField的问题。它适用于读取和更新,但不适用于创建。

所以我根据他们的回答创建了自己的WritableSerializerMethodField,它非常适合阅读,创建和编写。

class WritableSerializerMethodField(serializers.SerializerMethodField):
    def __init__(self, **kwargs):
        self.setter_method_name = kwargs.pop('setter_method_name', None)
        self.deserializer_field = kwargs.pop('deserializer_field')

        super().__init__(**kwargs)

        self.read_only = False

    def bind(self, field_name, parent):
        retval = super().bind(field_name, parent)
        if not self.setter_method_name:
            self.setter_method_name = f'set_{field_name}'

        return retval

    def get_default(self):
        default = super().get_default()

        return {
            self.field_name: default
        }

    def to_internal_value(self, data):
        value = self.deserializer_field.to_internal_value(data)
        method = getattr(self.parent, self.setter_method_name)
        return {self.field_name: self.deserializer_field.to_internal_value(method(value))}

然后我在序列化程序中使用了它

class ProjectSerializer(serializers.ModelSerializer):
    contract_price = WritableSerializerMethodField(deserializer_field=serializers.DecimalField(max_digits=12, decimal_places=2))

    def get_contract_price(self, project):
        return project.contract_price

    def set_contract_price(self, value):
        return value

答案 7 :(得分:-1)

您可以重复email字段,虽然可以使用,但可能会造成混淆

class ClientSerializer(serializers.ModelSerializer):
    email = serializers.SerializerMethodField()
    email = serializers.EmailField(required=False)

    def create(self, validated_data):
        email = validated_data.get("email", None) # This doesn't work because email isn't passed into validated_data because it's a readonly field
        # create the client and associated user here


    def get_email(self, obj):
        return obj.user.email

    class Meta:
        model = Client
        fields = (
            "id",
            "email",
        )