Partial support

This commit is contained in:
Tom Christie 2014-09-25 13:37:26 +01:00
parent 3a5335f09f
commit 417fe1b675
3 changed files with 61 additions and 13 deletions

View File

@ -109,8 +109,7 @@ class Field(object):
def __init__(self, read_only=False, write_only=False, def __init__(self, read_only=False, write_only=False,
required=None, default=empty, initial=None, source=None, required=None, default=empty, initial=None, source=None,
label=None, help_text=None, style=None, label=None, help_text=None, style=None,
error_messages=None, validators=[], allow_null=False, error_messages=None, validators=[], allow_null=False):
context=None):
self._creation_counter = Field._creation_counter self._creation_counter = Field._creation_counter
Field._creation_counter += 1 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. # These are set up by `.bind()` when the field is added to a serializer.
self.field_name = None self.field_name = None
self.parent = None self.parent = None
self._context = {} if (context is None) else context
# Collect default error message from self and parent classes # Collect default error message from self and parent classes
messages = {} messages = {}
@ -163,13 +161,6 @@ class Field(object):
kwargs = copy.deepcopy(self._kwargs) kwargs = copy.deepcopy(self._kwargs)
return self.__class__(*args, **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): def bind(self, field_name, parent):
""" """
Setup the context for the field instance. Setup the context for the field instance.
@ -254,6 +245,8 @@ class Field(object):
""" """
if data is empty: if data is empty:
if self.required: if self.required:
if getattr(self.root, 'partial', False):
raise SkipField()
self.fail('required') self.fail('required')
return self.get_default() return self.get_default()
@ -304,7 +297,29 @@ class Field(object):
raise AssertionError(msg) raise AssertionError(msg)
raise ValidationError(msg.format(**kwargs)) 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): 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) return representation.field_repr(self)

View File

@ -181,8 +181,9 @@ class BindingDict(object):
@six.add_metaclass(SerializerMetaclass) @six.add_metaclass(SerializerMetaclass)
class Serializer(BaseSerializer): class Serializer(BaseSerializer):
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
kwargs.pop('partial', None)
kwargs.pop('many', None) kwargs.pop('many', None)
self.partial = kwargs.pop('partial', False)
self._context = kwargs.pop('context', {})
super(Serializer, self).__init__(*args, **kwargs) super(Serializer, self).__init__(*args, **kwargs)
@ -289,7 +290,8 @@ class ListSerializer(BaseSerializer):
self.child = kwargs.pop('child', copy.deepcopy(self.child)) self.child = kwargs.pop('child', copy.deepcopy(self.child))
assert self.child is not None, '`child` is a required argument.' assert self.child is not None, '`child` is a required argument.'
assert not inspect.isclass(self.child), '`child` has not been instantiated.' 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) super(ListSerializer, self).__init__(*args, **kwargs)
self.child.bind(field_name='', parent=self) self.child.bind(field_name='', parent=self)

View File

@ -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 -*- # # -*- coding: utf-8 -*-
# from __future__ import unicode_literals # from __future__ import unicode_literals
# from django.db import models # from django.db import models
@ -334,7 +366,6 @@
# Check _data attribute is cleared on `save()` # Check _data attribute is cleared on `save()`
# Regression test for #1116 # Regression test for #1116
# — id field is not populated if `data` is accessed prior to `save()`
# """ # """
# serializer = ActionItemSerializer(self.actionitem) # serializer = ActionItemSerializer(self.actionitem)
# self.assertIsNone(serializer.data.get('id', None), 'New instance. `id` should not be set.') # self.assertIsNone(serializer.data.get('id', None), 'New instance. `id` should not be set.')