Fix TypeError in RelatedField.get_choices() when to_representation returns unhashable type - #9985
Fix TypeError in RelatedField.get_choices() when to_representation returns unhashable type#9985LeSingh1 wants to merge 9 commits into
TypeError in RelatedField.get_choices() when to_representation returns unhashable type#9985Conversation
…turns unhashable type When a subclass overrides to_representation() to return an unhashable type such as a dict, RelatedField.get_choices() raises: TypeError: unhashable type: 'dict' because the return value is used directly as a dictionary key. Fix by converting the representation to str() before using it as the key. This is safe since HTML select option values are always strings. Fixes encode#5141
MockObject.__str__ wraps its representation in angle brackets, so display_value() returns '<MockObject ...>'. Align the expected choices mapping with that output so the test passes.
There was a problem hiding this comment.
🟡 Changes recommended
Narrow the TypeError handling so display_value() is computed once and its errors are not misclassified.
Once you've addressed the issues Copilot identified, you can request another Copilot review.
Pull request overview
Fixes Browsable API failures when related-field representations are unhashable.
Changes:
- Falls back to string keys for unhashable representations.
- Adds regression and compatibility tests.
File summaries
| File | Description |
|---|---|
rest_framework/relations.py |
Handles unhashable choice keys. Moderate issue: the broad try can misclassify and repeat display_value() errors. |
tests/test_relations.py |
Tests unhashable and existing hashable behavior. |
Review details
- Files reviewed: 2/2 changed files
- Comments generated: 1
- Review effort level: Balanced
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
There was a problem hiding this comment.
🟡 Changes recommended
The import-blocking indentation error and overly broad TypeError handling must be fixed.
Once you've addressed the issues Copilot identified, you can request another Copilot review.
Review details
Suppressed comments (3)
Previously missed (1) — in code that hasn't changed since the last review.
rest_framework/relations.py:208
- Catching
TypeErroraround the whole insertion also swallows errors raised by a hashable key's equality comparison during a collision, incorrectly treating that key as unhashable. Probehash(value)separately, then let unrelated insertion errors propagate.
tests/test_relations.py:528
- This constructs a read-only field and then manually adds a queryset, bypassing the constructor invariant that read-only relational fields must not have querysets. Read-only fields are also excluded from HTML forms, so this does not model the reported Browsable API path; use a supported writable field configuration instead.
field = DictRelatedField(read_only=True)
field.queryset = queryset
tests/test_relations.py:544
- This preservation assertion contradicts the PR description's advertised unconditional
str(...)conversion. Preserving non-stringRelatedField.choiceskeys is an established compatibility requirement from #4379, so the implementation and test are appropriate, but the PR description should be updated to describe conditional stringification rather than claiming every key is converted.
# Keys should remain ints (not stringified), preserving existing behavior
assert list(choices.keys()) == [1, 2]
- Files reviewed: 2/2 changed files
- Comments generated: 1
- Review effort level: Balanced
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
TypeError in RelatedField.get_choices() when to_representation returns unhashable type
Summary
Fixes #5141.
When a user subclasses
RelatedFieldand overridesto_representation()to return an unhashable type (e.g. adict), visiting the endpoint in the Browsable API raises:This happens because
get_choices()uses the return value ofto_representation()directly as a dictionary key, which requires it to be hashable.Fix
Wrap the key in
str()before inserting into the dict:This is safe because:
<option value="...">values, which are always strings in HTML.iter_options()uses these keys asoption.valuefor the Browsable API select widget — string values are correct here.RelatedFieldsubclasses is unchanged, sincestr()on an int (pk), str, orHyperlinkreturns the same value.Reproduction
Before this fix, visiting this endpoint in the Browsable API raises
TypeError: unhashable type: 'dict'. After this fix it renders correctly.