aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTom Christie2015-01-05 14:32:12 +0000
committerTom Christie2015-01-05 14:32:12 +0000
commitb6ca7248ebcf95a95e1911aa0b130f653b8bf690 (patch)
tree3da53011f0e215da5e13efcaf39223f88edfefce
parent8cf37449715c32c4a692667814466c7f32e8734f (diff)
downloaddjango-rest-framework-b6ca7248ebcf95a95e1911aa0b130f653b8bf690.tar.bz2
required=False allows omission of value for output. Closes #2342
-rw-r--r--docs/api-guide/fields.md2
-rw-r--r--rest_framework/fields.py2
-rw-r--r--rest_framework/serializers.py8
-rw-r--r--tests/test_serializer.py62
4 files changed, 73 insertions, 1 deletions
diff --git a/docs/api-guide/fields.md b/docs/api-guide/fields.md
index 946e355d..b3d274dd 100644
--- a/docs/api-guide/fields.md
+++ b/docs/api-guide/fields.md
@@ -41,6 +41,8 @@ Defaults to `False`
Normally an error will be raised if a field is not supplied during deserialization.
Set to false if this field is not required to be present during deserialization.
+Setting this to `False` also allows the object attribute or dictionary key to be omitted from output when serializing the instance. If the key is not present it will simply not be included in the output representation.
+
Defaults to `True`.
### `allow_null`
diff --git a/rest_framework/fields.py b/rest_framework/fields.py
index aab80982..cc9410aa 100644
--- a/rest_framework/fields.py
+++ b/rest_framework/fields.py
@@ -288,6 +288,8 @@ class Field(object):
try:
return get_attribute(instance, self.source_attrs)
except (KeyError, AttributeError) as exc:
+ if not self.required and self.default is empty:
+ raise SkipField()
msg = (
'Got {exc_type} when attempting to get a value for field '
'`{field}` on serializer `{serializer}`.\nThe serializer '
diff --git a/rest_framework/serializers.py b/rest_framework/serializers.py
index 6f89df0d..53f092d7 100644
--- a/rest_framework/serializers.py
+++ b/rest_framework/serializers.py
@@ -419,8 +419,14 @@ class Serializer(BaseSerializer):
fields = [field for field in self.fields.values() if not field.write_only]
for field in fields:
- attribute = field.get_attribute(instance)
+ try:
+ attribute = field.get_attribute(instance)
+ except SkipField:
+ continue
+
if attribute is None:
+ # We skip `to_representation` for `None` values so that
+ # fields do not have to explicitly deal with that case.
ret[field.field_name] = None
else:
ret[field.field_name] = field.to_representation(attribute)
diff --git a/tests/test_serializer.py b/tests/test_serializer.py
index c17b6d8c..68bbbe98 100644
--- a/tests/test_serializer.py
+++ b/tests/test_serializer.py
@@ -1,5 +1,6 @@
# coding: utf-8
from __future__ import unicode_literals
+from .utils import MockObject
from rest_framework import serializers
from rest_framework.compat import unicode_repr
import pytest
@@ -216,3 +217,64 @@ class TestUnicodeRepr:
instance = ExampleObject()
serializer = ExampleSerializer(instance)
repr(serializer) # Should not error.
+
+
+class TestNotRequiredOutput:
+ def test_not_required_output_for_dict(self):
+ """
+ 'required=False' should allow a dictionary key to be missing in output.
+ """
+ class ExampleSerializer(serializers.Serializer):
+ omitted = serializers.CharField(required=False)
+ included = serializers.CharField()
+
+ serializer = ExampleSerializer(data={'included': 'abc'})
+ serializer.is_valid()
+ assert serializer.data == {'included': 'abc'}
+
+ def test_not_required_output_for_object(self):
+ """
+ 'required=False' should allow an object attribute to be missing in output.
+ """
+ class ExampleSerializer(serializers.Serializer):
+ omitted = serializers.CharField(required=False)
+ included = serializers.CharField()
+
+ def create(self, validated_data):
+ return MockObject(**validated_data)
+
+ serializer = ExampleSerializer(data={'included': 'abc'})
+ serializer.is_valid()
+ serializer.save()
+ assert serializer.data == {'included': 'abc'}
+
+ def test_default_required_output_for_dict(self):
+ """
+ 'default="something"' should require dictionary key.
+
+ We need to handle this as the field will have an implicit
+ 'required=False', but it should still have a value.
+ """
+ class ExampleSerializer(serializers.Serializer):
+ omitted = serializers.CharField(default='abc')
+ included = serializers.CharField()
+
+ serializer = ExampleSerializer({'included': 'abc'})
+ with pytest.raises(KeyError):
+ serializer.data
+
+ def test_default_required_output_for_object(self):
+ """
+ 'default="something"' should require object attribute.
+
+ We need to handle this as the field will have an implicit
+ 'required=False', but it should still have a value.
+ """
+ class ExampleSerializer(serializers.Serializer):
+ omitted = serializers.CharField(default='abc')
+ included = serializers.CharField()
+
+ instance = MockObject(included='abc')
+ serializer = ExampleSerializer(instance)
+ with pytest.raises(AttributeError):
+ serializer.data