diff --git a/docs/api-guide/serializers.md b/docs/api-guide/serializers.md index 2338b879d..902179bab 100644 --- a/docs/api-guide/serializers.md +++ b/docs/api-guide/serializers.md @@ -107,21 +107,21 @@ where some of the attributes of an object might not be simple datatypes such as The `Serializer` class is itself a type of `Field`, and can be used to represent relationships where one object type is nested inside another. class UserSerializer(serializers.Serializer): - email = serializers.EmailField() - username = serializers.CharField() - - def restore_object(self, attrs, instance=None): - return User(**attrs) - + email = serializers.Field() + username = serializers.Field() class CommentSerializer(serializers.Serializer): user = UserSerializer() - title = serializers.CharField() - content = serializers.CharField(max_length=200) - created = serializers.DateTimeField() - - def restore_object(self, attrs, instance=None): - return Comment(**attrs) + title = serializers.Field() + content = serializers.Field() + created = serializers.Field() + +--- + +**Note**: Nested serializers are only suitable for read-only representations, as there are cases where they would have ambiguous or non-obvious behavior if used when updating instances. For read-write representations you should always use a flat representation, by using one of the `RelatedField` subclasses. + +--- + ## Creating custom fields @@ -225,40 +225,54 @@ For example: ## Specifiying nested serialization -The default `ModelSerializer` uses primary keys for relationships, but you can also easily generate nested representations using the `nested` option: +The default `ModelSerializer` uses primary keys for relationships, but you can also easily generate nested representations using the `depth` option: class AccountSerializer(serializers.ModelSerializer): class Meta: model = Account exclude = ('id',) - nested = True + depth = 1 -The `nested` option may be set to either `True`, `False`, or an integer value. If given an integer value it indicates the depth of relationships that should be traversed before reverting to a flat representation. +The `depth` option should be set to an integer value that indicates the depth of relationships that should be traversed before reverting to a flat representation. -When serializing objects using a nested representation any occurances of recursion will be recognised, and will fall back to using a flat representation. +## Customising the default fields -## Customising the default fields used by a ModelSerializer +You can create customized subclasses of `ModelSerializer` that use a different set of default fields for the representation, by overriding various `get__field` methods. +Each of these methods may either return a field or serializer instance, or `None`. +### get_pk_field - class AccountSerializer(serializers.ModelSerializer): - class Meta: - model = Account +**Signature**: `.get_pk_field(self, model_field)` +Returns the field instance that should be used to represent the pk field. + +### get_nested_field + +**Signature**: `.get_nested_field(self, model_field)` + +Returns the field instance that should be used to represent a related field when `depth` is specified as being non-zero. + +### get_related_field + +**Signature**: `.get_related_field(self, model_field, to_many=False)` + +Returns the field instance that should be used to represent a related field when `depth` is not specified, or when nested representations are being used and the depth reaches zero. + +### get_field + +**Signature**: `.get_field(self, model_field)` + +Returns the field instance that should be used for non-relational, non-pk fields. + +### Example: + +The following custom model serializer could be used as a base class for model serializers that should always exclude the pk by default. + + class NoPKModelSerializer(serializers.ModelSerializer): def get_pk_field(self, model_field): - return serializers.Field(read_only=True) + return None - def get_nested_field(self, model_field): - return serializers.ModelSerializer() - - def get_related_field(self, model_field, to_many=False): - queryset = model_field.rel.to._default_manager - if to_many: - return serializers.ManyRelatedField(queryset=queryset) - return serializers.RelatedField(queryset=queryset) - - def get_field(self, model_field): - return serializers.ModelField(model_field=model_field) [cite]: https://groups.google.com/d/topic/django-users/sVFaOfQi4wY/discussion diff --git a/rest_framework/serializers.py b/rest_framework/serializers.py index 8a8953431..31d9d7a5d 100644 --- a/rest_framework/serializers.py +++ b/rest_framework/serializers.py @@ -74,7 +74,7 @@ class SerializerOptions(object): Meta class options for Serializer """ def __init__(self, meta): - self.nested = getattr(meta, 'nested', False) + self.depth = getattr(meta, 'depth', 0) self.fields = getattr(meta, 'fields', ()) self.exclude = getattr(meta, 'exclude', ()) @@ -156,10 +156,8 @@ class BaseSerializer(Field): """ super(BaseSerializer, self).initialize(parent) self.stack = parent.stack[:] - if parent.opts.nested and not isinstance(parent.opts.nested, bool): - self.opts.nested = parent.opts.nested - 1 - else: - self.opts.nested = parent.opts.nested + if parent.opts.depth: + self.opts.depth = parent.opts.depth - 1 ##### # Methods to convert or revert from objects <--> primative representations. @@ -182,14 +180,10 @@ class BaseSerializer(Field): ret = self._dict_class() ret.fields = {} - fields = self.get_fields(serialize=True, obj=obj, nested=self.opts.nested) + fields = self.get_fields(serialize=True, obj=obj, nested=bool(self.opts.depth)) for field_name, field in fields.items(): key = self.get_field_key(field_name) - try: - value = field.field_to_native(obj, field_name) - except RecursionOccured: - field = self.get_fields(serialize=True, obj=obj, nested=False)[field_name] - value = field.field_to_native(obj, field_name) + value = field.field_to_native(obj, field_name) ret[key] = value ret.fields[key] = field return ret @@ -199,7 +193,7 @@ class BaseSerializer(Field): Core of deserialization, together with `restore_object`. Converts a dictionary of data into a dictionary of deserialized fields. """ - fields = self.get_fields(serialize=False, data=data, nested=self.opts.nested) + fields = self.get_fields(serialize=False, data=data, nested=bool(self.opts.depth)) reverted_data = {} for field_name, field in fields.items(): try: @@ -213,7 +207,8 @@ class BaseSerializer(Field): """ Run `validate_()` and `validate()` methods on the serializer """ - fields = self.get_fields(serialize=False, data=attrs, nested=self.opts.nested) + # TODO: refactor this so we're not determining the fields again + fields = self.get_fields(serialize=False, data=attrs, nested=bool(self.opts.depth)) for field_name, field in fields.items(): try: