Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 8 additions & 3 deletions rest_framework/relations.py
Original file line number Diff line number Diff line change
Expand Up @@ -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):
Expand Down
35 changes: 35 additions & 0 deletions tests/test_relations.py
Original file line number Diff line number Diff line change
Expand Up @@ -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'}": '<MockObject name=foo, pk=1>',
"{'id': 2, 'name': 'bar'}": '<MockObject name=bar, pk=2>',
}

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')
Expand Down
Loading