feat(apidocs): Let serializers declare the type of validated_data - #123923
Merged
Conversation
azulus
force-pushed
the
jeremy/input-rules-3-typed-validated-data
branch
from
September 9, 2026 17:19
9233555 to
0791294
Compare
azulus
marked this pull request as ready for review
September 9, 2026 17:26
gricha
approved these changes
Sep 9, 2026
azulus
force-pushed
the
jeremy/input-rules-3-typed-validated-data
branch
from
September 9, 2026 17:56
0791294 to
a031b4a
Compare
Contributor
Sentry Snapshot Testing
|
Handlers read input through serializer.validated_data, which is Any, so nothing relates it to the serializer's fields and a misspelled key fails at runtime rather than in review. Request[T] does not help: it types request.data, which is not the path most handlers take. Vendors rest_framework/serializers.pyi with one change. BaseSerializer takes a second type parameter for the shape of validated_data, threaded through Serializer and ListSerializer, so a validator can opt in with Serializer[Any, MonitorData] and validated_data["nmae"] becomes an error. Both parameters default to Any, so the 219 unparameterized subclasses and the 171 already passing one argument need no edit. Verified against the whole tree: 46 errors with the stub and 46 without, all of them in an untracked scratch directory and none in sentry source. Schema output is byte-identical. Keys are declared NotRequired because DRF omits absent keys under partial=True, so a shape claiming a key is present would be wrong at those 46 call sites. Checking requiredness needs the field's call arguments, which mypy discards across module boundaries, and is left to a later change. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Review raised that these cases run mypy on a file outside the project tree, so a stub that stopped being found would leave validated_data as Any and every assertion would pass without checking anything. The suite is not vacuous today: removing the stub fails all six cases, because each one asserts a specific diagnostic rather than silence. But that safety was incidental, so state it directly. The new case returns a str-typed key as int, which errors only when the stub is in effect and passes when validated_data falls back to Any. Keeping the repo's own config rather than a local one, since resolving the stub the same way CI does is the point of running it this way. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
azulus
force-pushed
the
jeremy/input-rules-3-typed-validated-data
branch
from
September 9, 2026 19:34
a031b4a to
c7baa17
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Handlers read request input through
serializer.validated_data, which is typedAny. Nothing connects it to the serializer's own fields, so a misspelled key survives review and fails at runtime.This vendors
rest_framework/serializers.pyiwith one change:BaseSerializertakes a second type parameter for the shape ofvalidated_data, threaded throughSerializerandListSerializer. A validator opts in by declaringSerializer[Any, MonitorData], after whichvalidated_data["nmae"]is a type error and**validated_datais checked against the callee's keywords.Both type parameters default to
Any, so the 219 unparameterized subclasses and the 171 already passing a single argument are untouched, and the repository's error count does not move. Generated schema output is byte-identical, because drf-spectacular reads field objects rather than type arguments.Request[T]was the obvious alternative and does not work. It typesrequest.data, which is not the path most handlers take, so renaming a key read out ofvalidated_datastill goes uncaught.Declare keys
NotRequired. DRF omits absent keys underpartial=True, so a shape claiming a key is present would be wrong at every such call site, and a shape that is confidently wrong is worse than no shape at all. Verifying requiredness needs a field's call arguments, which mypy discards once a class crosses a module boundary, so that is left to a later change.