aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTom Christie2012-10-28 20:43:43 +0000
committerTom Christie2012-10-28 20:43:43 +0000
commit351382fe35f966c989b27add5bb04d0d983a99ee (patch)
treef0940e72394e01f8b33537c4a3fbe6432e010c4a
parent6e4ab09aae8295e4ef722d59894bc2934435ae46 (diff)
downloaddjango-rest-framework-351382fe35f966c989b27add5bb04d0d983a99ee.tar.bz2
nested -> depth
-rw-r--r--docs/api-guide/serializers.md76
-rw-r--r--rest_framework/serializers.py21
2 files changed, 53 insertions, 44 deletions
diff --git a/docs/api-guide/serializers.md b/docs/api-guide/serializers.md
index 2338b879..902179ba 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_type>_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)`
- def get_pk_field(self, model_field):
- return serializers.Field(read_only=True)
+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
- def get_nested_field(self, model_field):
- return serializers.ModelSerializer()
+**Signature**: `.get_related_field(self, model_field, to_many=False)`
- 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)
+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 None
- 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 8a895343..31d9d7a5 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_<fieldname>()` 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: