diff --git a/django-stubs/db/models/expressions.pyi b/django-stubs/db/models/expressions.pyi index 0b3b50e..0530cbf 100644 --- a/django-stubs/db/models/expressions.pyi +++ b/django-stubs/db/models/expressions.pyi @@ -8,7 +8,7 @@ from django.db.models.sql.compiler import SQLCompiler from django.db.backends.base.base import BaseDatabaseWrapper from django.db.models import Q, QuerySet from django.db.models.fields import Field -from django.db.models.query import _BaseQuerySet +from django.db.models.query import QuerySet _OutputField = Union[Field, str] @@ -132,7 +132,7 @@ class Subquery(Expression): template: str = ... queryset: QuerySet = ... extra: Dict[Any, Any] = ... - def __init__(self, queryset: _BaseQuerySet, output_field: Optional[_OutputField] = ..., **extra: Any) -> None: ... + def __init__(self, queryset: QuerySet, output_field: Optional[_OutputField] = ..., **extra: Any) -> None: ... class Exists(Subquery): negated: bool = ... diff --git a/django-stubs/db/models/query.pyi b/django-stubs/db/models/query.pyi index 874f33d..821346c 100644 --- a/django-stubs/db/models/query.pyi +++ b/django-stubs/db/models/query.pyi @@ -28,9 +28,9 @@ from django.db.models import Manager from django.db.models.query_utils import Q as Q # noqa: F401 _T = TypeVar("_T", bound=models.Model, covariant=True) -_QS = TypeVar("_QS", bound="_BaseQuerySet") +_QS = TypeVar("_QS", bound="QuerySet") -class _BaseQuerySet(Generic[_T], Sized): +class QuerySet(Generic[_T], Collection[_T], Reversible[_T], Sized): model: Type[_T] query: Query def __init__( @@ -47,8 +47,8 @@ class _BaseQuerySet(Generic[_T], Sized): def __class_getitem__(cls: Type[_QS], item: Type[_T]) -> Type[_QS]: ... def __getstate__(self) -> Dict[str, Any]: ... # Technically, the other QuerySet must be of the same type _T, but _T is covariant - def __and__(self: _QS, other: _BaseQuerySet[_T]) -> _QS: ... - def __or__(self: _QS, other: _BaseQuerySet[_T]) -> _QS: ... + def __and__(self: _QS, other: QuerySet[_T]) -> _QS: ... + def __or__(self: _QS, other: QuerySet[_T]) -> _QS: ... def iterator(self, chunk_size: int = ...) -> Iterator[_T]: ... def aggregate(self, *args: Any, **kwargs: Any) -> Dict[str, Any]: ... def get(self, *args: Any, **kwargs: Any) -> _T: ... @@ -78,15 +78,15 @@ class _BaseQuerySet(Generic[_T], Sized): using: Optional[str] = ..., ) -> RawQuerySet: ... # The type of values may be overridden to be more specific in the mypy plugin, depending on the fields param - def values(self, *fields: Union[str, Combinable], **expressions: Any) -> ValuesQuerySet[_T, Dict[str, Any]]: ... + def values(self, *fields: Union[str, Combinable], **expressions: Any) -> _ValuesQuerySet[_T, Dict[str, Any]]: ... # The type of values_list may be overridden to be more specific in the mypy plugin, depending on the fields param def values_list( self, *fields: Union[str, Combinable], flat: bool = ..., named: bool = ... - ) -> ValuesQuerySet[_T, Any]: ... - def dates(self, field_name: str, kind: str, order: str = ...) -> ValuesQuerySet[_T, datetime.date]: ... + ) -> _ValuesQuerySet[_T, Any]: ... + def dates(self, field_name: str, kind: str, order: str = ...) -> _ValuesQuerySet[_T, datetime.date]: ... def datetimes( self, field_name: str, kind: str, order: str = ..., tzinfo: Optional[datetime.tzinfo] = ... - ) -> ValuesQuerySet[_T, datetime.datetime]: ... + ) -> _ValuesQuerySet[_T, datetime.datetime]: ... def none(self: _QS) -> _QS: ... def all(self: _QS) -> _QS: ... def filter(self: _QS, *args: Any, **kwargs: Any) -> _QS: ... @@ -126,7 +126,7 @@ class _BaseQuerySet(Generic[_T], Sized): def db(self) -> str: ... def resolve_expression(self, *args: Any, **kwargs: Any) -> Any: ... -class QuerySet(_BaseQuerySet[_T], Collection[_T], Reversible[_T], Sized): + def __iter__(self) -> Iterator[_T]: ... def __contains__(self, x: object) -> bool: ... @overload @@ -138,7 +138,7 @@ class QuerySet(_BaseQuerySet[_T], Collection[_T], Reversible[_T], Sized): _Row = TypeVar("_Row", covariant=True) class BaseIterable(Sequence[_Row]): - def __init__(self, queryset: _BaseQuerySet, chunked_fetch: bool = ..., chunk_size: int = ...): ... + def __init__(self, queryset: QuerySet, chunked_fetch: bool = ..., chunk_size: int = ...): ... def __iter__(self) -> Iterator[_Row]: ... def __contains__(self, x: object) -> bool: ... def __len__(self) -> int: ... @@ -155,13 +155,14 @@ class NamedValuesListIterable(ValuesListIterable): ... class FlatValuesListIterable(BaseIterable): def __iter__(self) -> Iterator[Any]: ... -class ValuesQuerySet(_BaseQuerySet[_T], Collection[_Row], Sized): +class _ValuesQuerySet(Generic[_T, _Row], Collection[_Row], Reversible[_Row], QuerySet[_T], Sized): # type: ignore + def __len__(self) -> int: ... def __contains__(self, x: object) -> bool: ... def __iter__(self) -> Iterator[_Row]: ... # type: ignore @overload # type: ignore def __getitem__(self, i: int) -> _Row: ... @overload - def __getitem__(self: _QS, s: slice) -> _QS: ... + def __getitem__(self: _QS, s: slice) -> _QS: ... # type: ignore def iterator(self, chunk_size: int = ...) -> Iterator[_Row]: ... # type: ignore def get(self, *args: Any, **kwargs: Any) -> _Row: ... # type: ignore def earliest(self, *fields: Any, field_name: Optional[Any] = ...) -> _Row: ... # type: ignore diff --git a/django-stubs/forms/models.pyi b/django-stubs/forms/models.pyi index 1886578..b5816b6 100644 --- a/django-stubs/forms/models.pyi +++ b/django-stubs/forms/models.pyi @@ -24,7 +24,7 @@ from uuid import UUID from django.core.files.base import File from django.db.models.base import Model from django.db.models.manager import Manager -from django.db.models.query import QuerySet, _BaseQuerySet +from django.db.models.query import QuerySet from django.db.models.query_utils import Q from django.forms.fields import CharField, ChoiceField, Field from django.forms.forms import BaseForm, DeclarativeFieldsMetaclass @@ -257,7 +257,7 @@ class ModelChoiceField(ChoiceField): to_field_name: None = ... def __init__( self, - queryset: Optional[Union[Manager, _BaseQuerySet]], + queryset: Optional[Union[Manager, QuerySet]], *, empty_label: Optional[str] = ..., required: bool = ..., @@ -284,7 +284,7 @@ class ModelMultipleChoiceField(ModelChoiceField): widget: Any = ... hidden_widget: Any = ... default_error_messages: Any = ... - def __init__(self, queryset: Optional[Union[Manager, _BaseQuerySet]], **kwargs: Any) -> None: ... + def __init__(self, queryset: Optional[Union[Manager, QuerySet]], **kwargs: Any) -> None: ... def _get_foreign_key( parent_model: Type[Model], model: Type[Model], fk_name: Optional[str] = ..., can_fail: bool = ... diff --git a/django-stubs/views/generic/list.pyi b/django-stubs/views/generic/list.pyi index 83a3e98..b36046c 100644 --- a/django-stubs/views/generic/list.pyi +++ b/django-stubs/views/generic/list.pyi @@ -1,7 +1,7 @@ from typing import Any, Generic, Optional, Sequence, Tuple, Type, TypeVar from django.core.paginator import Paginator -from django.db.models.query import QuerySet, _BaseQuerySet +from django.db.models.query import QuerySet from django.views.generic.base import ContextMixin, TemplateResponseMixin, View from django.db.models import Model @@ -22,15 +22,15 @@ class MultipleObjectMixin(Generic[T], ContextMixin): def get_queryset(self) -> QuerySet[T]: ... def get_ordering(self) -> Sequence[str]: ... def paginate_queryset( - self, queryset: _BaseQuerySet, page_size: int + self, queryset: QuerySet, page_size: int ) -> Tuple[Paginator, int, QuerySet[T], bool]: ... - def get_paginate_by(self, queryset: _BaseQuerySet) -> Optional[int]: ... + def get_paginate_by(self, queryset: QuerySet) -> Optional[int]: ... def get_paginator( self, queryset: QuerySet, per_page: int, orphans: int = ..., allow_empty_first_page: bool = ..., **kwargs: Any ) -> Paginator: ... def get_paginate_orphans(self) -> int: ... def get_allow_empty(self) -> bool: ... - def get_context_object_name(self, object_list: _BaseQuerySet) -> Optional[str]: ... + def get_context_object_name(self, object_list: QuerySet) -> Optional[str]: ... class BaseListView(MultipleObjectMixin[T], View): def get(self, request: HttpRequest, *args: Any, **kwargs: Any) -> HttpResponse: ... diff --git a/django_stubs_ext/django_stubs_ext/__init__.py b/django_stubs_ext/django_stubs_ext/__init__.py index 33e82d4..999fb49 100644 --- a/django_stubs_ext/django_stubs_ext/__init__.py +++ b/django_stubs_ext/django_stubs_ext/__init__.py @@ -1,3 +1,4 @@ +from .aliases import ValuesQuerySet as ValuesQuerySet from .patch import monkeypatch as monkeypatch -__all__ = ["monkeypatch"] +__all__ = ["monkeypatch", "ValuesQuerySet"] diff --git a/django_stubs_ext/django_stubs_ext/aliases.py b/django_stubs_ext/django_stubs_ext/aliases.py new file mode 100644 index 0000000..ccaa345 --- /dev/null +++ b/django_stubs_ext/django_stubs_ext/aliases.py @@ -0,0 +1,8 @@ +import typing + +if typing.TYPE_CHECKING: + from django.db.models.query import _T, _Row, _ValuesQuerySet + + ValuesQuerySet = _ValuesQuerySet[_T, _Row] +else: + ValuesQuerySet = typing.Any diff --git a/django_stubs_ext/tests/typecheck/test_aliases.yml b/django_stubs_ext/tests/typecheck/test_aliases.yml new file mode 100644 index 0000000..d5c045f --- /dev/null +++ b/django_stubs_ext/tests/typecheck/test_aliases.yml @@ -0,0 +1,16 @@ +- case: alias_values_query_set + main: | + from django_stubs_ext import ValuesQuerySet + from myapp.models import Blog + + a: ValuesQuerySet[Blog, Blog] + installed_apps: + - myapp + files: + - path: myapp/__init__.py + - path: myapp/models.py + content: | + from django.db import models + + class Blog(models.Model): + created_at = models.DateTimeField() diff --git a/tests/typecheck/managers/querysets/test_basic_methods.yml b/tests/typecheck/managers/querysets/test_basic_methods.yml index 5bcf47e..c540a7d 100644 --- a/tests/typecheck/managers/querysets/test_basic_methods.yml +++ b/tests/typecheck/managers/querysets/test_basic_methods.yml @@ -14,8 +14,8 @@ reveal_type(qs.in_bulk()) # N: Revealed type is "builtins.dict[Any, myapp.models.Blog*]" # .dates / .datetimes - reveal_type(Blog.objects.dates("created_at", "day")) # N: Revealed type is "django.db.models.query.ValuesQuerySet[myapp.models.Blog*, datetime.date]" - reveal_type(Blog.objects.datetimes("created_at", "day")) # N: Revealed type is "django.db.models.query.ValuesQuerySet[myapp.models.Blog*, datetime.datetime]" + reveal_type(Blog.objects.dates("created_at", "day")) # N: Revealed type is "django.db.models.query._ValuesQuerySet[myapp.models.Blog*, datetime.date]" + reveal_type(Blog.objects.datetimes("created_at", "day")) # N: Revealed type is "django.db.models.query._ValuesQuerySet[myapp.models.Blog*, datetime.datetime]" # AND-ing QuerySets reveal_type(Blog.objects.all() & Blog.objects.all()) # N: Revealed type is "django.db.models.manager.Manager[myapp.models.Blog]" diff --git a/tests/typecheck/managers/querysets/test_values.yml b/tests/typecheck/managers/querysets/test_values.yml index 2defd0a..0ee4b06 100644 --- a/tests/typecheck/managers/querysets/test_values.yml +++ b/tests/typecheck/managers/querysets/test_values.yml @@ -111,8 +111,8 @@ - case: values_of_many_to_many_field main: | from myapp.models import Author, Book - reveal_type(Book.objects.values('authors')) # N: Revealed type is "django.db.models.query.ValuesQuerySet[myapp.models.Book, TypedDict({'authors': builtins.int})]" - reveal_type(Author.objects.values('books')) # N: Revealed type is "django.db.models.query.ValuesQuerySet[myapp.models.Author, TypedDict({'books': builtins.int})]" + reveal_type(Book.objects.values('authors')) # N: Revealed type is "django.db.models.query._ValuesQuerySet[myapp.models.Book, TypedDict({'authors': builtins.int})]" + reveal_type(Author.objects.values('books')) # N: Revealed type is "django.db.models.query._ValuesQuerySet[myapp.models.Author, TypedDict({'books': builtins.int})]" installed_apps: - myapp files: diff --git a/tests/typecheck/managers/querysets/test_values_list.yml b/tests/typecheck/managers/querysets/test_values_list.yml index 380d7f0..c567e56 100644 --- a/tests/typecheck/managers/querysets/test_values_list.yml +++ b/tests/typecheck/managers/querysets/test_values_list.yml @@ -191,8 +191,8 @@ - case: values_list_flat_true_with_ids main: | from myapp.models import Blog, Publisher - reveal_type(Blog.objects.values_list('id', flat=True)) # N: Revealed type is "django.db.models.query.ValuesQuerySet[myapp.models.Blog, builtins.int]" - reveal_type(Blog.objects.values_list('publisher_id', flat=True)) # N: Revealed type is "django.db.models.query.ValuesQuerySet[myapp.models.Blog, builtins.int]" + reveal_type(Blog.objects.values_list('id', flat=True)) # N: Revealed type is "django.db.models.query._ValuesQuerySet[myapp.models.Blog, builtins.int]" + reveal_type(Blog.objects.values_list('publisher_id', flat=True)) # N: Revealed type is "django.db.models.query._ValuesQuerySet[myapp.models.Blog, builtins.int]" # is Iterable[int] reveal_type(list(Blog.objects.values_list('id', flat=True))) # N: Revealed type is "builtins.list[builtins.int*]" installed_apps: @@ -211,8 +211,8 @@ main: | from myapp.models import TransactionQuerySet reveal_type(TransactionQuerySet()) # N: Revealed type is "myapp.models.TransactionQuerySet" - reveal_type(TransactionQuerySet().values()) # N: Revealed type is "django.db.models.query.ValuesQuerySet[myapp.models.Transaction, TypedDict({'id': builtins.int, 'total': builtins.int})]" - reveal_type(TransactionQuerySet().values_list()) # N: Revealed type is "django.db.models.query.ValuesQuerySet[myapp.models.Transaction, Tuple[builtins.int, builtins.int]]" + reveal_type(TransactionQuerySet().values()) # N: Revealed type is "django.db.models.query._ValuesQuerySet[myapp.models.Transaction, TypedDict({'id': builtins.int, 'total': builtins.int})]" + reveal_type(TransactionQuerySet().values_list()) # N: Revealed type is "django.db.models.query._ValuesQuerySet[myapp.models.Transaction, Tuple[builtins.int, builtins.int]]" installed_apps: - myapp files: @@ -228,8 +228,8 @@ - case: values_list_of_many_to_many_field main: | from myapp.models import Author, Book - reveal_type(Book.objects.values_list('authors')) # N: Revealed type is "django.db.models.query.ValuesQuerySet[myapp.models.Book, Tuple[builtins.int]]" - reveal_type(Author.objects.values_list('books')) # N: Revealed type is "django.db.models.query.ValuesQuerySet[myapp.models.Author, Tuple[builtins.int]]" + reveal_type(Book.objects.values_list('authors')) # N: Revealed type is "django.db.models.query._ValuesQuerySet[myapp.models.Book, Tuple[builtins.int]]" + reveal_type(Author.objects.values_list('books')) # N: Revealed type is "django.db.models.query._ValuesQuerySet[myapp.models.Author, Tuple[builtins.int]]" installed_apps: - myapp files: