diff --git a/rest_framework/fields.py b/rest_framework/fields.py index 446732c3a..328e93eff 100644 --- a/rest_framework/fields.py +++ b/rest_framework/fields.py @@ -178,7 +178,7 @@ class Field(object): # In order to enforce a consistent style, we error if a redundant # 'source' argument has been used. For example: # my_field = serializer.CharField(source='my_field') - assert self._kwargs.get('source') != field_name, ( + assert self.source != field_name, ( "It is redundant to specify `source='%s'` on field '%s' in " "serializer '%s', because it is the same as the field name. " "Remove the `source` keyword argument." % @@ -883,17 +883,32 @@ class SerializerMethodField(Field): def get_extra_info(self, obj): return ... # Calculate some data to return. """ - def __init__(self, method_attr=None, **kwargs): - self.method_attr = method_attr + def __init__(self, method_name=None, **kwargs): + self.method_name = method_name kwargs['source'] = '*' kwargs['read_only'] = True super(SerializerMethodField, self).__init__(**kwargs) + def bind(self, field_name, parent): + # In order to enforce a consistent style, we error if a redundant + # 'method_name' argument has been used. For example: + # my_field = serializer.CharField(source='my_field') + default_method_name = 'get_{field_name}'.format(field_name=field_name) + assert self.method_name != default_method_name, ( + "It is redundant to specify `%s` on SerializerMethodField '%s' in " + "serializer '%s', because it is the same as the default method name. " + "Remove the `method_name` argument." % + (self.method_name, field_name, parent.__class__.__name__) + ) + + # The method name should default to `get_{field_name}`. + if self.method_name is None: + self.method_name = default_method_name + + super(SerializerMethodField, self).bind(field_name, parent) + def to_representation(self, value): - method_attr = self.method_attr - if method_attr is None: - method_attr = 'get_{field_name}'.format(field_name=self.field_name) - method = getattr(self.parent, method_attr) + method = getattr(self.parent, self.method_name) return method(value) diff --git a/tests/test_fields.py b/tests/test_fields.py index ebb88d3d6..003b4b8c6 100644 --- a/tests/test_fields.py +++ b/tests/test_fields.py @@ -710,3 +710,33 @@ class TestMultipleChoiceField(FieldValues): ('diesel', 'Diesel'), ] ) + + +# Tests for SerializerMethodField. +# -------------------------------- + +class TestSerializerMethodField: + def test_serializer_method_field(self): + class ExampleSerializer(serializers.Serializer): + example_field = serializers.SerializerMethodField() + + def get_example_field(self, obj): + return 'ran get_example_field(%d)' % obj['example_field'] + + serializer = ExampleSerializer({'example_field': 123}) + assert serializer.data == { + 'example_field': 'ran get_example_field(123)' + } + + def test_redundant_method_name(self): + class ExampleSerializer(serializers.Serializer): + example_field = serializers.SerializerMethodField('get_example_field') + + with pytest.raises(AssertionError) as exc_info: + ExampleSerializer() + assert str(exc_info.value) == ( + "It is redundant to specify `get_example_field` on " + "SerializerMethodField 'example_field' in serializer " + "'ExampleSerializer', because it is the same as the default " + "method name. Remove the `method_name` argument." + )