djangorestframework-stubs icon indicating copy to clipboard operation
djangorestframework-stubs copied to clipboard

Add functionality for inferring TypedDict from serializer again

Open mkurnikov opened this issue 4 years ago • 5 comments

mkurnikov avatar Oct 07 '19 11:10 mkurnikov

@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.

kalekseev avatar Oct 16 '19 12:10 kalekseev

Let me provide some thoughts from my try to use TypedDict for serializer.data.

Serializer instantiation

  1. 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

  2. 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.

kalekseev avatar Oct 16 '19 15:10 kalekseev

This looks pretty good, is this still in the works @kalekseev ?

Goldziher avatar Oct 10 '20 12:10 Goldziher

@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

kalekseev avatar Oct 11 '20 11:10 kalekseev

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.

intgr avatar Jun 22 '21 12:06 intgr