Skip to content

Incorrect type when creating Serializer with many=True #827

@intgr

Description

@intgr

TL;DR: Serializer behavior with many=True is funky. djangorestframework-stubs 3.16.3 changed the behavior of ModelSerializer.instance attribute. If this caused a regression in your use case, please comment below.

Problem

>>> from rest_framework.serializers import *
>>> class MySer(Serializer):
...     a = CharField(10)
...
>>> type(MySer())
<class '__main__.MySer'>
>>> type(MySer(many=True))
<class 'rest_framework.serializers.ListSerializer'>

Django REST Framework contains a particular trick: when creating an instance of any class that derives from Serializer with the parameter many=True, then the constructor does not actually return an instance of the class itself, but wraps the instance with ListSerializer. This is done using Python's magic __new__() method:

https://github.com/encode/django-rest-framework/blob/0f576223f22e162b3ef72247883e243a47aa36af/rest_framework/serializers.py#L123-L128

Currently, this cannot be accurately described for the mypy type checker, because the return types from __new__() are constrained: they can only be subclasses of the "self" type:

ModelSerializer many=True work-around was removed

Up until version 3.16.2, djangorestframework-stubs contained a hack in the ModelSerializer class. The instance field was typed as Model | Sequence[Model], to acommodate for the fact that ModelSerializer could be instanciated with either many=True or many=False.

This hack was removed in version 3.16.3 -- PR #719:

  • There were multiple questions about this behavior and multiple requests to remove it.
  • With the "union" approach, instance field type is compatible with neither Model nor list[Model], requiring an override in both cases.
  • I expect that in accessing instance of models with many=True is not a common use case -- it's a helpful convenience mostly for nested serializers.

‼️ However, if you had a real use that was broken by version 3.16.3, please share it in a comment below.

Metadata

Metadata

Assignees

No one assigned

    Labels

    3rd partyPresence of a third party dependency has been mentionedblockedBlocked by some other PR, discussion or third party dependency.bugSomething isn't workingupstream mypyBugs in mypy

    Type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions