1

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 Answer 1

3

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.

Sign up to request clarification or add additional context in comments.

1 Comment

Thanks Ivan. Even though I didn't follow this solution. I did find the last paragraph very helpful: "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." That is why this I accepted this answer.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.