From 417fe1b675bd1d42518fb89a6f81547caef5b735 Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Thu, 25 Sep 2014 13:37:26 +0100 Subject: [PATCH] Partial support --- rest_framework/fields.py | 35 +++++++++++++++++++++++++---------- rest_framework/serializers.py | 6 ++++-- tests/test_serializer.py | 33 ++++++++++++++++++++++++++++++++- 3 files changed, 61 insertions(+), 13 deletions(-) diff --git a/rest_framework/fields.py b/rest_framework/fields.py index 7beccbb71..032bfd048 100644 --- a/rest_framework/fields.py +++ b/rest_framework/fields.py @@ -109,8 +109,7 @@ class Field(object): def __init__(self, read_only=False, write_only=False, required=None, default=empty, initial=None, source=None, label=None, help_text=None, style=None, - error_messages=None, validators=[], allow_null=False, - context=None): + error_messages=None, validators=[], allow_null=False): self._creation_counter = Field._creation_counter Field._creation_counter += 1 @@ -139,7 +138,6 @@ class Field(object): # These are set up by `.bind()` when the field is added to a serializer. self.field_name = None self.parent = None - self._context = {} if (context is None) else context # Collect default error message from self and parent classes messages = {} @@ -163,13 +161,6 @@ class Field(object): kwargs = copy.deepcopy(self._kwargs) return self.__class__(*args, **kwargs) - @property - def context(self): - root = self - while root.parent is not None: - root = root.parent - return root._context - def bind(self, field_name, parent): """ Setup the context for the field instance. @@ -254,6 +245,8 @@ class Field(object): """ if data is empty: if self.required: + if getattr(self.root, 'partial', False): + raise SkipField() self.fail('required') return self.get_default() @@ -304,7 +297,29 @@ class Field(object): raise AssertionError(msg) raise ValidationError(msg.format(**kwargs)) + @property + def root(self): + """ + Returns the top-level serializer for this field. + """ + root = self + while root.parent is not None: + root = root.parent + return root + + @property + def context(self): + """ + Returns the context as passed to the root serializer on initialization. + """ + return getattr(self.root, '_context', {}) + def __repr__(self): + """ + Fields are represented using their initial calling arguments. + This allows us to create descriptive representations for serializer + instances that show all the declared fields on the serializer. + """ return representation.field_repr(self) diff --git a/rest_framework/serializers.py b/rest_framework/serializers.py index 04721c7a3..b6a1898ca 100644 --- a/rest_framework/serializers.py +++ b/rest_framework/serializers.py @@ -181,8 +181,9 @@ class BindingDict(object): @six.add_metaclass(SerializerMetaclass) class Serializer(BaseSerializer): def __init__(self, *args, **kwargs): - kwargs.pop('partial', None) kwargs.pop('many', None) + self.partial = kwargs.pop('partial', False) + self._context = kwargs.pop('context', {}) super(Serializer, self).__init__(*args, **kwargs) @@ -289,7 +290,8 @@ 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.' - kwargs.pop('partial', None) + 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 b0eb4e270..5646f9940 100644 --- a/tests/test_serializer.py +++ b/tests/test_serializer.py @@ -1,3 +1,35 @@ +from rest_framework import serializers + + +# Tests for core functionality. +# ----------------------------- + +class TestSerializer: + def setup(self): + class ExampleSerializer(serializers.Serializer): + char = serializers.CharField() + integer = serializers.IntegerField() + self.Serializer = ExampleSerializer + + def test_valid_serializer(self): + serializer = self.Serializer(data={'char': 'abc', 'integer': 123}) + assert serializer.is_valid() + assert serializer.validated_data == {'char': 'abc', 'integer': 123} + assert serializer.errors == {} + + def test_invalid_serializer(self): + serializer = self.Serializer(data={'char': 'abc'}) + assert not serializer.is_valid() + assert serializer.validated_data == {} + assert serializer.errors == {'integer': ['This field is required.']} + + def test_partial_validation(self): + serializer = self.Serializer(data={'char': 'abc'}, partial=True) + assert serializer.is_valid() + assert serializer.validated_data == {'char': 'abc'} + assert serializer.errors == {} + + # # -*- coding: utf-8 -*- # from __future__ import unicode_literals # from django.db import models @@ -334,7 +366,6 @@ # Check _data attribute is cleared on `save()` # Regression test for #1116 -# — id field is not populated if `data` is accessed prior to `save()` # """ # serializer = ActionItemSerializer(self.actionitem) # self.assertIsNone(serializer.data.get('id', None), 'New instance. `id` should not be set.')