2013-04-25 15:47:34 +04:00
|
|
|
"""
|
|
|
|
Serializers and ModelSerializers are similar to Forms and ModelForms.
|
|
|
|
Unlike forms, they are not constrained to dealing with HTML output, and
|
|
|
|
form encoded input.
|
|
|
|
|
|
|
|
Serialization in REST framework is a two-phase process:
|
|
|
|
|
|
|
|
1. Serializers marshal between complex types like model instances, and
|
2013-11-11 13:54:30 +04:00
|
|
|
python primitives.
|
|
|
|
2. The process of marshalling between python primitives and request and
|
2013-04-25 15:47:34 +04:00
|
|
|
response content is handled by parsers and renderers.
|
|
|
|
"""
|
2014-10-10 17:16:09 +04:00
|
|
|
from django.core.exceptions import ImproperlyConfigured
|
2014-12-01 13:48:45 +03:00
|
|
|
from django.core.exceptions import ValidationError as DjangoValidationError
|
2012-10-02 19:16:49 +04:00
|
|
|
from django.db import models
|
2014-10-28 19:21:49 +03:00
|
|
|
from django.db.models.fields import FieldDoesNotExist
|
2014-08-19 20:06:55 +04:00
|
|
|
from django.utils import six
|
2014-11-03 17:01:02 +03:00
|
|
|
from django.utils.translation import ugettext_lazy as _
|
2014-11-06 15:00:30 +03:00
|
|
|
from rest_framework.compat import OrderedDict
|
2014-10-17 16:23:14 +04:00
|
|
|
from rest_framework.exceptions import ValidationError
|
2014-09-05 19:29:46 +04:00
|
|
|
from rest_framework.fields import empty, set_value, Field, SkipField
|
2014-01-14 12:38:49 +04:00
|
|
|
from rest_framework.settings import api_settings
|
2014-09-15 12:50:51 +04:00
|
|
|
from rest_framework.utils import html, model_meta, representation
|
2014-09-18 14:20:56 +04:00
|
|
|
from rest_framework.utils.field_mapping import (
|
|
|
|
get_url_kwargs, get_field_kwargs,
|
|
|
|
get_relation_kwargs, get_nested_relation_kwargs,
|
2014-09-24 17:09:49 +04:00
|
|
|
ClassLookupDict
|
2014-09-18 14:20:56 +04:00
|
|
|
)
|
2014-11-07 13:13:46 +03:00
|
|
|
from rest_framework.utils.serializer_helpers import (
|
|
|
|
ReturnDict, ReturnList, BoundField, NestedBoundField, BindingDict
|
|
|
|
)
|
2014-10-22 16:30:28 +04:00
|
|
|
from rest_framework.validators import (
|
2014-10-22 19:29:09 +04:00
|
|
|
UniqueForDateValidator, UniqueForMonthValidator, UniqueForYearValidator,
|
|
|
|
UniqueTogetherValidator
|
2014-10-22 16:30:28 +04:00
|
|
|
)
|
2014-08-29 19:46:26 +04:00
|
|
|
import copy
|
2014-09-19 19:43:13 +04:00
|
|
|
import inspect
|
2014-10-08 19:09:37 +04:00
|
|
|
import warnings
|
2012-11-05 14:56:30 +04:00
|
|
|
|
|
|
|
# Note: We do the following so that users of the framework can use this style:
|
|
|
|
#
|
|
|
|
# example_field = serializers.CharField(...)
|
|
|
|
#
|
2013-05-28 18:09:23 +04:00
|
|
|
# This helps keep the separation between model fields, form fields, and
|
2012-11-05 14:56:30 +04:00
|
|
|
# serializer fields more explicit.
|
|
|
|
|
2014-05-17 02:05:33 +04:00
|
|
|
from rest_framework.relations import * # NOQA
|
|
|
|
from rest_framework.fields import * # NOQA
|
2012-09-20 16:06:27 +04:00
|
|
|
|
2014-11-14 00:11:13 +03:00
|
|
|
|
|
|
|
# We assume that 'validators' are intended for the child serializer,
|
|
|
|
# rather than the parent serializer.
|
2014-11-05 18:23:13 +03:00
|
|
|
LIST_SERIALIZER_KWARGS = (
|
|
|
|
'read_only', 'write_only', 'required', 'default', 'initial', 'source',
|
|
|
|
'label', 'help_text', 'style', 'error_messages',
|
|
|
|
'instance', 'data', 'partial', 'context'
|
|
|
|
)
|
|
|
|
|
2012-09-20 16:06:27 +04:00
|
|
|
|
2014-10-02 19:24:24 +04:00
|
|
|
# BaseSerializer
|
|
|
|
# --------------
|
2013-10-02 16:45:35 +04:00
|
|
|
|
2014-08-29 19:46:26 +04:00
|
|
|
class BaseSerializer(Field):
|
2014-09-05 19:29:46 +04:00
|
|
|
"""
|
|
|
|
The BaseSerializer class provides a minimal class which may be used
|
|
|
|
for writing custom serializer implementations.
|
|
|
|
"""
|
2014-08-29 19:46:26 +04:00
|
|
|
def __init__(self, instance=None, data=None, **kwargs):
|
|
|
|
self.instance = instance
|
|
|
|
self._initial_data = data
|
2014-09-26 15:48:20 +04:00
|
|
|
self.partial = kwargs.pop('partial', False)
|
|
|
|
self._context = kwargs.pop('context', {})
|
|
|
|
kwargs.pop('many', None)
|
|
|
|
super(BaseSerializer, self).__init__(**kwargs)
|
|
|
|
|
|
|
|
def __new__(cls, *args, **kwargs):
|
|
|
|
# We override this method in order to automagically create
|
|
|
|
# `ListSerializer` classes instead when `many=True` is set.
|
|
|
|
if kwargs.pop('many', False):
|
2014-11-14 00:11:13 +03:00
|
|
|
return cls.many_init(*args, **kwargs)
|
2014-09-26 15:48:20 +04:00
|
|
|
return super(BaseSerializer, cls).__new__(cls, *args, **kwargs)
|
2013-10-02 16:45:35 +04:00
|
|
|
|
2014-11-14 00:11:13 +03:00
|
|
|
@classmethod
|
|
|
|
def many_init(cls, *args, **kwargs):
|
|
|
|
"""
|
|
|
|
This method implements the creation of a `ListSerializer` parent
|
|
|
|
class when `many=True` is used. You can customize it if you need to
|
|
|
|
control which keyword arguments are passed to the parent, and
|
|
|
|
which are passed to the child.
|
2014-11-25 13:39:58 +03:00
|
|
|
|
|
|
|
Note that we're over-cautious in passing most arguments to both parent
|
|
|
|
and child classes in order to try to cover the general case. If you're
|
|
|
|
overriding this method you'll probably want something much simpler, eg:
|
|
|
|
|
|
|
|
@classmethod
|
|
|
|
def many_init(cls, *args, **kwargs):
|
|
|
|
kwargs['child'] = cls()
|
|
|
|
return CustomListSerializer(*args, **kwargs)
|
2014-11-14 00:11:13 +03:00
|
|
|
"""
|
|
|
|
child_serializer = cls(*args, **kwargs)
|
|
|
|
list_kwargs = {'child': child_serializer}
|
|
|
|
list_kwargs.update(dict([
|
|
|
|
(key, value) for key, value in kwargs.items()
|
|
|
|
if key in LIST_SERIALIZER_KWARGS
|
|
|
|
]))
|
2014-11-28 12:56:44 +03:00
|
|
|
meta = getattr(cls, 'Meta', None)
|
|
|
|
list_serializer_class = getattr(meta, 'list_serializer_class', ListSerializer)
|
|
|
|
return list_serializer_class(*args, **list_kwargs)
|
2014-11-14 00:11:13 +03:00
|
|
|
|
2014-09-12 12:49:35 +04:00
|
|
|
def to_internal_value(self, data):
|
|
|
|
raise NotImplementedError('`to_internal_value()` must be implemented.')
|
2013-04-18 21:28:20 +04:00
|
|
|
|
2014-09-12 12:49:35 +04:00
|
|
|
def to_representation(self, instance):
|
|
|
|
raise NotImplementedError('`to_representation()` must be implemented.')
|
2013-11-08 16:12:40 +04:00
|
|
|
|
2014-09-26 13:46:52 +04:00
|
|
|
def update(self, instance, validated_data):
|
2014-09-05 19:29:46 +04:00
|
|
|
raise NotImplementedError('`update()` must be implemented.')
|
2013-03-12 22:35:20 +04:00
|
|
|
|
2014-09-26 13:46:52 +04:00
|
|
|
def create(self, validated_data):
|
2014-09-05 19:29:46 +04:00
|
|
|
raise NotImplementedError('`create()` must be implemented.')
|
2013-03-12 22:35:20 +04:00
|
|
|
|
2014-10-03 16:42:06 +04:00
|
|
|
def save(self, **kwargs):
|
2014-11-18 17:49:00 +03:00
|
|
|
assert not hasattr(self, 'save_object'), (
|
|
|
|
'Serializer `%s.%s` has old-style version 2 `.save_object()` '
|
|
|
|
'that is no longer compatible with REST framework 3. '
|
|
|
|
'Use the new-style `.create()` and `.update()` methods instead.' %
|
|
|
|
(self.__class__.__module__, self.__class__.__name__)
|
|
|
|
)
|
|
|
|
|
2014-12-02 12:27:40 +03:00
|
|
|
assert hasattr(self, '_errors'), (
|
|
|
|
'You must call `.is_valid()` before calling `.save()`.'
|
|
|
|
)
|
|
|
|
|
|
|
|
assert not self.errors, (
|
|
|
|
'You cannot call `.save()` on a serializer with invalid data.'
|
|
|
|
)
|
|
|
|
|
2014-11-06 13:34:59 +03:00
|
|
|
validated_data = dict(
|
|
|
|
list(self.validated_data.items()) +
|
|
|
|
list(kwargs.items())
|
|
|
|
)
|
2013-03-12 22:35:20 +04:00
|
|
|
|
2014-08-29 19:46:26 +04:00
|
|
|
if self.instance is not None:
|
2014-10-08 15:17:30 +04:00
|
|
|
self.instance = self.update(self.instance, validated_data)
|
|
|
|
assert self.instance is not None, (
|
|
|
|
'`update()` did not return an object instance.'
|
|
|
|
)
|
2013-03-13 07:59:25 +04:00
|
|
|
else:
|
2014-09-26 13:46:52 +04:00
|
|
|
self.instance = self.create(validated_data)
|
2014-09-26 14:56:29 +04:00
|
|
|
assert self.instance is not None, (
|
|
|
|
'`create()` did not return an object instance.'
|
|
|
|
)
|
2013-03-12 17:33:02 +04:00
|
|
|
|
2014-08-29 19:46:26 +04:00
|
|
|
return self.instance
|
2013-03-12 17:33:02 +04:00
|
|
|
|
2014-09-05 19:29:46 +04:00
|
|
|
def is_valid(self, raise_exception=False):
|
2014-10-17 16:23:14 +04:00
|
|
|
assert not hasattr(self, 'restore_object'), (
|
2014-10-22 13:32:32 +04:00
|
|
|
'Serializer `%s.%s` has old-style version 2 `.restore_object()` '
|
2014-10-17 16:23:14 +04:00
|
|
|
'that is no longer compatible with REST framework 3. '
|
|
|
|
'Use the new-style `.create()` and `.update()` methods instead.' %
|
2014-10-22 13:32:32 +04:00
|
|
|
(self.__class__.__module__, self.__class__.__name__)
|
2014-10-17 16:23:14 +04:00
|
|
|
)
|
|
|
|
|
2014-09-05 19:29:46 +04:00
|
|
|
if not hasattr(self, '_validated_data'):
|
|
|
|
try:
|
2014-09-29 14:23:02 +04:00
|
|
|
self._validated_data = self.run_validation(self._initial_data)
|
2014-10-17 16:23:14 +04:00
|
|
|
except ValidationError as exc:
|
2014-09-05 19:29:46 +04:00
|
|
|
self._validated_data = {}
|
2014-10-10 17:16:09 +04:00
|
|
|
self._errors = exc.detail
|
2014-09-05 19:29:46 +04:00
|
|
|
else:
|
|
|
|
self._errors = {}
|
|
|
|
|
|
|
|
if self._errors and raise_exception:
|
2014-10-17 16:23:14 +04:00
|
|
|
raise ValidationError(self._errors)
|
2014-09-05 19:29:46 +04:00
|
|
|
|
|
|
|
return not bool(self._errors)
|
2012-09-20 16:06:27 +04:00
|
|
|
|
2014-08-29 19:46:26 +04:00
|
|
|
@property
|
|
|
|
def data(self):
|
|
|
|
if not hasattr(self, '_data'):
|
2014-10-15 18:13:28 +04:00
|
|
|
if self.instance is not None and not getattr(self, '_errors', None):
|
2014-09-12 12:49:35 +04:00
|
|
|
self._data = self.to_representation(self.instance)
|
2014-10-17 16:23:14 +04:00
|
|
|
elif hasattr(self, '_validated_data') and not getattr(self, '_errors', None):
|
|
|
|
self._data = self.to_representation(self.validated_data)
|
2014-08-29 19:46:26 +04:00
|
|
|
else:
|
|
|
|
self._data = self.get_initial()
|
|
|
|
return self._data
|
2012-09-20 16:06:27 +04:00
|
|
|
|
2014-08-29 19:46:26 +04:00
|
|
|
@property
|
|
|
|
def errors(self):
|
|
|
|
if not hasattr(self, '_errors'):
|
|
|
|
msg = 'You must call `.is_valid()` before accessing `.errors`.'
|
|
|
|
raise AssertionError(msg)
|
|
|
|
return self._errors
|
2012-09-20 16:06:27 +04:00
|
|
|
|
2014-08-29 19:46:26 +04:00
|
|
|
@property
|
|
|
|
def validated_data(self):
|
|
|
|
if not hasattr(self, '_validated_data'):
|
|
|
|
msg = 'You must call `.is_valid()` before accessing `.validated_data`.'
|
|
|
|
raise AssertionError(msg)
|
|
|
|
return self._validated_data
|
2012-09-20 16:06:27 +04:00
|
|
|
|
|
|
|
|
2014-10-02 19:24:24 +04:00
|
|
|
# Serializer & ListSerializer classes
|
|
|
|
# -----------------------------------
|
|
|
|
|
|
|
|
class SerializerMetaclass(type):
|
|
|
|
"""
|
|
|
|
This metaclass sets a dictionary named `base_fields` on the class.
|
|
|
|
|
|
|
|
Any instances of `Field` included as attributes on either the class
|
|
|
|
or on any of its superclasses will be include in the
|
|
|
|
`base_fields` dictionary.
|
|
|
|
"""
|
|
|
|
|
|
|
|
@classmethod
|
|
|
|
def _get_declared_fields(cls, bases, attrs):
|
|
|
|
fields = [(field_name, attrs.pop(field_name))
|
|
|
|
for field_name, obj in list(attrs.items())
|
|
|
|
if isinstance(obj, Field)]
|
|
|
|
fields.sort(key=lambda x: x[1]._creation_counter)
|
|
|
|
|
|
|
|
# If this class is subclassing another Serializer, add that Serializer's
|
|
|
|
# fields. Note that we loop over the bases in *reverse*. This is necessary
|
|
|
|
# in order to maintain the correct order of fields.
|
|
|
|
for base in bases[::-1]:
|
|
|
|
if hasattr(base, '_declared_fields'):
|
|
|
|
fields = list(base._declared_fields.items()) + fields
|
|
|
|
|
2014-11-06 15:00:30 +03:00
|
|
|
return OrderedDict(fields)
|
2014-10-02 19:24:24 +04:00
|
|
|
|
|
|
|
def __new__(cls, name, bases, attrs):
|
|
|
|
attrs['_declared_fields'] = cls._get_declared_fields(bases, attrs)
|
|
|
|
return super(SerializerMetaclass, cls).__new__(cls, name, bases, attrs)
|
|
|
|
|
|
|
|
|
2014-08-29 19:46:26 +04:00
|
|
|
@six.add_metaclass(SerializerMetaclass)
|
|
|
|
class Serializer(BaseSerializer):
|
2014-11-03 17:01:02 +03:00
|
|
|
default_error_messages = {
|
|
|
|
'invalid': _('Invalid data. Expected a dictionary, but got {datatype}.')
|
|
|
|
}
|
|
|
|
|
2014-10-31 19:38:39 +03:00
|
|
|
@property
|
|
|
|
def fields(self):
|
2014-11-07 13:13:46 +03:00
|
|
|
"""
|
|
|
|
A dictionary of {field_name: field_instance}.
|
|
|
|
"""
|
|
|
|
# `fields` is evalutated lazily. We do this to ensure that we don't
|
|
|
|
# have issues importing modules that use ModelSerializers as fields,
|
|
|
|
# even if Django's app-loading stage has not yet run.
|
2014-10-31 19:38:39 +03:00
|
|
|
if not hasattr(self, '_fields'):
|
|
|
|
self._fields = BindingDict(self)
|
|
|
|
for key, value in self.get_fields().items():
|
|
|
|
self._fields[key] = value
|
|
|
|
return self._fields
|
|
|
|
|
|
|
|
def get_fields(self):
|
2014-11-07 13:13:46 +03:00
|
|
|
"""
|
|
|
|
Returns a dictionary of {field_name: field_instance}.
|
|
|
|
"""
|
2014-08-29 19:46:26 +04:00
|
|
|
# Every new serializer is created with a clone of the field instances.
|
|
|
|
# This allows users to dynamically modify the fields on a serializer
|
|
|
|
# instance without affecting every other serializer class.
|
2014-09-18 14:20:56 +04:00
|
|
|
return copy.deepcopy(self._declared_fields)
|
2012-09-20 16:06:27 +04:00
|
|
|
|
2014-10-31 19:38:39 +03:00
|
|
|
def get_validators(self):
|
2014-11-07 13:13:46 +03:00
|
|
|
"""
|
|
|
|
Returns a list of validator callables.
|
|
|
|
"""
|
|
|
|
# Used by the lazily-evaluated `validators` property.
|
2014-10-31 19:38:39 +03:00
|
|
|
return getattr(getattr(self, 'Meta', None), 'validators', [])
|
|
|
|
|
2014-08-29 19:46:26 +04:00
|
|
|
def get_initial(self):
|
2014-10-02 19:24:24 +04:00
|
|
|
if self._initial_data is not None:
|
2014-11-14 02:05:44 +03:00
|
|
|
return OrderedDict([
|
2014-11-07 17:13:50 +03:00
|
|
|
(field_name, field.get_value(self._initial_data))
|
|
|
|
for field_name, field in self.fields.items()
|
|
|
|
if field.get_value(self._initial_data) is not empty
|
2014-11-07 18:38:27 +03:00
|
|
|
and not field.read_only
|
2014-11-14 02:05:44 +03:00
|
|
|
])
|
2014-10-02 19:24:24 +04:00
|
|
|
|
2014-11-14 02:05:44 +03:00
|
|
|
return OrderedDict([
|
2014-09-10 19:57:22 +04:00
|
|
|
(field.field_name, field.get_initial())
|
2014-08-29 19:46:26 +04:00
|
|
|
for field in self.fields.values()
|
2014-11-07 18:38:27 +03:00
|
|
|
if not field.read_only
|
2014-11-14 02:05:44 +03:00
|
|
|
])
|
2012-10-24 12:28:10 +04:00
|
|
|
|
2014-08-29 19:46:26 +04:00
|
|
|
def get_value(self, dictionary):
|
|
|
|
# We override the default field access in order to support
|
|
|
|
# nested HTML forms.
|
|
|
|
if html.is_html_input(dictionary):
|
|
|
|
return html.parse_html_dict(dictionary, prefix=self.field_name)
|
|
|
|
return dictionary.get(self.field_name, empty)
|
2012-10-24 14:39:17 +04:00
|
|
|
|
2014-09-29 14:23:02 +04:00
|
|
|
def run_validation(self, data=empty):
|
2012-09-20 16:06:27 +04:00
|
|
|
"""
|
2014-09-29 14:23:02 +04:00
|
|
|
We override the default `run_validation`, because the validation
|
|
|
|
performed by validators and the `.validate()` method should
|
|
|
|
be coerced into an error dictionary with a 'non_fields_error' key.
|
2012-09-20 16:06:27 +04:00
|
|
|
"""
|
2014-09-29 14:23:02 +04:00
|
|
|
if data is empty:
|
|
|
|
if getattr(self.root, 'partial', False):
|
|
|
|
raise SkipField()
|
|
|
|
if self.required:
|
|
|
|
self.fail('required')
|
|
|
|
return self.get_default()
|
|
|
|
|
|
|
|
if data is None:
|
|
|
|
if not self.allow_null:
|
|
|
|
self.fail('null')
|
|
|
|
return None
|
|
|
|
|
2014-09-05 19:29:46 +04:00
|
|
|
if not isinstance(data, dict):
|
2014-11-03 17:01:02 +03:00
|
|
|
message = self.error_messages['invalid'].format(
|
|
|
|
datatype=type(data).__name__
|
|
|
|
)
|
2014-10-17 16:23:14 +04:00
|
|
|
raise ValidationError({
|
2014-11-03 17:01:02 +03:00
|
|
|
api_settings.NON_FIELD_ERRORS_KEY: [message]
|
2014-09-12 13:21:35 +04:00
|
|
|
})
|
2014-09-05 19:29:46 +04:00
|
|
|
|
2014-09-29 14:23:02 +04:00
|
|
|
value = self.to_internal_value(data)
|
|
|
|
try:
|
|
|
|
self.run_validators(value)
|
2014-10-08 15:36:28 +04:00
|
|
|
value = self.validate(value)
|
|
|
|
assert value is not None, '.validate() should return the validated data'
|
2014-10-17 16:23:14 +04:00
|
|
|
except ValidationError as exc:
|
2014-10-22 13:32:32 +04:00
|
|
|
if isinstance(exc.detail, dict):
|
|
|
|
# .validate() errors may be a dict, in which case, use
|
|
|
|
# standard {key: list of values} style.
|
|
|
|
raise ValidationError(dict([
|
|
|
|
(key, value if isinstance(value, list) else [value])
|
|
|
|
for key, value in exc.detail.items()
|
|
|
|
]))
|
|
|
|
elif isinstance(exc.detail, list):
|
|
|
|
raise ValidationError({
|
|
|
|
api_settings.NON_FIELD_ERRORS_KEY: exc.detail
|
|
|
|
})
|
|
|
|
else:
|
|
|
|
raise ValidationError({
|
|
|
|
api_settings.NON_FIELD_ERRORS_KEY: [exc.detail]
|
|
|
|
})
|
2014-12-01 13:48:45 +03:00
|
|
|
except DjangoValidationError as exc:
|
|
|
|
# Normally you should raise `serializers.ValidationError`
|
|
|
|
# inside your codebase, but we handle Django's validation
|
|
|
|
# exception class as well for simpler compat.
|
|
|
|
# Eg. Calling Model.clean() explictily inside Serializer.validate()
|
|
|
|
raise ValidationError({
|
|
|
|
api_settings.NON_FIELD_ERRORS_KEY: list(exc.messages)
|
|
|
|
})
|
2014-10-22 13:32:32 +04:00
|
|
|
|
2014-09-29 14:23:02 +04:00
|
|
|
return value
|
|
|
|
|
|
|
|
def to_internal_value(self, data):
|
|
|
|
"""
|
|
|
|
Dict of native values <- Dict of primitive datatypes.
|
|
|
|
"""
|
2014-11-14 02:05:44 +03:00
|
|
|
ret = OrderedDict()
|
|
|
|
errors = OrderedDict()
|
2014-10-28 19:21:49 +03:00
|
|
|
fields = [
|
|
|
|
field for field in self.fields.values()
|
|
|
|
if (not field.read_only) or (field.default is not empty)
|
|
|
|
]
|
2012-09-20 16:06:27 +04:00
|
|
|
|
2014-08-29 19:46:26 +04:00
|
|
|
for field in fields:
|
2014-09-05 19:29:46 +04:00
|
|
|
validate_method = getattr(self, 'validate_' + field.field_name, None)
|
2014-08-29 19:46:26 +04:00
|
|
|
primitive_value = field.get_value(data)
|
|
|
|
try:
|
2014-09-12 12:49:35 +04:00
|
|
|
validated_value = field.run_validation(primitive_value)
|
2014-09-05 19:29:46 +04:00
|
|
|
if validate_method is not None:
|
|
|
|
validated_value = validate_method(validated_value)
|
2014-10-17 16:23:14 +04:00
|
|
|
except ValidationError as exc:
|
2014-10-10 17:16:09 +04:00
|
|
|
errors[field.field_name] = exc.detail
|
2014-12-01 13:48:45 +03:00
|
|
|
except DjangoValidationError as exc:
|
|
|
|
errors[field.field_name] = list(exc.messages)
|
2014-08-29 19:46:26 +04:00
|
|
|
except SkipField:
|
|
|
|
pass
|
|
|
|
else:
|
|
|
|
set_value(ret, field.source_attrs, validated_value)
|
2013-01-31 21:06:23 +04:00
|
|
|
|
2014-08-29 19:46:26 +04:00
|
|
|
if errors:
|
2014-10-17 16:23:14 +04:00
|
|
|
raise ValidationError(errors)
|
2013-10-02 19:13:34 +04:00
|
|
|
|
2014-09-29 14:23:02 +04:00
|
|
|
return ret
|
2012-09-20 16:06:27 +04:00
|
|
|
|
2014-09-12 12:49:35 +04:00
|
|
|
def to_representation(self, instance):
|
2012-10-22 18:12:25 +04:00
|
|
|
"""
|
2014-08-29 19:46:26 +04:00
|
|
|
Object instance -> Dict of primitive datatypes.
|
2012-10-22 18:12:25 +04:00
|
|
|
"""
|
2014-11-14 02:05:44 +03:00
|
|
|
ret = OrderedDict()
|
2014-08-29 19:46:26 +04:00
|
|
|
fields = [field for field in self.fields.values() if not field.write_only]
|
2014-01-14 15:25:44 +04:00
|
|
|
|
2014-08-29 19:46:26 +04:00
|
|
|
for field in fields:
|
2014-10-16 23:45:36 +04:00
|
|
|
attribute = field.get_attribute(instance)
|
|
|
|
if attribute is None:
|
2014-11-27 19:40:58 +03:00
|
|
|
ret[field.field_name] = None
|
2014-10-16 23:45:36 +04:00
|
|
|
else:
|
2014-11-27 19:40:58 +03:00
|
|
|
ret[field.field_name] = field.to_representation(attribute)
|
2013-01-27 00:54:03 +04:00
|
|
|
|
2014-08-29 19:46:26 +04:00
|
|
|
return ret
|
2012-11-20 13:41:36 +04:00
|
|
|
|
2014-09-02 20:41:23 +04:00
|
|
|
def validate(self, attrs):
|
|
|
|
return attrs
|
|
|
|
|
2014-10-09 18:11:19 +04:00
|
|
|
def __repr__(self):
|
|
|
|
return representation.serializer_repr(self, indent=1)
|
|
|
|
|
|
|
|
# The following are used for accessing `BoundField` instances on the
|
|
|
|
# serializer, for the purposes of presenting a form-like API onto the
|
|
|
|
# field values and field errors.
|
|
|
|
|
2014-08-29 19:46:26 +04:00
|
|
|
def __iter__(self):
|
|
|
|
for field in self.fields.values():
|
2014-10-09 18:11:19 +04:00
|
|
|
yield self[field.field_name]
|
2012-10-22 18:12:25 +04:00
|
|
|
|
2014-10-09 18:11:19 +04:00
|
|
|
def __getitem__(self, key):
|
|
|
|
field = self.fields[key]
|
|
|
|
value = self.data.get(key)
|
|
|
|
error = self.errors.get(key) if hasattr(self, '_errors') else None
|
2014-10-10 17:16:09 +04:00
|
|
|
if isinstance(field, Serializer):
|
|
|
|
return NestedBoundField(field, value, error)
|
2014-10-09 18:11:19 +04:00
|
|
|
return BoundField(field, value, error)
|
2014-09-09 20:46:28 +04:00
|
|
|
|
2014-11-14 02:05:44 +03:00
|
|
|
# Include a backlink to the serializer class on return objects.
|
|
|
|
# Allows renderers such as HTMLFormRenderer to get the full field info.
|
|
|
|
|
|
|
|
@property
|
|
|
|
def data(self):
|
|
|
|
ret = super(Serializer, self).data
|
|
|
|
return ReturnDict(ret, serializer=self)
|
|
|
|
|
|
|
|
@property
|
|
|
|
def errors(self):
|
|
|
|
ret = super(Serializer, self).errors
|
|
|
|
return ReturnDict(ret, serializer=self)
|
|
|
|
|
2012-12-29 17:19:05 +04:00
|
|
|
|
2014-09-26 16:08:20 +04:00
|
|
|
# There's some replication of `ListField` here,
|
|
|
|
# but that's probably better than obfuscating the call hierarchy.
|
|
|
|
|
2014-08-29 19:46:26 +04:00
|
|
|
class ListSerializer(BaseSerializer):
|
|
|
|
child = None
|
2014-10-02 19:24:24 +04:00
|
|
|
many = True
|
2013-03-12 17:33:02 +04:00
|
|
|
|
2014-11-06 13:34:59 +03:00
|
|
|
default_error_messages = {
|
|
|
|
'not_a_list': _('Expected a list of items but got type `{input_type}`.')
|
|
|
|
}
|
|
|
|
|
2014-08-29 19:46:26 +04:00
|
|
|
def __init__(self, *args, **kwargs):
|
|
|
|
self.child = kwargs.pop('child', copy.deepcopy(self.child))
|
|
|
|
assert self.child is not None, '`child` is a required argument.'
|
2014-09-19 19:43:13 +04:00
|
|
|
assert not inspect.isclass(self.child), '`child` has not been instantiated.'
|
2014-08-29 19:46:26 +04:00
|
|
|
super(ListSerializer, self).__init__(*args, **kwargs)
|
2014-09-25 14:40:32 +04:00
|
|
|
self.child.bind(field_name='', parent=self)
|
2013-03-22 21:01:06 +04:00
|
|
|
|
2014-10-02 19:24:24 +04:00
|
|
|
def get_initial(self):
|
|
|
|
if self._initial_data is not None:
|
|
|
|
return self.to_representation(self._initial_data)
|
2014-11-14 02:05:44 +03:00
|
|
|
return []
|
2014-10-02 19:24:24 +04:00
|
|
|
|
2014-08-29 19:46:26 +04:00
|
|
|
def get_value(self, dictionary):
|
2014-11-07 13:51:08 +03:00
|
|
|
"""
|
|
|
|
Given the input dictionary, return the field value.
|
|
|
|
"""
|
2014-08-29 19:46:26 +04:00
|
|
|
# We override the default field access in order to support
|
|
|
|
# lists in HTML forms.
|
2014-09-26 16:08:20 +04:00
|
|
|
if html.is_html_input(dictionary):
|
2014-08-29 19:46:26 +04:00
|
|
|
return html.parse_html_list(dictionary, prefix=self.field_name)
|
|
|
|
return dictionary.get(self.field_name, empty)
|
2013-03-19 18:26:48 +04:00
|
|
|
|
2014-09-12 12:49:35 +04:00
|
|
|
def to_internal_value(self, data):
|
2012-09-20 16:06:27 +04:00
|
|
|
"""
|
2014-08-29 19:46:26 +04:00
|
|
|
List of dicts of native values <- List of dicts of primitive datatypes.
|
2012-09-20 16:06:27 +04:00
|
|
|
"""
|
2014-08-29 19:46:26 +04:00
|
|
|
if html.is_html_input(data):
|
|
|
|
data = html.parse_html_list(data)
|
2014-11-06 13:34:59 +03:00
|
|
|
|
|
|
|
if not isinstance(data, list):
|
|
|
|
message = self.error_messages['not_a_list'].format(
|
|
|
|
input_type=type(data).__name__
|
|
|
|
)
|
|
|
|
raise ValidationError({
|
|
|
|
api_settings.NON_FIELD_ERRORS_KEY: [message]
|
|
|
|
})
|
|
|
|
|
|
|
|
ret = []
|
2014-11-14 02:05:44 +03:00
|
|
|
errors = []
|
2014-11-06 13:34:59 +03:00
|
|
|
|
|
|
|
for item in data:
|
|
|
|
try:
|
|
|
|
validated = self.child.run_validation(item)
|
2014-11-06 14:35:34 +03:00
|
|
|
except ValidationError as exc:
|
2014-11-06 13:34:59 +03:00
|
|
|
errors.append(exc.detail)
|
|
|
|
else:
|
|
|
|
ret.append(validated)
|
|
|
|
errors.append({})
|
|
|
|
|
|
|
|
if any(errors):
|
|
|
|
raise ValidationError(errors)
|
|
|
|
|
|
|
|
return ret
|
2012-09-20 16:06:27 +04:00
|
|
|
|
2014-09-12 12:49:35 +04:00
|
|
|
def to_representation(self, data):
|
2013-01-31 00:38:11 +04:00
|
|
|
"""
|
2014-08-29 19:46:26 +04:00
|
|
|
List of object instances -> List of dicts of primitive datatypes.
|
2013-01-31 00:38:11 +04:00
|
|
|
"""
|
2014-10-09 18:11:19 +04:00
|
|
|
iterable = data.all() if (hasattr(data, 'all')) else data
|
2014-11-14 02:05:44 +03:00
|
|
|
return [
|
|
|
|
self.child.to_representation(item) for item in iterable
|
|
|
|
]
|
2013-01-31 21:06:23 +04:00
|
|
|
|
2014-11-13 23:24:48 +03:00
|
|
|
def update(self, instance, validated_data):
|
|
|
|
raise NotImplementedError(
|
|
|
|
"Serializers with many=True do not support multiple update by "
|
|
|
|
"default, only multiple create. For updates it is unclear how to "
|
|
|
|
"deal with insertions and deletions. If you need to support "
|
|
|
|
"multiple update, use a `ListSerializer` class and override "
|
|
|
|
"`.update()` so you can specify the behavior exactly."
|
|
|
|
)
|
|
|
|
|
|
|
|
def create(self, validated_data):
|
|
|
|
return [
|
|
|
|
self.child.create(attrs) for attrs in validated_data
|
|
|
|
]
|
|
|
|
|
2014-11-06 13:34:59 +03:00
|
|
|
def save(self, **kwargs):
|
2014-11-07 13:51:08 +03:00
|
|
|
"""
|
|
|
|
Save and return a list of object instances.
|
|
|
|
"""
|
2014-11-06 13:34:59 +03:00
|
|
|
validated_data = [
|
|
|
|
dict(list(attrs.items()) + list(kwargs.items()))
|
|
|
|
for attrs in self.validated_data
|
|
|
|
]
|
|
|
|
|
2014-11-13 23:24:48 +03:00
|
|
|
if self.instance is not None:
|
|
|
|
self.instance = self.update(self.instance, validated_data)
|
|
|
|
assert self.instance is not None, (
|
|
|
|
'`update()` did not return an object instance.'
|
|
|
|
)
|
|
|
|
else:
|
|
|
|
self.instance = self.create(validated_data)
|
|
|
|
assert self.instance is not None, (
|
|
|
|
'`create()` did not return an object instance.'
|
|
|
|
)
|
2014-11-06 13:34:59 +03:00
|
|
|
|
|
|
|
return self.instance
|
2013-01-31 21:06:23 +04:00
|
|
|
|
2014-09-09 20:46:28 +04:00
|
|
|
def __repr__(self):
|
|
|
|
return representation.list_repr(self, indent=1)
|
2012-09-20 16:06:27 +04:00
|
|
|
|
2014-11-14 02:05:44 +03:00
|
|
|
# Include a backlink to the serializer class on return objects.
|
|
|
|
# Allows renderers such as HTMLFormRenderer to get the full field info.
|
|
|
|
|
|
|
|
@property
|
|
|
|
def data(self):
|
|
|
|
ret = super(ListSerializer, self).data
|
|
|
|
return ReturnList(ret, serializer=self)
|
|
|
|
|
|
|
|
@property
|
|
|
|
def errors(self):
|
|
|
|
ret = super(ListSerializer, self).errors
|
|
|
|
if isinstance(ret, dict):
|
|
|
|
return ReturnDict(ret, serializer=self)
|
|
|
|
return ReturnList(ret, serializer=self)
|
|
|
|
|
2012-09-20 16:06:27 +04:00
|
|
|
|
2014-10-02 19:24:24 +04:00
|
|
|
# ModelSerializer & HyperlinkedModelSerializer
|
|
|
|
# --------------------------------------------
|
|
|
|
|
2012-10-04 16:28:14 +04:00
|
|
|
class ModelSerializer(Serializer):
|
2014-11-07 13:13:46 +03:00
|
|
|
"""
|
|
|
|
A `ModelSerializer` is just a regular `Serializer`, except that:
|
|
|
|
|
|
|
|
* A set of default fields are automatically populated.
|
|
|
|
* A set of default validators are automatically populated.
|
|
|
|
* Default `.create()` and `.update()` implementations are provided.
|
2014-12-01 14:37:38 +03:00
|
|
|
|
|
|
|
The process of automatically determining a set of serializer fields
|
|
|
|
based on the model fields is reasonably complex, but you almost certainly
|
|
|
|
don't need to dig into the implemention.
|
|
|
|
|
|
|
|
If the `ModelSerializer` class *doesn't* generate the set of fields that
|
|
|
|
you need you should either declare the extra/differing fields explicitly on
|
|
|
|
the serializer class, or simply use a `Serializer` class.
|
2014-11-07 13:13:46 +03:00
|
|
|
"""
|
2014-09-24 17:09:49 +04:00
|
|
|
_field_mapping = ClassLookupDict({
|
2013-02-28 17:41:42 +04:00
|
|
|
models.AutoField: IntegerField,
|
2014-09-09 20:46:28 +04:00
|
|
|
models.BigIntegerField: IntegerField,
|
|
|
|
models.BooleanField: BooleanField,
|
|
|
|
models.CharField: CharField,
|
|
|
|
models.CommaSeparatedIntegerField: CharField,
|
|
|
|
models.DateField: DateField,
|
|
|
|
models.DateTimeField: DateTimeField,
|
|
|
|
models.DecimalField: DecimalField,
|
|
|
|
models.EmailField: EmailField,
|
2014-09-11 23:22:32 +04:00
|
|
|
models.Field: ModelField,
|
2014-09-09 20:46:28 +04:00
|
|
|
models.FileField: FileField,
|
|
|
|
models.FloatField: FloatField,
|
2014-09-10 11:53:33 +04:00
|
|
|
models.ImageField: ImageField,
|
2013-02-28 17:41:42 +04:00
|
|
|
models.IntegerField: IntegerField,
|
2014-09-23 17:30:17 +04:00
|
|
|
models.NullBooleanField: NullBooleanField,
|
2013-02-28 17:41:42 +04:00
|
|
|
models.PositiveIntegerField: IntegerField,
|
|
|
|
models.PositiveSmallIntegerField: IntegerField,
|
2014-09-09 20:46:28 +04:00
|
|
|
models.SlugField: SlugField,
|
|
|
|
models.SmallIntegerField: IntegerField,
|
|
|
|
models.TextField: CharField,
|
2014-09-02 20:41:23 +04:00
|
|
|
models.TimeField: TimeField,
|
|
|
|
models.URLField: URLField,
|
2014-09-24 17:09:49 +04:00
|
|
|
})
|
2014-09-18 14:20:56 +04:00
|
|
|
_related_class = PrimaryKeyRelatedField
|
2014-08-29 19:46:26 +04:00
|
|
|
|
2014-12-04 01:52:35 +03:00
|
|
|
def create(self, validated_data):
|
2014-12-01 14:59:04 +03:00
|
|
|
"""
|
|
|
|
We have a bit of extra checking around this in order to provide
|
|
|
|
descriptive messages when something goes wrong, but this method is
|
|
|
|
essentially just:
|
|
|
|
|
2014-12-04 01:52:35 +03:00
|
|
|
return ExampleModel.objects.create(**validated_data)
|
2014-12-01 14:59:04 +03:00
|
|
|
|
|
|
|
If there are many to many fields present on the instance then they
|
|
|
|
cannot be set until the model is instantiated, in which case the
|
|
|
|
implementation is like so:
|
|
|
|
|
2014-12-04 01:52:35 +03:00
|
|
|
example_relationship = validated_data.pop('example_relationship')
|
|
|
|
instance = ExampleModel.objects.create(**validated_data)
|
2014-12-01 14:59:04 +03:00
|
|
|
instance.example_relationship = example_relationship
|
|
|
|
return instance
|
|
|
|
|
|
|
|
The default implementation also does not handle nested relationships.
|
|
|
|
If you want to support writable nested relationships you'll need
|
|
|
|
to write an explicit `.create()` method.
|
|
|
|
"""
|
2014-10-22 13:32:32 +04:00
|
|
|
# Check that the user isn't trying to handle a writable nested field.
|
|
|
|
# If we don't do this explicitly they'd likely get a confusing
|
|
|
|
# error at the point of calling `Model.objects.create()`.
|
2014-10-16 23:45:18 +04:00
|
|
|
assert not any(
|
2014-12-04 01:45:44 +03:00
|
|
|
isinstance(field, BaseSerializer) and (key in validated_attrs)
|
|
|
|
for key, field in self.fields.items()
|
2014-10-16 23:45:18 +04:00
|
|
|
), (
|
|
|
|
'The `.create()` method does not suport nested writable fields '
|
|
|
|
'by default. Write an explicit `.create()` method for serializer '
|
2014-10-22 13:32:32 +04:00
|
|
|
'`%s.%s`, or set `read_only=True` on nested serializer fields.' %
|
|
|
|
(self.__class__.__module__, self.__class__.__name__)
|
2014-10-16 23:45:18 +04:00
|
|
|
)
|
|
|
|
|
2014-09-18 14:20:56 +04:00
|
|
|
ModelClass = self.Meta.model
|
2014-09-18 17:58:08 +04:00
|
|
|
|
2014-12-04 01:52:35 +03:00
|
|
|
# Remove many-to-many relationships from validated_data.
|
2014-09-18 17:58:08 +04:00
|
|
|
# They are not valid arguments to the default `.create()` method,
|
|
|
|
# as they require that the instance has already been saved.
|
|
|
|
info = model_meta.get_field_info(ModelClass)
|
|
|
|
many_to_many = {}
|
2014-09-18 18:47:27 +04:00
|
|
|
for field_name, relation_info in info.relations.items():
|
2014-12-04 01:52:35 +03:00
|
|
|
if relation_info.to_many and (field_name in validated_data):
|
|
|
|
many_to_many[field_name] = validated_data.pop(field_name)
|
2014-09-18 17:58:08 +04:00
|
|
|
|
2014-11-15 17:23:58 +03:00
|
|
|
try:
|
2014-12-04 01:52:35 +03:00
|
|
|
instance = ModelClass.objects.create(**validated_data)
|
2014-11-15 17:23:58 +03:00
|
|
|
except TypeError as exc:
|
|
|
|
msg = (
|
2014-12-02 16:04:49 +03:00
|
|
|
'Got a `TypeError` when calling `%s.objects.create()`. '
|
|
|
|
'This may be because you have a writable field on the '
|
|
|
|
'serializer class that is not a valid argument to '
|
|
|
|
'`%s.objects.create()`. You may need to make the field '
|
|
|
|
'read-only, or override the %s.create() method to handle '
|
|
|
|
'this correctly.\nOriginal exception text was: %s.' %
|
|
|
|
(
|
|
|
|
ModelClass.__name__,
|
|
|
|
ModelClass.__name__,
|
|
|
|
self.__class__.__name__,
|
|
|
|
exc
|
|
|
|
)
|
|
|
|
)
|
|
|
|
raise TypeError(msg)
|
2014-09-18 17:58:08 +04:00
|
|
|
|
2014-09-18 18:47:27 +04:00
|
|
|
# Save many-to-many relationships after the instance is created.
|
2014-09-18 17:58:08 +04:00
|
|
|
if many_to_many:
|
2014-09-18 18:47:27 +04:00
|
|
|
for field_name, value in many_to_many.items():
|
|
|
|
setattr(instance, field_name, value)
|
2014-09-18 17:58:08 +04:00
|
|
|
|
|
|
|
return instance
|
2014-09-02 20:41:23 +04:00
|
|
|
|
2014-12-04 01:52:35 +03:00
|
|
|
def update(self, instance, validated_data):
|
2014-10-16 23:45:18 +04:00
|
|
|
assert not any(
|
2014-12-04 01:43:40 +03:00
|
|
|
isinstance(field, BaseSerializer) and (key in validated_attrs)
|
2014-12-04 01:45:44 +03:00
|
|
|
for key, field in self.fields.items()
|
2014-10-16 23:45:18 +04:00
|
|
|
), (
|
|
|
|
'The `.update()` method does not suport nested writable fields '
|
|
|
|
'by default. Write an explicit `.update()` method for serializer '
|
2014-10-22 13:32:32 +04:00
|
|
|
'`%s.%s`, or set `read_only=True` on nested serializer fields.' %
|
|
|
|
(self.__class__.__module__, self.__class__.__name__)
|
2014-10-16 23:45:18 +04:00
|
|
|
)
|
|
|
|
|
2014-12-04 01:52:35 +03:00
|
|
|
for attr, value in validated_data.items():
|
2014-10-08 15:17:30 +04:00
|
|
|
setattr(instance, attr, value)
|
|
|
|
instance.save()
|
|
|
|
return instance
|
2014-09-02 20:41:23 +04:00
|
|
|
|
2014-10-31 19:38:39 +03:00
|
|
|
def get_validators(self):
|
2014-12-01 14:59:04 +03:00
|
|
|
# If the validators have been declared explicitly then use that.
|
|
|
|
validators = getattr(getattr(self, 'Meta', None), 'validators', None)
|
|
|
|
if validators is not None:
|
|
|
|
return validators
|
|
|
|
|
|
|
|
# Determine the default set of validators.
|
|
|
|
validators = []
|
|
|
|
model_class = self.Meta.model
|
2014-09-29 14:23:02 +04:00
|
|
|
field_names = set([
|
|
|
|
field.source for field in self.fields.values()
|
|
|
|
if (field.source != '*') and ('.' not in field.source)
|
|
|
|
])
|
|
|
|
|
2014-10-22 16:30:28 +04:00
|
|
|
# Note that we make sure to check `unique_together` both on the
|
|
|
|
# base model class, but also on any parent classes.
|
|
|
|
for parent_class in [model_class] + list(model_class._meta.parents.keys()):
|
2014-09-29 14:23:02 +04:00
|
|
|
for unique_together in parent_class._meta.unique_together:
|
|
|
|
if field_names.issuperset(set(unique_together)):
|
|
|
|
validator = UniqueTogetherValidator(
|
|
|
|
queryset=parent_class._default_manager,
|
|
|
|
fields=unique_together
|
|
|
|
)
|
|
|
|
validators.append(validator)
|
|
|
|
|
2014-10-28 19:21:49 +03:00
|
|
|
# Add any unique_for_date/unique_for_month/unique_for_year constraints.
|
2014-10-22 16:30:28 +04:00
|
|
|
info = model_meta.get_field_info(model_class)
|
|
|
|
for field_name, field in info.fields_and_pk.items():
|
|
|
|
if field.unique_for_date and field_name in field_names:
|
|
|
|
validator = UniqueForDateValidator(
|
|
|
|
queryset=model_class._default_manager,
|
|
|
|
field=field_name,
|
|
|
|
date_field=field.unique_for_date
|
|
|
|
)
|
|
|
|
validators.append(validator)
|
|
|
|
|
2014-10-22 19:29:09 +04:00
|
|
|
if field.unique_for_month and field_name in field_names:
|
|
|
|
validator = UniqueForMonthValidator(
|
|
|
|
queryset=model_class._default_manager,
|
|
|
|
field=field_name,
|
|
|
|
date_field=field.unique_for_month
|
|
|
|
)
|
|
|
|
validators.append(validator)
|
|
|
|
|
|
|
|
if field.unique_for_year and field_name in field_names:
|
|
|
|
validator = UniqueForYearValidator(
|
|
|
|
queryset=model_class._default_manager,
|
|
|
|
field=field_name,
|
|
|
|
date_field=field.unique_for_year
|
|
|
|
)
|
|
|
|
validators.append(validator)
|
|
|
|
|
2014-09-29 14:23:02 +04:00
|
|
|
return validators
|
|
|
|
|
2014-10-31 19:38:39 +03:00
|
|
|
def get_fields(self):
|
2014-09-18 14:20:56 +04:00
|
|
|
declared_fields = copy.deepcopy(self._declared_fields)
|
2014-08-29 19:46:26 +04:00
|
|
|
|
2014-11-06 15:00:30 +03:00
|
|
|
ret = OrderedDict()
|
2014-09-18 14:20:56 +04:00
|
|
|
model = getattr(self.Meta, 'model')
|
|
|
|
fields = getattr(self.Meta, 'fields', None)
|
2014-10-10 17:16:09 +04:00
|
|
|
exclude = getattr(self.Meta, 'exclude', None)
|
2014-09-18 14:20:56 +04:00
|
|
|
depth = getattr(self.Meta, 'depth', 0)
|
2014-09-18 15:17:21 +04:00
|
|
|
extra_kwargs = getattr(self.Meta, 'extra_kwargs', {})
|
2014-09-18 14:20:56 +04:00
|
|
|
|
2014-10-10 18:08:43 +04:00
|
|
|
assert not (fields and exclude), "Cannot set both 'fields' and 'exclude'."
|
2014-10-10 17:16:09 +04:00
|
|
|
|
2014-10-08 19:09:37 +04:00
|
|
|
extra_kwargs = self._include_additional_options(extra_kwargs)
|
|
|
|
|
2014-09-18 14:20:56 +04:00
|
|
|
# Retrieve metadata about fields & relationships on the model class.
|
|
|
|
info = model_meta.get_field_info(model)
|
|
|
|
|
2014-10-28 19:21:49 +03:00
|
|
|
# Use the default set of field names if none is supplied explicitly.
|
2014-09-18 14:20:56 +04:00
|
|
|
if fields is None:
|
|
|
|
fields = self._get_default_field_names(declared_fields, info)
|
2014-10-08 19:09:37 +04:00
|
|
|
exclude = getattr(self.Meta, 'exclude', None)
|
|
|
|
if exclude is not None:
|
|
|
|
for field_name in exclude:
|
|
|
|
fields.remove(field_name)
|
2014-09-18 14:20:56 +04:00
|
|
|
|
2014-10-28 19:21:49 +03:00
|
|
|
# Determine the set of model fields, and the fields that they map to.
|
|
|
|
# We actually only need this to deal with the slightly awkward case
|
|
|
|
# of supporting `unique_for_date`/`unique_for_month`/`unique_for_year`.
|
|
|
|
model_field_mapping = {}
|
|
|
|
for field_name in fields:
|
|
|
|
if field_name in declared_fields:
|
|
|
|
field = declared_fields[field_name]
|
|
|
|
source = field.source or field_name
|
|
|
|
else:
|
|
|
|
try:
|
|
|
|
source = extra_kwargs[field_name]['source']
|
|
|
|
except KeyError:
|
|
|
|
source = field_name
|
|
|
|
# Model fields will always have a simple source mapping,
|
|
|
|
# they can't be nested attribute lookups.
|
|
|
|
if '.' not in source and source != '*':
|
|
|
|
model_field_mapping[source] = field_name
|
|
|
|
|
|
|
|
# Determine if we need any additional `HiddenField` or extra keyword
|
|
|
|
# arguments to deal with `unique_for` dates that are required to
|
|
|
|
# be in the input data in order to validate it.
|
2014-11-19 16:55:10 +03:00
|
|
|
hidden_fields = {}
|
2014-11-19 17:51:49 +03:00
|
|
|
unique_constraint_names = set()
|
2014-11-19 16:55:10 +03:00
|
|
|
|
2014-10-28 19:21:49 +03:00
|
|
|
for model_field_name, field_name in model_field_mapping.items():
|
|
|
|
try:
|
|
|
|
model_field = model._meta.get_field(model_field_name)
|
|
|
|
except FieldDoesNotExist:
|
|
|
|
continue
|
|
|
|
|
2014-11-19 16:55:10 +03:00
|
|
|
# Include each of the `unique_for_*` field names.
|
2014-11-19 17:51:49 +03:00
|
|
|
unique_constraint_names |= set([
|
2014-10-28 19:21:49 +03:00
|
|
|
model_field.unique_for_date,
|
|
|
|
model_field.unique_for_month,
|
|
|
|
model_field.unique_for_year
|
2014-11-19 16:55:10 +03:00
|
|
|
])
|
2014-11-19 17:51:49 +03:00
|
|
|
|
|
|
|
unique_constraint_names -= set([None])
|
2014-11-19 16:55:10 +03:00
|
|
|
|
2014-11-19 17:40:30 +03:00
|
|
|
# Include each of the `unique_together` field names,
|
|
|
|
# so long as all the field names are included on the serializer.
|
|
|
|
for parent_class in [model] + list(model._meta.parents.keys()):
|
|
|
|
for unique_together_list in parent_class._meta.unique_together:
|
|
|
|
if set(fields).issuperset(set(unique_together_list)):
|
|
|
|
unique_constraint_names |= set(unique_together_list)
|
2014-11-19 16:55:10 +03:00
|
|
|
|
|
|
|
# Now we have all the field names that have uniqueness constraints
|
|
|
|
# applied, we can add the extra 'required=...' or 'default=...'
|
|
|
|
# arguments that are appropriate to these fields, or add a `HiddenField` for it.
|
|
|
|
for unique_constraint_name in unique_constraint_names:
|
|
|
|
# Get the model field that is refered too.
|
|
|
|
unique_constraint_field = model._meta.get_field(unique_constraint_name)
|
|
|
|
|
|
|
|
if getattr(unique_constraint_field, 'auto_now_add', None):
|
|
|
|
default = CreateOnlyDefault(timezone.now)
|
|
|
|
elif getattr(unique_constraint_field, 'auto_now', None):
|
|
|
|
default = timezone.now
|
|
|
|
elif unique_constraint_field.has_default():
|
2014-11-20 12:30:49 +03:00
|
|
|
default = unique_constraint_field.default
|
2014-11-19 16:55:10 +03:00
|
|
|
else:
|
|
|
|
default = empty
|
|
|
|
|
|
|
|
if unique_constraint_name in model_field_mapping:
|
|
|
|
# The corresponding field is present in the serializer
|
|
|
|
if unique_constraint_name not in extra_kwargs:
|
|
|
|
extra_kwargs[unique_constraint_name] = {}
|
|
|
|
if default is empty:
|
|
|
|
if 'required' not in extra_kwargs[unique_constraint_name]:
|
|
|
|
extra_kwargs[unique_constraint_name]['required'] = True
|
2014-10-28 19:21:49 +03:00
|
|
|
else:
|
2014-11-19 16:55:10 +03:00
|
|
|
if 'default' not in extra_kwargs[unique_constraint_name]:
|
|
|
|
extra_kwargs[unique_constraint_name]['default'] = default
|
|
|
|
elif default is not empty:
|
|
|
|
# The corresponding field is not present in the,
|
|
|
|
# serializer. We have a default to use for it, so
|
|
|
|
# add in a hidden field that populates it.
|
|
|
|
hidden_fields[unique_constraint_name] = HiddenField(default=default)
|
2014-10-28 19:21:49 +03:00
|
|
|
|
|
|
|
# Now determine the fields that should be included on the serializer.
|
2014-09-18 14:20:56 +04:00
|
|
|
for field_name in fields:
|
|
|
|
if field_name in declared_fields:
|
|
|
|
# Field is explicitly declared on the class, use that.
|
|
|
|
ret[field_name] = declared_fields[field_name]
|
|
|
|
continue
|
|
|
|
|
|
|
|
elif field_name in info.fields_and_pk:
|
|
|
|
# Create regular model fields.
|
|
|
|
model_field = info.fields_and_pk[field_name]
|
2014-09-24 17:09:49 +04:00
|
|
|
field_cls = self._field_mapping[model_field]
|
2014-09-18 14:20:56 +04:00
|
|
|
kwargs = get_field_kwargs(field_name, model_field)
|
|
|
|
if 'choices' in kwargs:
|
|
|
|
# Fields with choices get coerced into `ChoiceField`
|
|
|
|
# instead of using their regular typed field.
|
|
|
|
field_cls = ChoiceField
|
|
|
|
if not issubclass(field_cls, ModelField):
|
|
|
|
# `model_field` is only valid for the fallback case of
|
|
|
|
# `ModelField`, which is used when no other typed field
|
|
|
|
# matched to the model field.
|
|
|
|
kwargs.pop('model_field', None)
|
2014-09-23 17:15:00 +04:00
|
|
|
if not issubclass(field_cls, CharField):
|
|
|
|
# `allow_blank` is only valid for textual fields.
|
|
|
|
kwargs.pop('allow_blank', None)
|
2014-09-18 14:20:56 +04:00
|
|
|
|
|
|
|
elif field_name in info.relations:
|
|
|
|
# Create forward and reverse relationships.
|
|
|
|
relation_info = info.relations[field_name]
|
|
|
|
if depth:
|
|
|
|
field_cls = self._get_nested_class(depth, relation_info)
|
|
|
|
kwargs = get_nested_relation_kwargs(relation_info)
|
|
|
|
else:
|
|
|
|
field_cls = self._related_class
|
|
|
|
kwargs = get_relation_kwargs(field_name, relation_info)
|
|
|
|
# `view_name` is only valid for hyperlinked relationships.
|
|
|
|
if not issubclass(field_cls, HyperlinkedRelatedField):
|
|
|
|
kwargs.pop('view_name', None)
|
2013-03-11 11:23:44 +04:00
|
|
|
|
2014-09-18 15:17:21 +04:00
|
|
|
elif hasattr(model, field_name):
|
|
|
|
# Create a read only field for model methods and properties.
|
|
|
|
field_cls = ReadOnlyField
|
|
|
|
kwargs = {}
|
2013-04-23 14:31:38 +04:00
|
|
|
|
2014-10-08 14:22:10 +04:00
|
|
|
elif field_name == api_settings.URL_FIELD_NAME:
|
|
|
|
# Create the URL field.
|
|
|
|
field_cls = HyperlinkedIdentityField
|
|
|
|
kwargs = get_url_kwargs(model)
|
|
|
|
|
2014-09-18 15:17:21 +04:00
|
|
|
else:
|
|
|
|
raise ImproperlyConfigured(
|
|
|
|
'Field name `%s` is not valid for model `%s`.' %
|
|
|
|
(field_name, model.__class__.__name__)
|
|
|
|
)
|
|
|
|
|
|
|
|
# Check that any fields declared on the class are
|
|
|
|
# also explicity included in `Meta.fields`.
|
|
|
|
missing_fields = set(declared_fields.keys()) - set(fields)
|
|
|
|
if missing_fields:
|
|
|
|
missing_field = list(missing_fields)[0]
|
|
|
|
raise ImproperlyConfigured(
|
|
|
|
'Field `%s` has been declared on serializer `%s`, but '
|
|
|
|
'is missing from `Meta.fields`.' %
|
|
|
|
(missing_field, self.__class__.__name__)
|
|
|
|
)
|
|
|
|
|
|
|
|
# Populate any kwargs defined in `Meta.extra_kwargs`
|
2014-10-08 19:09:37 +04:00
|
|
|
extras = extra_kwargs.get(field_name, {})
|
|
|
|
if extras.get('read_only', False):
|
|
|
|
for attr in [
|
|
|
|
'required', 'default', 'allow_blank', 'allow_null',
|
|
|
|
'min_length', 'max_length', 'min_value', 'max_value',
|
2014-10-10 18:34:00 +04:00
|
|
|
'validators', 'queryset'
|
2014-10-08 19:09:37 +04:00
|
|
|
]:
|
|
|
|
kwargs.pop(attr, None)
|
2014-11-19 16:55:10 +03:00
|
|
|
|
|
|
|
if extras.get('default') and kwargs.get('required') is False:
|
|
|
|
kwargs.pop('required')
|
|
|
|
|
2014-10-08 19:09:37 +04:00
|
|
|
kwargs.update(extras)
|
2014-09-18 15:17:21 +04:00
|
|
|
|
|
|
|
# Create the serializer field.
|
2014-09-18 14:20:56 +04:00
|
|
|
ret[field_name] = field_cls(**kwargs)
|
2014-05-22 23:51:20 +04:00
|
|
|
|
2014-11-19 16:55:10 +03:00
|
|
|
for field_name, field in hidden_fields.items():
|
2014-10-28 19:21:49 +03:00
|
|
|
ret[field_name] = field
|
|
|
|
|
2012-09-20 16:06:27 +04:00
|
|
|
return ret
|
|
|
|
|
2014-10-08 19:09:37 +04:00
|
|
|
def _include_additional_options(self, extra_kwargs):
|
|
|
|
read_only_fields = getattr(self.Meta, 'read_only_fields', None)
|
|
|
|
if read_only_fields is not None:
|
|
|
|
for field_name in read_only_fields:
|
|
|
|
kwargs = extra_kwargs.get(field_name, {})
|
|
|
|
kwargs['read_only'] = True
|
|
|
|
extra_kwargs[field_name] = kwargs
|
|
|
|
|
|
|
|
# These are all pending deprecation.
|
|
|
|
write_only_fields = getattr(self.Meta, 'write_only_fields', None)
|
|
|
|
if write_only_fields is not None:
|
|
|
|
warnings.warn(
|
|
|
|
"The `Meta.write_only_fields` option is pending deprecation. "
|
|
|
|
"Use `Meta.extra_kwargs={<field_name>: {'write_only': True}}` instead.",
|
|
|
|
PendingDeprecationWarning,
|
|
|
|
stacklevel=3
|
|
|
|
)
|
|
|
|
for field_name in write_only_fields:
|
|
|
|
kwargs = extra_kwargs.get(field_name, {})
|
|
|
|
kwargs['write_only'] = True
|
|
|
|
extra_kwargs[field_name] = kwargs
|
|
|
|
|
|
|
|
view_name = getattr(self.Meta, 'view_name', None)
|
|
|
|
if view_name is not None:
|
|
|
|
warnings.warn(
|
|
|
|
"The `Meta.view_name` option is pending deprecation. "
|
|
|
|
"Use `Meta.extra_kwargs={'url': {'view_name': ...}}` instead.",
|
|
|
|
PendingDeprecationWarning,
|
|
|
|
stacklevel=3
|
|
|
|
)
|
2014-10-15 12:24:49 +04:00
|
|
|
kwargs = extra_kwargs.get(api_settings.URL_FIELD_NAME, {})
|
2014-10-08 19:09:37 +04:00
|
|
|
kwargs['view_name'] = view_name
|
|
|
|
extra_kwargs[api_settings.URL_FIELD_NAME] = kwargs
|
|
|
|
|
|
|
|
lookup_field = getattr(self.Meta, 'lookup_field', None)
|
|
|
|
if lookup_field is not None:
|
|
|
|
warnings.warn(
|
|
|
|
"The `Meta.lookup_field` option is pending deprecation. "
|
|
|
|
"Use `Meta.extra_kwargs={'url': {'lookup_field': ...}}` instead.",
|
|
|
|
PendingDeprecationWarning,
|
|
|
|
stacklevel=3
|
|
|
|
)
|
2014-10-15 12:24:49 +04:00
|
|
|
kwargs = extra_kwargs.get(api_settings.URL_FIELD_NAME, {})
|
2014-10-08 19:09:37 +04:00
|
|
|
kwargs['lookup_field'] = lookup_field
|
|
|
|
extra_kwargs[api_settings.URL_FIELD_NAME] = kwargs
|
|
|
|
|
|
|
|
return extra_kwargs
|
|
|
|
|
2014-09-18 14:20:56 +04:00
|
|
|
def _get_default_field_names(self, declared_fields, model_info):
|
|
|
|
return (
|
|
|
|
[model_info.pk.name] +
|
|
|
|
list(declared_fields.keys()) +
|
|
|
|
list(model_info.fields.keys()) +
|
|
|
|
list(model_info.forward_relations.keys())
|
|
|
|
)
|
2013-04-30 11:24:33 +04:00
|
|
|
|
2014-09-18 14:20:56 +04:00
|
|
|
def _get_nested_class(self, nested_depth, relation_info):
|
|
|
|
class NestedSerializer(ModelSerializer):
|
2012-11-13 15:47:32 +04:00
|
|
|
class Meta:
|
2014-09-18 14:20:56 +04:00
|
|
|
model = relation_info.related
|
|
|
|
depth = nested_depth
|
|
|
|
return NestedSerializer
|
2012-10-04 14:26:41 +04:00
|
|
|
|
|
|
|
|
|
|
|
class HyperlinkedModelSerializer(ModelSerializer):
|
2014-11-07 13:13:46 +03:00
|
|
|
"""
|
|
|
|
A type of `ModelSerializer` that uses hyperlinked relationships instead
|
|
|
|
of primary key relationships. Specifically:
|
|
|
|
|
|
|
|
* A 'url' field is included instead of the 'id' field.
|
|
|
|
* Relationships to other instances are hyperlinks, instead of primary keys.
|
|
|
|
"""
|
2014-09-18 14:20:56 +04:00
|
|
|
_related_class = HyperlinkedRelatedField
|
|
|
|
|
|
|
|
def _get_default_field_names(self, declared_fields, model_info):
|
|
|
|
return (
|
|
|
|
[api_settings.URL_FIELD_NAME] +
|
|
|
|
list(declared_fields.keys()) +
|
|
|
|
list(model_info.fields.keys()) +
|
|
|
|
list(model_info.forward_relations.keys())
|
|
|
|
)
|
|
|
|
|
|
|
|
def _get_nested_class(self, nested_depth, relation_info):
|
|
|
|
class NestedSerializer(HyperlinkedModelSerializer):
|
|
|
|
class Meta:
|
|
|
|
model = relation_info.related
|
|
|
|
depth = nested_depth
|
|
|
|
return NestedSerializer
|