diff --git a/CHANGELOG.md b/CHANGELOG.md index e9b41f3b..1318b9ba 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -20,6 +20,7 @@ any parts of the framework not mentioned in the documentation should generally b * Ensure that `409 Conflict` is returned when processing a `PATCH` request in which the resource object’s type and id do not match the server’s endpoint properly as outlined in [JSON:API](https://jsonapi.org/format/#crud-updating-responses-409) spec. * Properly return parser error when primary data is of invalid type +* Pass instance to child serializer when `PolymorphicModelSerializer` inits it in `to_internal_value` ## [3.0.0] - 2019-10-14 diff --git a/example/tests/test_serializers.py b/example/tests/test_serializers.py index e1296e2f..50a84f4d 100644 --- 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 diff --git a/rest_framework_json_api/serializers.py b/rest_framework_json_api/serializers.py index 5f7a31e8..56688bd9 100644 --- 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)