Add support for passing callable as source argument for a field.

Sometimes it is desirable that the serialized form will contain a field that
is not directly accessible through a model attribute or method. That may
happen, for instance, if you are serializing a third-party model which you
cannot modify.

This change adds support for passing a callable as a `source` argument
for a field. That callable accepts a single argument which is the object
being serialized, and in turn, returns the value that goes into the field.
This commit is contained in:
Nadav Samet 2013-06-04 21:29:33 -07:00
parent f1251e8c58
commit 9eba60f5b0
3 changed files with 24 additions and 1 deletions

View File

@ -20,7 +20,7 @@ Each serializer field class constructor takes at least these arguments. Some Fi
### `source`
The name of the attribute that will be used to populate the field. May be a method that only takes a `self` argument, such as `Field(source='get_absolute_url')`, or may use dotted notation to traverse attributes, such as `Field(source='user.email')`.
The name of the attribute that will be used to populate the field. May be a method that only takes a `self` argument, such as `Field(source='get_absolute_url')`, or may use dotted notation to traverse attributes, such as `Field(source='user.email')`. Alternatively, `source` can be a callable that takes a single argument (the object being serialized) and returns the value that will populate this field.
The value `source='*'` has a special meaning, and is used to indicate that the entire object should be passed through to the field. This can be useful for creating nested representations. (See the implementation of the `PaginationSerializer` class for an example.)

View File

@ -160,6 +160,9 @@ class Field(object):
source = self.source or field_name
value = obj
if callable(source):
return source(value)
for component in source.split('.'):
value = get_component(value, component)
if value is None:

View File

@ -803,6 +803,26 @@ class CallableDefaultValueTests(TestCase):
self.assertEqual(instance.text, 'overridden')
class CallableSourceTests(TestCase):
def setUp(self):
class CommentSerializer(serializers.Serializer):
email = serializers.EmailField()
content = serializers.CharField(max_length=1000)
length = serializers.IntegerField(
source=lambda comment: len(comment.content),
read_only=True)
self.serializer_class = CommentSerializer
def test_callable_source(self):
instance = Comment('user@email.com', 'foobar', None)
serializer = self.serializer_class(instance=instance)
self.assertEquals(serializer.data, {
'email': 'user@email.com',
'content': 'foobar',
'length': 6
})
class ManyRelatedTests(TestCase):
def test_reverse_relations(self):
post = BlogPost.objects.create(title="Test blog post")