Django Rest Framework, add object to request.data

2019-03-01 03:29发布

问题:

I'm wondering which is the best way to solve this, I have a nested serializer like this:

serializers.py:

class PaymentMethodSerializer(serializers.ModelSerializer):

    data = serializers.JSONField()
    payment_type = PaymentTypeSerializer(required=False)

Then, the view looks something like this:

class PaymentMethodView(APIView):
    def put(self, request, id):

        try:
            payment_method = PaymentMethod.objects.get(id=id)
        except ObjectDoesNotExist:
            return Response("No PaymentMethod with that id", status=status.HTTP_404_NOT_FOUND)

        payment_method_serialized = PaymentMethodSerializer(instance=payment_method, data=request.data)
        payment_type = request.data.get("payment_type", None)

        if payment_type:
            try:
                payment_method_type = PaymentType.objects.get(id=payment_type)
            except ObjectDoesNotExist:
                payment_method_type = None
        else:
            payment_method_type = None

    # Now, call is_valid()
        if payment_method_serialized.is_valid():
            payment_method_serialized.save(payment_type=payment_method_type)
            return Response(payment_method_serialized.data, status=status.HTTP_200_OK)

is_valid() returns False and this error:

{"payment_type":{"non_field_errors":["Invalid data. Expected a dictionary, but got int."]}}

I understand it, I'm giving the serializer a pk. However I don't want to create a new serializer with a PrimaryKeyRelatedField instead of the nested relationship just for this. How can I get the PaymentType that corresponds to that pk and then add that object to the request.data dictionary so that is_valid doesn't fail? is it the best way to solve this?

回答1:

Suppose payment_type is ForeignKey field in a PaymentMethod model, with null=True because of required=False.
If you provide only a pk so you don't need serializer for it field, you can just write in fields, like all other fields.

class PaymentMethodSerializer(serializers.ModelSerializer):

    data = serializers.JSONField()

    class Meta:
        model = PaymentMethod
        fields = ('data', 'payment_type')

It will accept pk or None. If you want to provide special representation for your PaymentType model, you can override a to_representation() method.

class PaymentMethodSerializer(serializers.ModelSerializer):

    ...
    def to_representation(self, instance):
        representation = super(PaymentMethodSerializer, self).to_representation(instance)
        representation['payment_type'] = PaymentTypeSerializer(instance.payment_type).data
        return representation

Now it will use PaymentTypeSerializer for representation of related PaymentType model.
You can move you payment_type validation in a serializer though. Override to_internal_value, check provided PaymentType pk and throw an exception if it wrong and then catch that exception in a view.