mirror of
https://github.com/encode/django-rest-framework.git
synced 2025-08-03 12:00:12 +03:00
make serializers more extensible
The general idea of this commit is to be able to pass metadata about the serialization to the serializers, so the serializers can change their behavior. In order to do this, we must be able to (1) pass additional parameters to the serializer at serialization time, (2) the serializer must be able to do something with those parameters, and (3) the serializer must be able to pass the parameters down to child serializers. (1) to_native now accepts *args, **kwargs (2) we add several extension points to to_native so subclasses can do things with the *args, **kwargs. to_native now calls _skip_field to determine whether a given field should be skipped. A subclass can subclass this method to skip certain fields. to_native now calls _get_field_value to actually get a subfield's value. _get_field_value can be subclassed to modify the *args, *kwargs passed to a child serializer, or to modify the child field's value based on the *args,**kwargs. (3) to_native calls child fields with the *args, **kwargs. However it does so in a try/except block so if it is an old field without support for *args, **kwargs, it will not fail, it simply won't support the *args,**kwargs functionality. field_to_native now passes all *args, **kwargs received into to_native. all tests pass
This commit is contained in:
parent
115fe04842
commit
b4f0fab043
|
@ -334,7 +334,22 @@ class BaseSerializer(WritableField):
|
||||||
return instance
|
return instance
|
||||||
return attrs
|
return attrs
|
||||||
|
|
||||||
def to_native(self, obj):
|
def _skip_field(self, obj, field_name, field, *args, **kwargs):
|
||||||
|
""" Whether to skip serializing the field; return True to skip"""
|
||||||
|
return field.read_only and obj is None
|
||||||
|
|
||||||
|
def _get_field_value(self, obj, field_name, field, *args, **kwargs):
|
||||||
|
"""
|
||||||
|
call subfield's field_to_native. First try with args and kwargs,
|
||||||
|
fallback to without for serializers/fields without support.
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
value = field.field_to_native(obj, field_name, *args, **kwargs)
|
||||||
|
except TypeError:
|
||||||
|
value = field.field_to_native(obj, field_name)
|
||||||
|
return value
|
||||||
|
|
||||||
|
def to_native(self, obj, *args, **kwargs):
|
||||||
"""
|
"""
|
||||||
Serialize objects -> primitives.
|
Serialize objects -> primitives.
|
||||||
"""
|
"""
|
||||||
|
@ -342,11 +357,11 @@ class BaseSerializer(WritableField):
|
||||||
ret.fields = self._dict_class()
|
ret.fields = self._dict_class()
|
||||||
|
|
||||||
for field_name, field in self.fields.items():
|
for field_name, field in self.fields.items():
|
||||||
if field.read_only and obj is None:
|
if self._skip_field(obj, field_name, field, *args, **kwargs):
|
||||||
continue
|
continue
|
||||||
field.initialize(parent=self, field_name=field_name)
|
field.initialize(parent=self, field_name=field_name)
|
||||||
key = self.get_field_key(field_name)
|
key = self.get_field_key(field_name)
|
||||||
value = field.field_to_native(obj, field_name)
|
value = self._get_field_value(obj, field_name, field, *args, **kwargs)
|
||||||
method = getattr(self, 'transform_%s' % field_name, None)
|
method = getattr(self, 'transform_%s' % field_name, None)
|
||||||
if callable(method):
|
if callable(method):
|
||||||
value = method(obj, value)
|
value = method(obj, value)
|
||||||
|
@ -381,7 +396,7 @@ class BaseSerializer(WritableField):
|
||||||
field.label = pretty_name(key)
|
field.label = pretty_name(key)
|
||||||
return field
|
return field
|
||||||
|
|
||||||
def field_to_native(self, obj, field_name):
|
def field_to_native(self, obj, field_name, *args, **kwargs):
|
||||||
"""
|
"""
|
||||||
Override default so that the serializer can be used as a nested field
|
Override default so that the serializer can be used as a nested field
|
||||||
across relationships.
|
across relationships.
|
||||||
|
@ -390,7 +405,7 @@ class BaseSerializer(WritableField):
|
||||||
return None
|
return None
|
||||||
|
|
||||||
if self.source == '*':
|
if self.source == '*':
|
||||||
return self.to_native(obj)
|
return self.to_native(obj, *args, **kwargs)
|
||||||
|
|
||||||
# Get the raw field value
|
# Get the raw field value
|
||||||
try:
|
try:
|
||||||
|
@ -405,7 +420,7 @@ class BaseSerializer(WritableField):
|
||||||
return None
|
return None
|
||||||
|
|
||||||
if is_simple_callable(getattr(value, 'all', None)):
|
if is_simple_callable(getattr(value, 'all', None)):
|
||||||
return [self.to_native(item) for item in value.all()]
|
return [self.to_native(item, *args, **kwargs) for item in value.all()]
|
||||||
|
|
||||||
if value is None:
|
if value is None:
|
||||||
return None
|
return None
|
||||||
|
@ -416,8 +431,8 @@ class BaseSerializer(WritableField):
|
||||||
many = hasattr(value, '__iter__') and not isinstance(value, (Page, dict, six.text_type))
|
many = hasattr(value, '__iter__') and not isinstance(value, (Page, dict, six.text_type))
|
||||||
|
|
||||||
if many:
|
if many:
|
||||||
return [self.to_native(item) for item in value]
|
return [self.to_native(item, *args, **kwargs) for item in value]
|
||||||
return self.to_native(value)
|
return self.to_native(value, *args, **kwargs)
|
||||||
|
|
||||||
def field_from_native(self, data, files, field_name, into):
|
def field_from_native(self, data, files, field_name, into):
|
||||||
"""
|
"""
|
||||||
|
|
Loading…
Reference in New Issue
Block a user