Skip to content

Pass on instance when using polymorphic serializers #764

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
58 changes: 56 additions & 2 deletions example/tests/test_serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
2 changes: 1 addition & 1 deletion rest_framework_json_api/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)