djangorestframework-stubs
djangorestframework-stubs copied to clipboard
Add functionality for inferring TypedDict from serializer again
@mkurnikov actually I've started working on TypeDicts for serializer.fields and serializer.data you can check it here https://github.com/kalekseev/djangorestframework-stubs/blob/b4e23ec074fc761b7fe6424c7595619f39cdc001/mypy_drf_plugin/main.py#L30 I was planning to write some kind of rfc but didn't have time yet.
Let me provide some thoughts from my try to use TypedDict for serializer.data.
Serializer instantiation
-
On my projects we often override
__init__
in order to tweak fields settings based on current user or other context, so we need to access self.context that must contain at least "request", so it's not safe to expect that instantiation without right context will be successful. One workaround that I came to is to override init with default method https://github.com/kalekseev/djangorestframework-stubs/blob/b4e23ec074fc761b7fe6424c7595619f39cdc001/mypy_drf_plugin/main.py#L46 -
I think it's unwanted behaviour if we generate types for serializers outside of project, for that we need to detect that serializer is from project code and not from some external library
Nested serializers
There can be complex cases with nesting, for example some field may have source=related_obj.field
and other source=related_obj.field2
as a result we need to generate a nested typeddicts like data = {"relaed_obj": {"field": Type, "field2": Type}}
List serializers
Typing may become very tricky with list serializers, for example one may have nested serializers with param many=True
in that case it starts accept/return queryset instead of model and data become a list of typeddicts
data/fields TypedDict
It's not obvious should we make all keys of TypedDict as required, if code uses dynamic fields a lot (eg. add, removes fields in __init__
or update data in def data
) then it maybe better to make all keys required or at least provide some way to user to mark serializer as "has non required keys". (maybe just use settings serializer_data_keys_not_required
defaulted to false)
Utility Types
I found that typed data is useless for me if I don't have access to type itself, eg. when I pass data to a function I need to provide generated type. For that I come up with special utility type SerializerDataType
It allows me to use resulting type of serializer.data in function arguments, eg.: def process(data: SerializerDataType[MySerializer])
.
The problem with that - if we place SerializerDataType inside stub module then the code will import from stub module.
Overall I'm looking forward to better typings for DRF let me know if I can help somehow.
This looks pretty good, is this still in the works @kalekseev ?
@Goldziher I'm not working on that. I've tested type checked serializer.data on one of my big projects with extensive usage of dynamic fields and I got a feeling that at this point it adds more friction than value, so I would concentrate on overall type improvements first. If someone really need fully type checked serializers I would recommend to take a look at alternatives, eg.: https://github.com/samuelcolvin/pydantic
Another alternative: I've been using djangorestframework-dataclasses and have been happy with it: https://github.com/oxan/djangorestframework-dataclasses
Pydantic is probably a cleaner implementation without DRF's baggage, but DRF-dataclasses integrates better with Django REST Framework.