diff --git a/docs/topics/3.0-announcement.md b/docs/topics/3.0-announcement.md index a4e4db148..faba2d356 100644 --- a/docs/topics/3.0-announcement.md +++ b/docs/topics/3.0-announcement.md @@ -2,6 +2,13 @@ See the [Version 3.0 GitHub issue](https://github.com/tomchristie/django-rest-framework/pull/1800) for more details. +Most notable outstanding issues still to resolved on the `version-3.0` branch. + +* `FileField` and `ImageField` support. +* Forms support for serializers and in the browsable API. +* Enforcing uniqueness on `unique=True` and `unique_together` fields. +* Optimisations for serialializing primary keys. + # REST framework 3.0 The 3.0 release of Django REST framework is the result of almost four years of iteration and refinement. It comprehensively addresses some of the previous remaining design issues in serializers, fields and the generic views. diff --git a/rest_framework/serializers.py b/rest_framework/serializers.py index 86bed773b..245ec26f1 100644 --- a/rest_framework/serializers.py +++ b/rest_framework/serializers.py @@ -47,9 +47,20 @@ class BaseSerializer(Field): """ def __init__(self, instance=None, data=None, **kwargs): - super(BaseSerializer, self).__init__(**kwargs) self.instance = instance self._initial_data = data + self.partial = kwargs.pop('partial', False) + self._context = kwargs.pop('context', {}) + kwargs.pop('many', None) + super(BaseSerializer, self).__init__(**kwargs) + + def __new__(cls, *args, **kwargs): + # We override this method in order to automagically create + # `ListSerializer` classes instead when `many=True` is set. + if kwargs.pop('many', False): + kwargs['child'] = cls() + return ListSerializer(*args, **kwargs) + return super(BaseSerializer, cls).__new__(cls, *args, **kwargs) def to_internal_value(self, data): raise NotImplementedError('`to_internal_value()` must be implemented.') @@ -187,10 +198,6 @@ class BindingDict(object): @six.add_metaclass(SerializerMetaclass) class Serializer(BaseSerializer): def __init__(self, *args, **kwargs): - kwargs.pop('many', None) - self.partial = kwargs.pop('partial', False) - self._context = kwargs.pop('context', {}) - super(Serializer, self).__init__(*args, **kwargs) # Every new serializer is created with a clone of the field instances. @@ -200,14 +207,6 @@ class Serializer(BaseSerializer): for key, value in self._get_base_fields().items(): self.fields[key] = value - def __new__(cls, *args, **kwargs): - # We override this method in order to automagically create - # `ListSerializer` classes instead when `many=True` is set. - if kwargs.pop('many', False): - kwargs['child'] = cls() - return ListSerializer(*args, **kwargs) - return super(Serializer, cls).__new__(cls, *args, **kwargs) - def _get_base_fields(self): return copy.deepcopy(self._declared_fields) @@ -296,9 +295,6 @@ class ListSerializer(BaseSerializer): self.child = kwargs.pop('child', copy.deepcopy(self.child)) assert self.child is not None, '`child` is a required argument.' assert not inspect.isclass(self.child), '`child` has not been instantiated.' - self.partial = kwargs.pop('partial', False) - self._context = kwargs.pop('context', {}) - super(ListSerializer, self).__init__(*args, **kwargs) self.child.bind(field_name='', parent=self) diff --git a/tests/test_serializer.py b/tests/test_serializer.py index 256a12e68..4df1b7364 100644 --- a/tests/test_serializer.py +++ b/tests/test_serializer.py @@ -43,6 +43,64 @@ class TestSerializer: serializer.data +class TestBaseSerializer: + def setup(self): + class ExampleSerializer(serializers.BaseSerializer): + def to_representation(self, obj): + return { + 'id': obj['id'], + 'email': obj['name'] + '@' + obj['domain'] + } + + def to_internal_value(self, data): + name, domain = str(data['email']).split('@') + return { + 'id': int(data['id']), + 'name': name, + 'domain': domain, + } + + self.Serializer = ExampleSerializer + + def test_serialize_instance(self): + instance = {'id': 1, 'name': 'tom', 'domain': 'example.com'} + serializer = self.Serializer(instance) + assert serializer.data == {'id': 1, 'email': 'tom@example.com'} + + def test_serialize_list(self): + instances = [ + {'id': 1, 'name': 'tom', 'domain': 'example.com'}, + {'id': 2, 'name': 'ann', 'domain': 'example.com'}, + ] + serializer = self.Serializer(instances, many=True) + assert serializer.data == [ + {'id': 1, 'email': 'tom@example.com'}, + {'id': 2, 'email': 'ann@example.com'} + ] + + def test_validate_data(self): + data = {'id': 1, 'email': 'tom@example.com'} + serializer = self.Serializer(data=data) + assert serializer.is_valid() + assert serializer.validated_data == { + 'id': 1, + 'name': 'tom', + 'domain': 'example.com' + } + + def test_validate_list(self): + data = [ + {'id': 1, 'email': 'tom@example.com'}, + {'id': 2, 'email': 'ann@example.com'}, + ] + serializer = self.Serializer(data=data, many=True) + assert serializer.is_valid() + assert serializer.validated_data == [ + {'id': 1, 'name': 'tom', 'domain': 'example.com'}, + {'id': 2, 'name': 'ann', 'domain': 'example.com'} + ] + + class TestStarredSource: """ Tests for `source='*'` argument, which is used for nested representations.