diff --git a/rest_framework/relations.py b/rest_framework/relations.py index 4409bce77c..47452cd6f4 100644 --- a/rest_framework/relations.py +++ b/rest_framework/relations.py @@ -198,9 +198,14 @@ def get_choices(self, cutoff=None): if cutoff is not None: queryset = queryset[:cutoff] - return { - self.to_representation(item): self.display_value(item) for item in queryset - } + result = {} + for item in queryset: + value = self.to_representation(item) + try: + result[value] = self.display_value(item) + except TypeError: + result[str(value)] = self.display_value(item) + return result @property def choices(self): diff --git a/tests/test_relations.py b/tests/test_relations.py index b9ab157896..b72b0266b5 100644 --- a/tests/test_relations.py +++ b/tests/test_relations.py @@ -509,6 +509,41 @@ def test_get_value_multi_dictionary_partial(self): assert empty == self.field.get_value(mvd) +class TestRelatedFieldGetChoicesUnhashable(APISimpleTestCase): + def test_get_choices_with_unhashable_to_representation(self): + """get_choices() should not raise TypeError when to_representation returns an unhashable type.""" + queryset = MockQueryset([ + MockObject(pk=1, name='foo'), + MockObject(pk=2, name='bar'), + ]) + + class DictRelatedField(serializers.RelatedField): + def to_representation(self, obj): + return {'id': obj.pk, 'name': obj.name} + + def to_internal_value(self, data): + pass + + field = DictRelatedField(read_only=True) + field.queryset = queryset + choices = field.get_choices() + assert choices == { + "{'id': 1, 'name': 'foo'}": '', + "{'id': 2, 'name': 'bar'}": '', + } + + def test_get_choices_with_hashable_to_representation_unchanged(self): + """get_choices() should return original type keys when to_representation returns a hashable.""" + queryset = MockQueryset([ + MockObject(pk=1, name='foo'), + MockObject(pk=2, name='bar'), + ]) + field = serializers.PrimaryKeyRelatedField(queryset=queryset) + choices = field.get_choices() + # Keys should remain ints (not stringified), preserving existing behavior + assert list(choices.keys()) == [1, 2] + + class TestHyperlink: def setup_method(self): self.default_hyperlink = serializers.Hyperlink('http://example.com', 'test')