django-stubs
django-stubs copied to clipboard
Value of `_Row` not filled in for fully-typed queryset methods
What's wrong
In order to work around #1845 I tried this:
from django.db import models
from typing import TypeVar, Any, TYPE_CHECKING, cast, reveal_type
_T = TypeVar("_T", bound=models.Model)
if TYPE_CHECKING:
from django.db.models.query import _Row, _QuerySet
else:
from django.db.models.query import QuerySet as _QuerySet
_Row = TypeVar("_Row")
class SpecialQuerySet(_QuerySet[_T, _Row]):
if TYPE_CHECKING:
def values(
self, *fields: str | models.expressions.Combinable, **expressions: Any
) -> "SpecialQuerySet[_T, dict[str, Any]]":
return cast(
SpecialQuerySet[_T, dict[str, Any]],
super().values(*fields, **expressions),
)
def values_list(
self,
*fields: str | models.expressions.Combinable,
flat: bool = False,
named: bool = False,
) -> "SpecialQuerySet[_T, Any]":
return cast(
SpecialQuerySet[_T, Any],
super().values_list(*fields, flat=flat, named=named),
)
def special_get(self, x: int, **kwargs: Any) -> _Row:
if x % 2 == 0:
return self.get(**kwargs)
else:
return self.filter(pk=3).get(**kwargs)
# this is necessary for obscure reasons to get line 4 of test() to work
class FooQuerySet(SpecialQuerySet[_T, _T]):
pass
SManager = models.Manager.from_queryset(FooQuerySet)
class Foo(models.Model):
text = models.TextField()
objects = SManager()
def test() -> None:
reveal_type(Foo.objects.special_get(4, text="hi").text)
reveal_type(Foo.objects.values("text").special_get(4))
reveal_type(Foo.objects.get)
reveal_type(Foo.objects.filter(text="text").special_get(4))
This almost works! That is, it works for Foo.objects.values(), and with the FooQuerySet for .filter().special_get(). The rest of it does not work:
mypy .
foo/models.py:55: error: "_Row" has no attribute "text" [attr-defined]
foo/models.py:55: note: Revealed type is "Any"
foo/models.py:56: note: Revealed type is "TypedDict({'text': builtins.str})"
foo/models.py:57: note: Revealed type is "def (*args: Any, **kwargs: Any) -> digest.models.Foo"
foo/models.py:58: note: Revealed type is "digest.models.Foo"
If you want this to work completely, the only choice, as far as I can tell, is manually implementing a manager, not using from_queryset(), and re-typing every single manager method—not just .values() and others that change _Row, but filter(), all(), etc—literally all of them!
System information
- OS: macos 13.5.2
-
pythonversion: 3.11 -
djangoversion: 4.2 -
mypyversion: 1.6.1 (also happens with 1.7 and 1.8-dev versions) -
django-stubsversion: 4.2.6 -
django-stubs-extversion: 4.2.5