mirror of
https://github.com/encode/django-rest-framework.git
synced 2024-11-29 13:04:03 +03:00
Serializer API restrictions.
This commit is contained in:
parent
426547c61c
commit
c6137bbf5a
|
@ -240,6 +240,12 @@ Serializer classes can also include reusable validators that are applied to the
|
||||||
|
|
||||||
For more information see the [validators documentation](validators.md).
|
For more information see the [validators documentation](validators.md).
|
||||||
|
|
||||||
|
## Accessing the initial data and instance
|
||||||
|
|
||||||
|
When passing an initial object or queryset to a serializer instance, the object will be made available as `.instance`. If no initial object is passed then the `.instance` attribute will be `None`.
|
||||||
|
|
||||||
|
When passing data to a serializer instance, the unmodified data will be made available as `.initial_data`. If the data keyword argument is not passed then the `.initial_data` attribute will not exist.
|
||||||
|
|
||||||
## Partial updates
|
## Partial updates
|
||||||
|
|
||||||
By default, serializers must be passed values for all required fields or they will raise validation errors. You can use the `partial` argument in order to allow partial updates.
|
By default, serializers must be passed values for all required fields or they will raise validation errors. You can use the `partial` argument in order to allow partial updates.
|
||||||
|
|
|
@ -79,16 +79,14 @@ class GenericAPIView(views.APIView):
|
||||||
'view': self
|
'view': self
|
||||||
}
|
}
|
||||||
|
|
||||||
def get_serializer(self, instance=None, data=None, many=False, partial=False):
|
def get_serializer(self, *args, **kwargs):
|
||||||
"""
|
"""
|
||||||
Return the serializer instance that should be used for validating and
|
Return the serializer instance that should be used for validating and
|
||||||
deserializing input, and for serializing output.
|
deserializing input, and for serializing output.
|
||||||
"""
|
"""
|
||||||
serializer_class = self.get_serializer_class()
|
serializer_class = self.get_serializer_class()
|
||||||
context = self.get_serializer_context()
|
kwargs['context'] = self.get_serializer_context()
|
||||||
return serializer_class(
|
return serializer_class(*args, **kwargs)
|
||||||
instance, data=data, many=many, partial=partial, context=context
|
|
||||||
)
|
|
||||||
|
|
||||||
def get_pagination_serializer(self, page):
|
def get_pagination_serializer(self, page):
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -544,12 +544,12 @@ class BrowsableAPIRenderer(BaseRenderer):
|
||||||
# serializer instance, rather than dynamically creating a new one.
|
# serializer instance, rather than dynamically creating a new one.
|
||||||
if request.method == method and serializer is not None:
|
if request.method == method and serializer is not None:
|
||||||
try:
|
try:
|
||||||
data = request.data
|
kwargs = {'data': request.data}
|
||||||
except ParseError:
|
except ParseError:
|
||||||
data = None
|
kwargs = {}
|
||||||
existing_serializer = serializer
|
existing_serializer = serializer
|
||||||
else:
|
else:
|
||||||
data = None
|
kwargs = {}
|
||||||
existing_serializer = None
|
existing_serializer = None
|
||||||
|
|
||||||
with override_method(view, request, method) as request:
|
with override_method(view, request, method) as request:
|
||||||
|
@ -569,11 +569,13 @@ class BrowsableAPIRenderer(BaseRenderer):
|
||||||
serializer = existing_serializer
|
serializer = existing_serializer
|
||||||
else:
|
else:
|
||||||
if method in ('PUT', 'PATCH'):
|
if method in ('PUT', 'PATCH'):
|
||||||
serializer = view.get_serializer(instance=instance, data=data)
|
serializer = view.get_serializer(instance=instance, **kwargs)
|
||||||
else:
|
else:
|
||||||
serializer = view.get_serializer(data=data)
|
serializer = view.get_serializer(**kwargs)
|
||||||
if data is not None:
|
|
||||||
|
if hasattr(serializer, 'initial_data'):
|
||||||
serializer.is_valid()
|
serializer.is_valid()
|
||||||
|
|
||||||
form_renderer = self.form_renderer_class()
|
form_renderer = self.form_renderer_class()
|
||||||
return form_renderer.render(
|
return form_renderer.render(
|
||||||
serializer.data,
|
serializer.data,
|
||||||
|
|
|
@ -58,11 +58,31 @@ class BaseSerializer(Field):
|
||||||
"""
|
"""
|
||||||
The BaseSerializer class provides a minimal class which may be used
|
The BaseSerializer class provides a minimal class which may be used
|
||||||
for writing custom serializer implementations.
|
for writing custom serializer implementations.
|
||||||
|
|
||||||
|
Note that we strongly restrict the ordering of operations/properties
|
||||||
|
that may be used on the serializer in order to enforce correct usage.
|
||||||
|
|
||||||
|
In particular, if a `data=` argument is passed then:
|
||||||
|
|
||||||
|
.is_valid() - Available.
|
||||||
|
.initial_data - Available.
|
||||||
|
.validated_data - Only available after calling `is_valid()`
|
||||||
|
.errors - Only available after calling `is_valid()`
|
||||||
|
.data - Only available after calling `is_valid()`
|
||||||
|
|
||||||
|
If a `data=` argument is not passed then:
|
||||||
|
|
||||||
|
.is_valid() - Not available.
|
||||||
|
.initial_data - Not available.
|
||||||
|
.validated_data - Not available.
|
||||||
|
.errors - Not available.
|
||||||
|
.data - Available.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, instance=None, data=None, **kwargs):
|
def __init__(self, instance=None, data=empty, **kwargs):
|
||||||
self.instance = instance
|
self.instance = instance
|
||||||
self._initial_data = data
|
if data is not empty:
|
||||||
|
self.initial_data = data
|
||||||
self.partial = kwargs.pop('partial', False)
|
self.partial = kwargs.pop('partial', False)
|
||||||
self._context = kwargs.pop('context', {})
|
self._context = kwargs.pop('context', {})
|
||||||
kwargs.pop('many', None)
|
kwargs.pop('many', None)
|
||||||
|
@ -156,9 +176,14 @@ class BaseSerializer(Field):
|
||||||
(self.__class__.__module__, self.__class__.__name__)
|
(self.__class__.__module__, self.__class__.__name__)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
assert hasattr(self, 'initial_data'), (
|
||||||
|
'Cannot call `.is_valid()` as no `data=` keyword argument was'
|
||||||
|
'passed when instantiating the serializer instance.'
|
||||||
|
)
|
||||||
|
|
||||||
if not hasattr(self, '_validated_data'):
|
if not hasattr(self, '_validated_data'):
|
||||||
try:
|
try:
|
||||||
self._validated_data = self.run_validation(self._initial_data)
|
self._validated_data = self.run_validation(self.initial_data)
|
||||||
except ValidationError as exc:
|
except ValidationError as exc:
|
||||||
self._validated_data = {}
|
self._validated_data = {}
|
||||||
self._errors = exc.detail
|
self._errors = exc.detail
|
||||||
|
@ -172,6 +197,16 @@ class BaseSerializer(Field):
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def data(self):
|
def data(self):
|
||||||
|
if hasattr(self, 'initial_data') and not hasattr(self, '_validated_data'):
|
||||||
|
msg = (
|
||||||
|
'When a serializer is passed a `data` keyword argument you '
|
||||||
|
'must call `.is_valid()` before attempting to access the '
|
||||||
|
'serialized `.data` representation.\n'
|
||||||
|
'You should either call `.is_valid()` first, '
|
||||||
|
'or access `.initial_data` instead.'
|
||||||
|
)
|
||||||
|
raise AssertionError(msg)
|
||||||
|
|
||||||
if not hasattr(self, '_data'):
|
if not hasattr(self, '_data'):
|
||||||
if self.instance is not None and not getattr(self, '_errors', None):
|
if self.instance is not None and not getattr(self, '_errors', None):
|
||||||
self._data = self.to_representation(self.instance)
|
self._data = self.to_representation(self.instance)
|
||||||
|
@ -295,11 +330,11 @@ class Serializer(BaseSerializer):
|
||||||
return getattr(getattr(self, 'Meta', None), 'validators', [])
|
return getattr(getattr(self, 'Meta', None), 'validators', [])
|
||||||
|
|
||||||
def get_initial(self):
|
def get_initial(self):
|
||||||
if self._initial_data is not None:
|
if hasattr(self, 'initial_data'):
|
||||||
return OrderedDict([
|
return OrderedDict([
|
||||||
(field_name, field.get_value(self._initial_data))
|
(field_name, field.get_value(self.initial_data))
|
||||||
for field_name, field in self.fields.items()
|
for field_name, field in self.fields.items()
|
||||||
if field.get_value(self._initial_data) is not empty
|
if field.get_value(self.initial_data) is not empty
|
||||||
and not field.read_only
|
and not field.read_only
|
||||||
])
|
])
|
||||||
|
|
||||||
|
@ -447,8 +482,8 @@ class ListSerializer(BaseSerializer):
|
||||||
self.child.bind(field_name='', parent=self)
|
self.child.bind(field_name='', parent=self)
|
||||||
|
|
||||||
def get_initial(self):
|
def get_initial(self):
|
||||||
if self._initial_data is not None:
|
if hasattr(self, 'initial_data'):
|
||||||
return self.to_representation(self._initial_data)
|
return self.to_representation(self.initial_data)
|
||||||
return []
|
return []
|
||||||
|
|
||||||
def get_value(self, dictionary):
|
def get_value(self, dictionary):
|
||||||
|
|
|
@ -22,7 +22,7 @@ class TestSimpleBoundField:
|
||||||
amount = serializers.IntegerField()
|
amount = serializers.IntegerField()
|
||||||
|
|
||||||
serializer = ExampleSerializer(data={'text': 'abc', 'amount': 123})
|
serializer = ExampleSerializer(data={'text': 'abc', 'amount': 123})
|
||||||
|
assert serializer.is_valid()
|
||||||
assert serializer['text'].value == 'abc'
|
assert serializer['text'].value == 'abc'
|
||||||
assert serializer['text'].errors is None
|
assert serializer['text'].errors is None
|
||||||
assert serializer['text'].name == 'text'
|
assert serializer['text'].name == 'text'
|
||||||
|
|
Loading…
Reference in New Issue
Block a user