web-dev-qa-db-ja.com

Django RESTシリアライザーメソッドの書き込み可能フィールド

Django REST Frameworkを読んでいて、 SerializerMethodField() を使用してゲッターでシリアル化されたモデルがあります。

ただし、このエンドポイントにPOSTの場合、このフィールドも設定できるようにしたいのですが、上記のドキュメントに示されているように、に書き込むことができないため、機能しません。 SerializerMethodField。Django RESTで、カスタムゲッターメソッドとカスタムセッターメソッドを定義するシリアライザーフィールドを作成する方法はありますか?

編集:これが私がやろうとしていることの源です。クライアントはユーザーと1対1の関係にあります。

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",
        )
12
Nick

別のタイプのフィールドを使用する必要があります。

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",
        )
4
zymud

私は同じ問題を抱えていて、以下の解決策を思いつきました。

request.userと特定の権限に基づいてフィールドに入力する必要があったため、シリアライザーでSerializerMethodFieldを使用する必要がありましたが、これはSerializerFieldには複雑すぎました。他の回答で提案された他の解決策。

解決策は、APIビューのperform_updateを「ハイジャック」し、その時点で特定の書き込みを実行することでした(私の場合、通常のシリアライザーの上に別のシリアライザーを使用します)。これはアップデートで行う必要があるだけですが、これがユースケースである場合は、perform_createで行う必要があるかもしれません。

こんなふうになります:

    def perform_update(self, serializer):
        if 'myField' in self.request.data and isinstance(self.request.data['myField'], bool):
        if self.request.user == serializer.instance.owner:
            serializer.instance.myField = self.request.data['myField']
        else:
            # we toggle myField in OtherClass
            try:
                other = models.OtherClass.objects.get(...)
            except models. OtherClass.DoesNotExist:
                return Response("You don't sufficient permissions to run this action.", status=status.HTTP_401_UNAUTHORIZED)
            except models.OtherClass.MultipleObjectsReturned:  # should never happen...
                return Response("Internal Error: too many instances.", status=status.HTTP_500_INTERNAL_SERVER_ERROR)
            else:
                data = {
                    'myField': self.request.data['myField']
                    ... # filled up with OtherClass params
                }
                otherSerializer = serializers.OtherClassSerializer(other, data=data)
                if otherSerializer.is_valid():
                    otherSerializer.save()
    serializer.save()  # takes care of all the non-read-only fields 

私はそれがMVCパターンに従って理想的ではないことを認めなければなりませんが、それは機能します。

0
harrouet

代わりに、ビューでクライアントを作成しないのはなぜですか?

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
0
Patrick Falvey

シリアライザーの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
0
gitaarik