Repository: django-json-api/django-rest-framework-json-api

Issue:
Pass on instance when using polymorphic serializers
I'm trying to understand the code in `PolymorphicModelSerializer` and I wonder if this is a bug or me just not understanding something, but in `PolymorphicModelSerializer.to_internal_value`, in the end, when in inits `serializer_class` like so: `return serializer_class(data, context=self.context, partial=self.partial).to_internal_value(data)`, why is it passing `data` as the first argument?

For what I see, the init in `BaseSerializer` is expecting the first argument to be `instance` and the second one to be `data`: `def __init__(self, instance=None, data=empty, **kwargs):`. Wouldn't it make more sense for `PolymorphicModelSerializer.to_internal_value` to init `serializer_class` this other way: `return serializer_class(self.instance, data, context=self.context, partial=self.partial).to_internal_value(data)`?

That way the child serializer will keep the instance if there is any and will set the `initial_data` too.
Pass on instance when using polymorphic serializers
I'm trying to understand the code in `PolymorphicModelSerializer` and I wonder if this is a bug or me just not understanding something, but in `PolymorphicModelSerializer.to_internal_value`, in the end, when in inits `serializer_class` like so: `return serializer_class(data, context=self.context, partial=self.partial).to_internal_value(data)`, why is it passing `data` as the first argument?

For what I see, the init in `BaseSerializer` is expecting the first argument to be `instance` and the second one to be `data`: `def __init__(self, instance=None, data=empty, **kwargs):`. Wouldn't it make more sense for `PolymorphicModelSerializer.to_internal_value` to init `serializer_class` this other way: `return serializer_class(self.instance, data, context=self.context, partial=self.partial).to_internal_value(data)`?

That way the child serializer will keep the instance if there is any and will set the `initial_data` too.


Discussion:
I have looked at the code as well and this seems invalid indeed. Usually this is not noticed as it only affects serializers which internally access `self.instance` in case of patch call. However this is a bug and wouldn't need to be changed as you have suggested.

PR is welcome.
I have looked at the code as well and this seems invalid indeed. Usually this is not noticed as it only affects serializers which internally access `self.instance` in case of patch call. However this is a bug and wouldn't need to be changed as you have suggested.

PR is welcome.

Resolving patch:
diff --git a/rest_framework_json_api/serializers.py b/rest_framework_json_api/serializers.py
--- a/rest_framework_json_api/serializers.py
+++ b/rest_framework_json_api/serializers.py
@@ -352,5 +352,5 @@ def to_internal_value(self, data):
                     expected_types=', '.join(expected_types), received_type=received_type))
         serializer_class = self.get_polymorphic_serializer_for_type(received_type)
         self.__class__ = serializer_class
-        return serializer_class(data, context=self.context,
+        return serializer_class(self.instance, data, context=self.context,
                                 partial=self.partial).to_internal_value(data)


Test patch:
diff --git a/example/tests/test_serializers.py b/example/tests/test_serializers.py
--- a/example/tests/test_serializers.py
+++ b/example/tests/test_serializers.py
@@ -7,15 +7,21 @@
 from rest_framework.request import Request
 from rest_framework.test import APIRequestFactory
 
+from example.factories import ArtProjectFactory
 from rest_framework_json_api.serializers import (
     DateField,
     ModelSerializer,
-    ResourceIdentifierObjectSerializer
+    ResourceIdentifierObjectSerializer,
+    empty,
 )
 from rest_framework_json_api.utils import format_resource_type
 
 from example.models import Author, Blog, Entry
-from example.serializers import BlogSerializer
+from example.serializers import (
+    BlogSerializer,
+    ProjectSerializer,
+    ArtProjectSerializer,
+)
 
 request_factory = APIRequestFactory()
 pytestmark = pytest.mark.django_db
@@ -193,3 +199,51 @@ def test_model_serializer_with_implicit_fields(self, comment, client):
 
         assert response.status_code == 200
         assert expected == response.json()
+
+
+class TestPolymorphicModelSerializer(TestCase):
+    def setUp(self):
+        self.project = ArtProjectFactory.create()
+        self.child_init_args = {}
+
+        # Override `__init__` with our own method
+        def overridden_init(child_self, instance=None, data=empty, **kwargs):
+            """
+            Override `ArtProjectSerializer.__init__` with the same signature that
+            `BaseSerializer.__init__` has to assert that it receives the parameters
+            that `BaseSerializer` expects
+            """
+            self.child_init_args = dict(instance=instance, data=data, **kwargs)
+
+            return super(ArtProjectSerializer, child_self).__init__(
+                instance, data, **kwargs
+            )
+
+        self.child_serializer_init = ArtProjectSerializer.__init__
+        ArtProjectSerializer.__init__ = overridden_init
+
+    def tearDown(self):
+        # Restore original init to avoid affecting other tests
+        ArtProjectSerializer.__init__ = self.child_serializer_init
+
+    def test_polymorphic_model_serializer_passes_instance_to_child(self):
+        """
+        Ensure that `PolymorphicModelSerializer` is passing the instance to the
+        child serializer when initializing them
+        """
+        # Initialize a serializer that would partially update a model instance
+        initial_data = {"artist": "Mark Bishop", "type": "artProjects"}
+        parent_serializer = ProjectSerializer(
+            instance=self.project, data=initial_data, partial=True
+        )
+
+        parent_serializer.is_valid(raise_exception=True)
+
+        # Run save to force `ProjectSerializer` to init `ArtProjectSerializer`
+        parent_serializer.save()
+
+        # Assert that child init received the expected arguments
+        assert self.child_init_args["instance"] == self.project
+        assert self.child_init_args["data"] == initial_data
+        assert self.child_init_args["partial"] == parent_serializer.partial
+        assert self.child_init_args["context"] == parent_serializer.context