Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Value of _Row not filled in for fully-typed queryset methods #1849

Open
bwo opened this issue Nov 22, 2023 · 0 comments
Open

Value of _Row not filled in for fully-typed queryset methods #1849

bwo opened this issue Nov 22, 2023 · 0 comments
Labels
bug Something isn't working

Comments

@bwo
Copy link

bwo commented Nov 22, 2023

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
  • python version: 3.11
  • django version: 4.2
  • mypy version: 1.6.1 (also happens with 1.7 and 1.8-dev versions)
  • django-stubs version: 4.2.6
  • django-stubs-ext version: 4.2.5
@bwo bwo added the bug Something isn't working label Nov 22, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Development

No branches or pull requests

1 participant