From 2f6e5d0509632364befe375d013a1d3acc0b245b Mon Sep 17 00:00:00 2001 From: James Beith Date: Mon, 10 Aug 2015 11:03:57 +0100 Subject: [PATCH 1/5] Add display value method Returns the text representation of the instance. Subclasses can override this method to provide a different display value used for populating the `choices` property. --- rest_framework/relations.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/rest_framework/relations.py b/rest_framework/relations.py index 330d17c08..8f433d421 100644 --- a/rest_framework/relations.py +++ b/rest_framework/relations.py @@ -148,7 +148,7 @@ class RelatedField(Field): return OrderedDict([ ( six.text_type(self.to_representation(item)), - six.text_type(item) + six.text_type(self.display_value(item)) ) for item in queryset ]) @@ -160,6 +160,9 @@ class RelatedField(Field): def iter_options(self): return iter_options(self.grouped_choices) + def display_value(self, instance): + return six.text_type(instance) + class StringRelatedField(RelatedField): """ From e9d9cb701163ebef5b43535c39909f953977eb08 Mon Sep 17 00:00:00 2001 From: James Beith Date: Mon, 10 Aug 2015 12:19:46 +0100 Subject: [PATCH 2/5] Add tests for `display_value` --- tests/test_model_serializer.py | 46 +++++++++++++++++++++++++++++++++- 1 file changed, 45 insertions(+), 1 deletion(-) diff --git a/tests/test_model_serializer.py b/tests/test_model_serializer.py index 00c6319ea..62a9cc05a 100644 --- a/tests/test_model_serializer.py +++ b/tests/test_model_serializer.py @@ -21,7 +21,7 @@ from django.utils import six from rest_framework import serializers from rest_framework.compat import DurationField as ModelDurationField -from rest_framework.compat import unicode_repr +from rest_framework.compat import OrderedDict, unicode_repr def dedent(blocktext): @@ -546,6 +546,50 @@ class TestRelationalFieldMappings(TestCase): self.assertEqual(unicode_repr(TestSerializer()), expected) +class DisplayValueTargetModel(models.Model): + name = models.CharField(max_length=100) + + def __str__(self): + return '%s Color' % (self.name) + + +class DisplayValueModel(models.Model): + color = models.ForeignKey(DisplayValueTargetModel) + + +class TestRelationalFieldDisplayValue(TestCase): + def setUp(self): + DisplayValueTargetModel.objects.bulk_create([ + DisplayValueTargetModel(name='Red'), + DisplayValueTargetModel(name='Yellow'), + DisplayValueTargetModel(name='Green'), + ]) + + def test_default_display_value(self): + class TestSerializer(serializers.ModelSerializer): + class Meta: + model = DisplayValueModel + + serializer = TestSerializer() + expected = OrderedDict([('1', 'Red Color'), ('2', 'Yellow Color'), ('3', 'Green Color')]) + self.assertEqual(serializer.fields['color'].choices, expected) + + def test_custom_display_value(self): + class TestField(serializers.PrimaryKeyRelatedField): + def display_value(self, instance): + return 'My %s Color' % (instance.name) + + class TestSerializer(serializers.ModelSerializer): + color = TestField(queryset=DisplayValueTargetModel.objects.all()) + + class Meta: + model = DisplayValueModel + + serializer = TestSerializer() + expected = OrderedDict([('1', 'My Red Color'), ('2', 'My Yellow Color'), ('3', 'My Green Color')]) + self.assertEqual(serializer.fields['color'].choices, expected) + + class TestIntegration(TestCase): def setUp(self): self.foreign_key_target = ForeignKeyTargetModel.objects.create( From 66ae19229e8e0d1f99c21501e821facfb5d0566d Mon Sep 17 00:00:00 2001 From: James Beith Date: Mon, 10 Aug 2015 12:38:27 +0100 Subject: [PATCH 3/5] Add docs for `display_value` --- docs/api-guide/relations.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/docs/api-guide/relations.md b/docs/api-guide/relations.md index bc24667de..ba3f88063 100644 --- a/docs/api-guide/relations.md +++ b/docs/api-guide/relations.md @@ -537,6 +537,14 @@ If you explicitly specify a relational field pointing to a ``ManyToManyField`` with a through model, be sure to set ``read_only`` to ``True``. +## The `display_value` method + +The `__str__` (`__unicode__` on Python 2) method of the model will be called to generate string representations of the objects used to populate the `choices` property. To provide customized representations, override `display_value` of a `RelatedField` subclass. This method will receive a model object, and should return a string suitable for representing it. For example: + + class TrackPrimaryKeyRelatedField(serializers.PrimaryKeyRelatedField): + def display_value(self, instance): + return 'Track: %s' % (instance.title) + --- # Third Party Packages From eaf61449a85b9563d23b2922425c9ed4a67b7bea Mon Sep 17 00:00:00 2001 From: James Beith Date: Mon, 10 Aug 2015 13:02:07 +0100 Subject: [PATCH 4/5] Explain use case for `display_value` in docs --- docs/api-guide/relations.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/api-guide/relations.md b/docs/api-guide/relations.md index ba3f88063..152f44cdc 100644 --- a/docs/api-guide/relations.md +++ b/docs/api-guide/relations.md @@ -539,7 +539,7 @@ to ``True``. ## The `display_value` method -The `__str__` (`__unicode__` on Python 2) method of the model will be called to generate string representations of the objects used to populate the `choices` property. To provide customized representations, override `display_value` of a `RelatedField` subclass. This method will receive a model object, and should return a string suitable for representing it. For example: +The `__str__` (`__unicode__` on Python 2) method of the model will be called to generate string representations of the objects used to populate the `choices` property. These choices are used to populate select HTML inputs in the browsable API. To provide customized representations for such inputs, override `display_value` of a `RelatedField` subclass. This method will receive a model object, and should return a string suitable for representing it. For example: class TrackPrimaryKeyRelatedField(serializers.PrimaryKeyRelatedField): def display_value(self, instance): From 5c0a2b79b3173656b3b95215920f834b332cc7fd Mon Sep 17 00:00:00 2001 From: James Beith Date: Mon, 10 Aug 2015 15:33:04 +0100 Subject: [PATCH 5/5] Remove converting a string in to a string The `display_value` method returns a text type. --- rest_framework/relations.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rest_framework/relations.py b/rest_framework/relations.py index 8f433d421..e0edc645f 100644 --- a/rest_framework/relations.py +++ b/rest_framework/relations.py @@ -148,7 +148,7 @@ class RelatedField(Field): return OrderedDict([ ( six.text_type(self.to_representation(item)), - six.text_type(self.display_value(item)) + self.display_value(item) ) for item in queryset ])