diff --git a/rest_framework/relations.py b/rest_framework/relations.py index 97a6417ea87..1d20ee82ba5 100644 --- a/rest_framework/relations.py +++ b/rest_framework/relations.py @@ -13,7 +13,9 @@ from django.utils.translation import ugettext_lazy as _ from rest_framework.compat import OrderedDict -from rest_framework.fields import Field, empty, get_attribute +from rest_framework.fields import ( + Field, empty, get_attribute, is_simple_callable +) from rest_framework.reverse import reverse from rest_framework.utils import html @@ -106,7 +108,12 @@ def get_attribute(self, instance): # Optimized case, return a mock object only containing the pk attribute. try: instance = get_attribute(instance, self.source_attrs[:-1]) - return PKOnlyObject(pk=instance.serializable_value(self.source_attrs[-1])) + + value = instance.serializable_value(self.source_attrs[-1]) + if is_simple_callable(value): + value = value().pk + + return PKOnlyObject(pk=value) except AttributeError: pass diff --git a/tests/test_relations_pk.py b/tests/test_relations_pk.py index 169f7d9c5e9..505d4d959df 100644 --- a/tests/test_relations_pk.py +++ b/tests/test_relations_pk.py @@ -30,6 +30,16 @@ class Meta: fields = ('id', 'name', 'sources') +class ForeignKeyTargetCallableSourceSerializer(serializers.ModelSerializer): + class Meta: + model = ForeignKeyTarget + fields = ('id', 'name', 'first_source') + + first_source = serializers.PrimaryKeyRelatedField( + read_only=True, + source='get_first_source') + + class ForeignKeySourceSerializer(serializers.ModelSerializer): class Meta: model = ForeignKeySource @@ -450,3 +460,21 @@ def test_reverse_foreign_key_retrieve_with_null(self): {'id': 2, 'name': 'target-2', 'nullable_source': 1}, ] self.assertEqual(serializer.data, expected) + + +class PKRelationCallableSourceTests(TestCase): + + def setUp(self): + self.target = ForeignKeyTarget.objects.create(name='target-1') + self.first_source = ForeignKeySource.objects.create(id=10, name='source-1', target=self.target) + ForeignKeySource.objects.create(name='source-2', target=self.target) + + def test_relation_field_callable_source(self): + serializer = ForeignKeyTargetCallableSourceSerializer(self.target) + expected = { + 'id': 1, + 'name': 'target-1', + 'first_source': 10, + } + with self.assertNumQueries(1): + self.assertEqual(serializer.data, expected)