Open
Description
Checklist
- I have verified that that issue exists against the
master
branch of Django REST framework. - I have searched for similar issues in both open and closed tickets and cannot find a duplicate.
- This is not a usage question. (Those should be directed to the discussion group instead.)
- This cannot be dealt with as a third party library. (We prefer new functionality to be in the form of third party libraries where possible.)
- I have reduced the issue to the simplest possible case.
- I have included a failing test as a pull request. (If you are unable to do so we can still accept the issue.)
Steps to reproduce
Create a subclass of rest_framework.serializers.RelatedField
that overrides to_representation
such that it returns an unhashable type, such as a dict
.
from rest_framework import serializers
class MyRelatedField(serializers.RelatedField):
def to_representation(self, obj):
return {
'id': obj.pk,
}
class MySerializer(serializers.Serializer):
related_field = MyRelatedField()
Use this Serializer
in a view, then visit the view's endpoint in the browsable API.
Expected behavior
The page renders.
Actual behavior
An uncaught exception is thrown:
TypeError: unhashable type: 'dict'
(44 additional frame(s) were not displayed)
...
File "rest_framework/templatetags/rest_framework.py", line 91, in render_field
return renderer.render_field(field, style)
File "rest_framework/renderers.py", line 350, in render_field
return template_render(template, context)
File "rest_framework/compat.py", line 332, in template_render
return template.render(context, request=request)
File "rest_framework/relations.py", line 204, in iter_options
self.get_choices(cutoff=self.html_cutoff),
File "rest_framework/relations.py", line 191, in get_choices
for item in queryset
TypeError: unhashable type: 'dict'
Additional notes
This is caused by the change in #4379 .
We are currently working around the issue by overriding get_choices
. Since there is no hook to format the keys in the return value, we had to copy and paste RelatedField's implmentation.
class MyRelatedField(serializers.RelatedField):
def to_representation(self, obj):
return {
'id': obj.pk,
}
# Below code is copied from rest_framework.serializers.RelatedField
# because we need to override the keys in the return value
def get_choices(self, cutoff=None):
queryset = self.get_queryset()
if queryset is None:
# Ensure that field.choices returns something sensible
# even when accessed with a read-only field.
return {}
if cutoff is not None:
queryset = queryset[:cutoff]
return OrderedDict([
(
# This is the only line that differs
# from the RelatedField's implementation
item.pk,
self.display_value(item)
)
for item in queryset
])
One possible solution would be to expose a hook to format the keys of the returned dictionary (like display_value
, except for the keys).