From 7dfc791841134e96b7632eb5902f52bc59bcc414 Mon Sep 17 00:00:00 2001 From: Sassan Haradji Date: Thu, 3 Aug 2017 17:47:40 +0430 Subject: [PATCH 1/2] Binding child should happen in bind no init It should fix several issues related to child caching wrong root, the one I was dealing with was that child of a `many=True` serializer didn't have access to context. --- rest_framework/serializers.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rest_framework/serializers.py b/rest_framework/serializers.py index a4b51ae9d..2ee566006 100644 --- a/rest_framework/serializers.py +++ b/rest_framework/serializers.py @@ -563,10 +563,10 @@ class ListSerializer(BaseSerializer): assert self.child is not None, '`child` is a required argument.' assert not inspect.isclass(self.child), '`child` has not been instantiated.' super(ListSerializer, self).__init__(*args, **kwargs) - self.child.bind(field_name='', parent=self) def bind(self, field_name, parent): super(ListSerializer, self).bind(field_name, parent) + self.child.bind(field_name='', parent=self) self.partial = self.parent.partial def get_initial(self): From 2ef3cb8b68fd62c25b32fd7a540e544702cdad4c Mon Sep 17 00:00:00 2001 From: Sassan Haradji Date: Sat, 5 Aug 2017 03:30:34 +0430 Subject: [PATCH 2/2] A test case for ruined root --- tests/test_root.py | 62 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 62 insertions(+) create mode 100644 tests/test_root.py diff --git a/tests/test_root.py b/tests/test_root.py new file mode 100644 index 000000000..079de531c --- /dev/null +++ b/tests/test_root.py @@ -0,0 +1,62 @@ +from __future__ import unicode_literals + +from django.test import TestCase, override_settings + +from rest_framework import serializers + + +class ChildSerializer(serializers.Serializer): + def __init__(self, *args, **kwargs): + super().__init__(self, *args, **kwargs) + # Not calling self.root + + def to_internal_value(self, data): + value = super().to_internal_value(data) + value['root'] = self.root + return value + + +class AnotherChildSerializer(serializers.Serializer): + def __init__(self, *args, **kwargs): + super().__init__(self, *args, **kwargs) + self.root # Calling self.root to ruin the cache + + def to_internal_value(self, data): + value = super().to_internal_value(data) + value['root'] = self.root + return value + + +class ParentSerializer(serializers.Serializer): + children = ChildSerializer(many=True) + other_children = AnotherChildSerializer(many=True) + + def __init__(self, *args, **kwargs): + super().__init__(self, *args, **kwargs) + # Not calling self.root + + def to_internal_value(self, data): + value = super().to_internal_value(data) + value['root'] = self.root + return value + + +class RootSerializer(serializers.Serializer): + parents = ParentSerializer(many=True) + + +class ListManyToManyTests(TestCase): + def setUp(self): + self.root = RootSerializer(data={ + 'parents': [{ + 'children': [{'name': 'child'}], + 'other_children': [{'name': 'child'}], + }], + }) + self.root.is_valid() + + def test_relative_hyperlinks(self): + assert self.root.root == self.root + assert self.root.validated_data['parents'][0]['root'] == self.root + assert self.root.validated_data['parents'][0]['children'][0]['root'] == self.root + assert self.root.validated_data['parents'][0]['other_children'][0]['root'] == self.root