aboutsummaryrefslogtreecommitdiffstats
path: root/rest_framework/serializers.py
diff options
context:
space:
mode:
Diffstat (limited to 'rest_framework/serializers.py')
-rw-r--r--rest_framework/serializers.py126
1 files changed, 77 insertions, 49 deletions
diff --git a/rest_framework/serializers.py b/rest_framework/serializers.py
index cb7539e0..be8ad3f2 100644
--- a/rest_framework/serializers.py
+++ b/rest_framework/serializers.py
@@ -16,11 +16,13 @@ import datetime
import inspect
import types
from decimal import Decimal
+from django.contrib.contenttypes.generic import GenericForeignKey
from django.core.paginator import Page
from django.db import models
from django.forms import widgets
+from django.utils import six
from django.utils.datastructures import SortedDict
-from rest_framework.compat import get_concrete_model, six
+from django.core.exceptions import ObjectDoesNotExist
from rest_framework.settings import api_settings
@@ -31,8 +33,8 @@ from rest_framework.settings import api_settings
# This helps keep the separation between model fields, form fields, and
# serializer fields more explicit.
-from rest_framework.relations import *
-from rest_framework.fields import *
+from rest_framework.relations import * # NOQA
+from rest_framework.fields import * # NOQA
def _resolve_model(obj):
@@ -47,7 +49,7 @@ def _resolve_model(obj):
String representations should have the format:
'appname.ModelName'
"""
- if type(obj) == str and len(obj.split('.')) == 2:
+ if isinstance(obj, six.string_types) and len(obj.split('.')) == 2:
app_name, model_name = obj.split('.')
return models.get_model(app_name, model_name)
elif inspect.isclass(obj) and issubclass(obj, models.Model):
@@ -180,7 +182,7 @@ class BaseSerializer(WritableField):
_dict_class = SortedDictWithMetadata
def __init__(self, instance=None, data=None, files=None,
- context=None, partial=False, many=None,
+ context=None, partial=False, many=False,
allow_add_remove=False, **kwargs):
super(BaseSerializer, self).__init__(**kwargs)
self.opts = self._options_class(self.Meta)
@@ -343,7 +345,7 @@ class BaseSerializer(WritableField):
for field_name, field in self.fields.items():
if field.read_only and obj is None:
- continue
+ continue
field.initialize(parent=self, field_name=field_name)
key = self.get_field_key(field_name)
value = field.field_to_native(obj, field_name)
@@ -410,12 +412,7 @@ class BaseSerializer(WritableField):
if value is None:
return None
- if self.many is not None:
- many = self.many
- else:
- many = hasattr(value, '__iter__') and not isinstance(value, (Page, dict, six.text_type))
-
- if many:
+ if self.many:
return [self.to_native(item) for item in value]
return self.to_native(value)
@@ -452,9 +449,11 @@ class BaseSerializer(WritableField):
# If we have a model manager or similar object then we need
# to iterate through each instance.
- if (self.many and
+ if (
+ self.many and
not hasattr(obj, '__iter__') and
- is_simple_callable(getattr(obj, 'all', None))):
+ is_simple_callable(getattr(obj, 'all', None))
+ ):
obj = obj.all()
kwargs = {
@@ -604,8 +603,10 @@ class BaseSerializer(WritableField):
API schemas for auto-documentation.
"""
return SortedDict(
- [(field_name, field.metadata())
- for field_name, field in six.iteritems(self.fields)]
+ [
+ (field_name, field.metadata())
+ for field_name, field in six.iteritems(self.fields)
+ ]
)
@@ -659,9 +660,11 @@ class ModelSerializer(Serializer):
"""
cls = self.opts.model
- assert cls is not None, \
- "Serializer class '%s' is missing 'model' Meta option" % self.__class__.__name__
- opts = get_concrete_model(cls)._meta
+ assert cls is not None, (
+ "Serializer class '%s' is missing 'model' Meta option" %
+ self.__class__.__name__
+ )
+ opts = cls._meta.concrete_model._meta
ret = SortedDict()
nested = bool(self.opts.depth)
@@ -671,9 +674,9 @@ class ModelSerializer(Serializer):
# If model is a child via multitable inheritance, use parent's pk
pk_field = pk_field.rel.to._meta.pk
- field = self.get_pk_field(pk_field)
- if field:
- ret[pk_field.name] = field
+ serializer_pk_field = self.get_pk_field(pk_field)
+ if serializer_pk_field:
+ ret[pk_field.name] = serializer_pk_field
# Deal with forward relationships
forward_rels = [field for field in opts.fields if field.serialize]
@@ -694,10 +697,10 @@ class ModelSerializer(Serializer):
if len(inspect.getargspec(self.get_nested_field).args) == 2:
warnings.warn(
'The `get_nested_field(model_field)` call signature '
- 'is due to be deprecated. '
+ 'is deprecated. '
'Use `get_nested_field(model_field, related_model, '
'to_many) instead',
- PendingDeprecationWarning
+ DeprecationWarning
)
field = self.get_nested_field(model_field)
else:
@@ -706,10 +709,10 @@ class ModelSerializer(Serializer):
if len(inspect.getargspec(self.get_nested_field).args) == 3:
warnings.warn(
'The `get_related_field(model_field, to_many)` call '
- 'signature is due to be deprecated. '
+ 'signature is deprecated. '
'Use `get_related_field(model_field, related_model, '
'to_many) instead',
- PendingDeprecationWarning
+ DeprecationWarning
)
field = self.get_related_field(model_field, to_many=to_many)
else:
@@ -742,9 +745,11 @@ class ModelSerializer(Serializer):
is_m2m = isinstance(relation.field,
models.fields.related.ManyToManyField)
- if (is_m2m and
+ if (
+ is_m2m and
hasattr(relation.field.rel, 'through') and
- not relation.field.rel.through._meta.auto_created):
+ not relation.field.rel.through._meta.auto_created
+ ):
has_through_model = True
if nested:
@@ -757,9 +762,9 @@ class ModelSerializer(Serializer):
field.read_only = True
ret[accessor_name] = field
-
+
# Ensure that 'read_only_fields' is an iterable
- assert isinstance(self.opts.read_only_fields, (list, tuple)), '`read_only_fields` must be a list or tuple'
+ assert isinstance(self.opts.read_only_fields, (list, tuple)), '`read_only_fields` must be a list or tuple'
# Add the `read_only` flag to any fields that have been specified
# in the `read_only_fields` option
@@ -774,10 +779,10 @@ class ModelSerializer(Serializer):
"on serializer '%s'." %
(field_name, self.__class__.__name__))
ret[field_name].read_only = True
-
+
# Ensure that 'write_only_fields' is an iterable
- assert isinstance(self.opts.write_only_fields, (list, tuple)), '`write_only_fields` must be a list or tuple'
-
+ assert isinstance(self.opts.write_only_fields, (list, tuple)), '`write_only_fields` must be a list or tuple'
+
for field_name in self.opts.write_only_fields:
assert field_name not in self.base_fields.keys(), (
"field '%s' on serializer '%s' specified in "
@@ -788,7 +793,7 @@ class ModelSerializer(Serializer):
"Non-existant field '%s' specified in `write_only_fields` "
"on serializer '%s'." %
(field_name, self.__class__.__name__))
- ret[field_name].write_only = True
+ ret[field_name].write_only = True
return ret
@@ -827,6 +832,19 @@ class ModelSerializer(Serializer):
if model_field:
kwargs['required'] = not(model_field.null or model_field.blank)
+ if model_field.help_text is not None:
+ kwargs['help_text'] = model_field.help_text
+ if model_field.verbose_name is not None:
+ kwargs['label'] = model_field.verbose_name
+
+ if not model_field.editable:
+ kwargs['read_only'] = True
+
+ if model_field.verbose_name is not None:
+ kwargs['label'] = model_field.verbose_name
+
+ if model_field.help_text is not None:
+ kwargs['help_text'] = model_field.help_text
return PrimaryKeyRelatedField(**kwargs)
@@ -866,6 +884,10 @@ class ModelSerializer(Serializer):
issubclass(model_field.__class__, models.PositiveSmallIntegerField):
kwargs['min_value'] = 0
+ if model_field.null and \
+ issubclass(model_field.__class__, (models.CharField, models.TextField)):
+ kwargs['allow_none'] = True
+
attribute_dict = {
models.CharField: ['max_length'],
models.CommaSeparatedIntegerField: ['max_length'],
@@ -892,15 +914,17 @@ class ModelSerializer(Serializer):
Return a list of field names to exclude from model validation.
"""
cls = self.opts.model
- opts = get_concrete_model(cls)._meta
+ opts = cls._meta.concrete_model._meta
exclusions = [field.name for field in opts.fields + opts.many_to_many]
for field_name, field in self.fields.items():
field_name = field.source or field_name
- if field_name in exclusions \
- and not field.read_only \
- and (field.required or hasattr(instance, field_name)) \
- and not isinstance(field, Serializer):
+ if (
+ field_name in exclusions
+ and not field.read_only
+ and (field.required or hasattr(instance, field_name))
+ and not isinstance(field, Serializer)
+ ):
exclusions.remove(field_name)
return exclusions
@@ -943,6 +967,8 @@ class ModelSerializer(Serializer):
# Forward m2m relations
for field in meta.many_to_many + meta.virtual_fields:
+ if isinstance(field, GenericForeignKey):
+ continue
if field.name in attrs:
m2m_data[field.name] = attrs.pop(field.name)
@@ -952,17 +978,15 @@ class ModelSerializer(Serializer):
if isinstance(self.fields.get(field_name, None), Serializer):
nested_forward_relations[field_name] = attrs[field_name]
- # Update an existing instance...
- if instance is not None:
- for key, val in attrs.items():
- try:
- setattr(instance, key, val)
- except ValueError:
- self._errors[key] = self.error_messages['required']
+ # Create an empty instance of the model
+ if instance is None:
+ instance = self.opts.model()
- # ...or create a new instance
- else:
- instance = self.opts.model(**attrs)
+ for key, val in attrs.items():
+ try:
+ setattr(instance, key, val)
+ except ValueError:
+ self._errors[key] = [self.error_messages['required']]
# Any relations that cannot be set until we've
# saved the model get hidden away on these
@@ -1087,6 +1111,10 @@ class HyperlinkedModelSerializer(ModelSerializer):
if model_field:
kwargs['required'] = not(model_field.null or model_field.blank)
+ if model_field.help_text is not None:
+ kwargs['help_text'] = model_field.help_text
+ if model_field.verbose_name is not None:
+ kwargs['label'] = model_field.verbose_name
if self.opts.lookup_field:
kwargs['lookup_field'] = self.opts.lookup_field