Skip to content

Commit ffc099a

Browse files
committed
2 parents 1ba8220 + 76cfc5e commit ffc099a

File tree

5 files changed

+62
-21
lines changed

5 files changed

+62
-21
lines changed

docs/api-guide/serializers.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -240,6 +240,12 @@ Serializer classes can also include reusable validators that are applied to the
240240

241241
For more information see the [validators documentation](validators.md).
242242

243+
## Accessing the initial data and instance
244+
245+
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`.
246+
247+
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.
248+
243249
## Partial updates
244250

245251
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.

rest_framework/generics.py

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -79,16 +79,14 @@ def get_serializer_context(self):
7979
'view': self
8080
}
8181

82-
def get_serializer(self, instance=None, data=None, many=False, partial=False):
82+
def get_serializer(self, *args, **kwargs):
8383
"""
8484
Return the serializer instance that should be used for validating and
8585
deserializing input, and for serializing output.
8686
"""
8787
serializer_class = self.get_serializer_class()
88-
context = self.get_serializer_context()
89-
return serializer_class(
90-
instance, data=data, many=many, partial=partial, context=context
91-
)
88+
kwargs['context'] = self.get_serializer_context()
89+
return serializer_class(*args, **kwargs)
9290

9391
def get_pagination_serializer(self, page):
9492
"""

rest_framework/renderers.py

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -544,12 +544,12 @@ def get_rendered_html_form(self, data, view, method, request):
544544
# serializer instance, rather than dynamically creating a new one.
545545
if request.method == method and serializer is not None:
546546
try:
547-
data = request.data
547+
kwargs = {'data': request.data}
548548
except ParseError:
549-
data = None
549+
kwargs = {}
550550
existing_serializer = serializer
551551
else:
552-
data = None
552+
kwargs = {}
553553
existing_serializer = None
554554

555555
with override_method(view, request, method) as request:
@@ -569,11 +569,13 @@ def get_rendered_html_form(self, data, view, method, request):
569569
serializer = existing_serializer
570570
else:
571571
if method in ('PUT', 'PATCH'):
572-
serializer = view.get_serializer(instance=instance, data=data)
572+
serializer = view.get_serializer(instance=instance, **kwargs)
573573
else:
574-
serializer = view.get_serializer(data=data)
575-
if data is not None:
576-
serializer.is_valid()
574+
serializer = view.get_serializer(**kwargs)
575+
576+
if hasattr(serializer, 'initial_data'):
577+
serializer.is_valid()
578+
577579
form_renderer = self.form_renderer_class()
578580
return form_renderer.render(
579581
serializer.data,

rest_framework/serializers.py

Lines changed: 43 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -58,11 +58,31 @@ class BaseSerializer(Field):
5858
"""
5959
The BaseSerializer class provides a minimal class which may be used
6060
for writing custom serializer implementations.
61+
62+
Note that we strongly restrict the ordering of operations/properties
63+
that may be used on the serializer in order to enforce correct usage.
64+
65+
In particular, if a `data=` argument is passed then:
66+
67+
.is_valid() - Available.
68+
.initial_data - Available.
69+
.validated_data - Only available after calling `is_valid()`
70+
.errors - Only available after calling `is_valid()`
71+
.data - Only available after calling `is_valid()`
72+
73+
If a `data=` argument is not passed then:
74+
75+
.is_valid() - Not available.
76+
.initial_data - Not available.
77+
.validated_data - Not available.
78+
.errors - Not available.
79+
.data - Available.
6180
"""
6281

63-
def __init__(self, instance=None, data=None, **kwargs):
82+
def __init__(self, instance=None, data=empty, **kwargs):
6483
self.instance = instance
65-
self._initial_data = data
84+
if data is not empty:
85+
self.initial_data = data
6686
self.partial = kwargs.pop('partial', False)
6787
self._context = kwargs.pop('context', {})
6888
kwargs.pop('many', None)
@@ -156,9 +176,14 @@ def is_valid(self, raise_exception=False):
156176
(self.__class__.__module__, self.__class__.__name__)
157177
)
158178

179+
assert hasattr(self, 'initial_data'), (
180+
'Cannot call `.is_valid()` as no `data=` keyword argument was'
181+
'passed when instantiating the serializer instance.'
182+
)
183+
159184
if not hasattr(self, '_validated_data'):
160185
try:
161-
self._validated_data = self.run_validation(self._initial_data)
186+
self._validated_data = self.run_validation(self.initial_data)
162187
except ValidationError as exc:
163188
self._validated_data = {}
164189
self._errors = exc.detail
@@ -172,6 +197,16 @@ def is_valid(self, raise_exception=False):
172197

173198
@property
174199
def data(self):
200+
if hasattr(self, 'initial_data') and not hasattr(self, '_validated_data'):
201+
msg = (
202+
'When a serializer is passed a `data` keyword argument you '
203+
'must call `.is_valid()` before attempting to access the '
204+
'serialized `.data` representation.\n'
205+
'You should either call `.is_valid()` first, '
206+
'or access `.initial_data` instead.'
207+
)
208+
raise AssertionError(msg)
209+
175210
if not hasattr(self, '_data'):
176211
if self.instance is not None and not getattr(self, '_errors', None):
177212
self._data = self.to_representation(self.instance)
@@ -295,11 +330,11 @@ def get_validators(self):
295330
return getattr(getattr(self, 'Meta', None), 'validators', [])
296331

297332
def get_initial(self):
298-
if self._initial_data is not None:
333+
if hasattr(self, 'initial_data'):
299334
return OrderedDict([
300-
(field_name, field.get_value(self._initial_data))
335+
(field_name, field.get_value(self.initial_data))
301336
for field_name, field in self.fields.items()
302-
if field.get_value(self._initial_data) is not empty
337+
if field.get_value(self.initial_data) is not empty
303338
and not field.read_only
304339
])
305340

@@ -447,8 +482,8 @@ def __init__(self, *args, **kwargs):
447482
self.child.bind(field_name='', parent=self)
448483

449484
def get_initial(self):
450-
if self._initial_data is not None:
451-
return self.to_representation(self._initial_data)
485+
if hasattr(self, 'initial_data'):
486+
return self.to_representation(self.initial_data)
452487
return []
453488

454489
def get_value(self, dictionary):

tests/test_bound_fields.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ class ExampleSerializer(serializers.Serializer):
2222
amount = serializers.IntegerField()
2323

2424
serializer = ExampleSerializer(data={'text': 'abc', 'amount': 123})
25-
25+
assert serializer.is_valid()
2626
assert serializer['text'].value == 'abc'
2727
assert serializer['text'].errors is None
2828
assert serializer['text'].name == 'text'

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy