26 Commits

Author SHA1 Message Date
sobolevn
3881df188c Adds github tempaltes, refs #112 2019-09-05 12:45:08 +03:00
Jared Kerim
b8379d4fe6 Add property to forms.changed_data fixes #148 (#149) 2019-08-30 11:18:24 +03:00
Nikita Sobolev
caa0e60743 Fixes travis badge 2019-08-25 20:42:00 +03:00
Maxim Kurnikov
de4fa92441 bump to 1.1.0 2019-08-24 18:40:57 +03:00
Maxim Kurnikov
dce0c0e930 Add 'django.core.cache.backends.memcached' stubs (#141) 2019-08-24 18:35:42 +03:00
Maxim Kurnikov
fc9a335dfd make ValuesQuerySet have proper Collection generic type (#140) 2019-08-24 18:24:21 +03:00
Maxim Kurnikov
5fc39ff110 Merge pull request #139 from mkurnikov/none-to-optional-annotations
Replace None annotations with Optional[...] around, stability fixes
2019-08-24 17:32:14 +03:00
Maxim Kurnikov
e95b40ef52 stability fixes 2019-08-24 17:04:50 +03:00
Maxim Kurnikov
c91a6d1d5b replace None annotations with Optional[] around 2019-08-24 16:35:07 +03:00
Maxim Kurnikov
d7e8222163 Fix related fields inheritance from abstract models (#138) 2019-08-24 02:35:55 +03:00
Maxim Kurnikov
09767210ec Return corresponding descriptors for some fields in class access (#137)
* return corresponding descriptors for some related fields in class access

* return corresponding descriptors for file fields in class access

* fix tests
2019-08-23 04:03:03 +03:00
Maxim Kurnikov
656105bab2 make first() an Optional, allow to specify QuerySet with one parameter (#136) 2019-08-23 03:31:07 +03:00
Nikita Sobolev
ff7bf33e9c Fixes #127, adds urls.pyi for several contrib apps (#129) 2019-08-16 15:45:17 +03:00
Nikita Sobolev
d94b7b0c6a Fixes collectstatic storage type, closes #130 (#131) 2019-08-16 15:45:07 +03:00
Swen Kooij
7bf1664307 Add properties to runserver.Command (#135) 2019-08-16 15:44:49 +03:00
sobolevn
0545c2d3ea Removes .editorconfig, refs #132 2019-08-13 15:06:13 +03:00
sobolevn
825931da9f Fixes yaml spacing, refs #132 2019-08-13 15:05:33 +03:00
sobolevn
737fd239b6 Fixes .editorconfig, refs #132 2019-08-13 15:04:02 +03:00
Nikita Sobolev
7f476057b0 Refactors bad default values, now using Ellipsis (#132)
* Refactors bad default values, now using Ellipsis

* Blacked
2019-08-13 15:01:15 +03:00
Nikita Sobolev
d31512854a Merge pull request #124 from K0Te/fix-email-annotations
Fix EmailMessage.attachements parameter annotation.
2019-08-12 18:46:10 +03:00
Nikita Sobolev
552de422dc Merge pull request #126 from rik/lazy-translation
Improve lazy translation functions
2019-08-12 18:45:15 +03:00
Anthony Ricaud
a9978cc021 Improve lazy translation functions
The lazy versions have the same signature as the non-lazy versions
2019-08-09 12:37:17 +01:00
Oleg Nykolyn
bb08212b20 Fix EmailMessage.attachements parameter annotation. 2019-08-01 22:02:18 +03:00
Maxim Kurnikov
5d2efdb80b Merge pull request #119 from mkurnikov/subclass-queryset-proper-typing
Allow to subclass queryset without loss of typing
2019-07-26 22:47:02 +03:00
Maxim Kurnikov
27793ecd32 allow to subclass queryset without loss of typing 2019-07-26 22:40:37 +03:00
Maxim Kurnikov
dddcb20fe4 bump version to 1.0.2 2019-07-26 22:22:22 +03:00
52 changed files with 635 additions and 332 deletions

10
.editorconfig Normal file
View File

@@ -0,0 +1,10 @@
# Check http://editorconfig.org for more information
# This is the main config file for this project:
root = true
[*]
charset = utf-8
end_of_line = lf
insert_final_newline = true
indent_style = space
trim_trailing_whitespace = true

35
.github/ISSUE_TEMPLATE/bug.md vendored Normal file
View File

@@ -0,0 +1,35 @@
---
name: Bug
about: Create a report of something is not working
labels: 'bug'
---
# Bug report
<!--
Hi, thanks for submitting a bug. We appreciate that.
But, we will need some information about what's wrong to help you.
-->
## What's wrong
<!--
Describe what is not working.
Please, attach a traceback.
We would also appreciate a failing test case.
That is EXTREMELY helpful!
-->
## How is that should be
<!-- Describe how it should work. -->
## System information
- OS:
- `python` version:
- `django` version:
- `mypy` version:
- `django-stubs` version:

29
.github/pull_request_template.md vendored Normal file
View File

@@ -0,0 +1,29 @@
# I have made things!
<!--
Hi, thanks for submitting a Pull Request. We appreciate it.
Please, fill in all the required information
to make our review and merging processes easier.
Cheers!
-->
## Related issues
<!--
Mark what issues this Pull Request closes or references.
Format is:
- Closes #issue-number
- Refs #issue-number
Example. Refs #0
Documentation: https://blog.github.com/2013-05-14-closing-issues-via-pull-requests/
-->
<!--
If you have any feedback, just write it here.
It can be whatever you want!
-->

View File

@@ -2,7 +2,7 @@
# pep484 stubs for Django framework
[![Build Status](https://travis-ci.org/mkurnikov/django-stubs.svg?branch=master)](https://travis-ci.org/mkurnikov/django-stubs)
[![Build Status](https://travis-ci.org/typeddjango/django-stubs.svg?branch=master)](https://travis-ci.org/typeddjango/django-stubs)
[![Checked with mypy](http://www.mypy-lang.org/static/mypy_badge.svg)](http://mypy-lang.org/)
This package contains type stubs and mypy plugin to provide more precise static types and type inference for Django framework. Django uses some Python "magic" that makes having precise types for some code patterns problematic. This is why we need to accompany the stubs with mypy plugins. The final goal is to be able to get precise types for most common patterns.

View File

@@ -24,7 +24,7 @@ class Apps:
def get_app_configs(self) -> Iterable[AppConfig]: ...
def get_app_config(self, app_label: str) -> AppConfig: ...
# it's not possible to support it in plugin properly now
def get_models(self, include_auto_created: bool = ..., include_swapped: bool = ...) -> List[Type[Any]]: ...
def get_models(self, include_auto_created: bool = ..., include_swapped: bool = ...) -> List[Type[Model]]: ...
def get_model(self, app_label: str, model_name: Optional[str] = ..., require_ready: bool = ...) -> Type[Any]: ...
def register_model(self, app_label: str, model: Type[Model]) -> None: ...
def is_installed(self, app_name: str) -> bool: ...

View File

@@ -0,0 +1,3 @@
from typing import Any, List
urlpatterns: List[Any] = ...

View File

@@ -0,0 +1,3 @@
from typing import Any, List
urlpatterns: List[Any] = ...

View File

@@ -13,7 +13,7 @@ class SearchVectorCombinable:
ADD: str = ...
class SearchVector(SearchVectorCombinable, Func):
config: Optional[Any] = None
config: Optional[Any] = ...
def __init__(self, *expressions: Union[str, Combinable], **extra: Any): ...
class CombinedSearchVector(SearchVectorCombinable, CombinedExpression):
@@ -31,11 +31,11 @@ class SearchQueryCombinable:
class SearchQuery(SearchQueryCombinable, Value):
SEARCH_TYPES: Dict[str, str] = {"plain": "plainto_tsquery", "phrase": "phraseto_tsquery", "raw": "to_tsquery"}
def __init__(self, value, output_field=None, *, config=None, invert=False, search_type="plain"): ...
def __init__(self, value, output_field=..., *, config=..., invert=False, search_type="plain"): ...
def __invert__(self: _T) -> _T: ...
class CombinedSearchQuery(SearchQueryCombinable, CombinedExpression):
def __init__(self, lhs, connector, rhs, config, output_field=None) -> None: ...
def __init__(self, lhs, connector, rhs, config, output_field=...) -> None: ...
class SearchRank(Func):
def __init__(self, vector, query, **extra: Any) -> None: ...

View File

@@ -1,6 +1,6 @@
from typing import Any, Dict, List
from django.core.files.storage import FileSystemStorage
from django.core.files.storage import Storage
from django.core.management.base import BaseCommand
class Command(BaseCommand):
@@ -23,6 +23,6 @@ class Command(BaseCommand):
def log(self, msg: str, level: int = ...) -> None: ...
def is_local_storage(self) -> bool: ...
def clear_dir(self, path: str) -> None: ...
def delete_file(self, path: str, prefixed_path: str, source_storage: FileSystemStorage) -> bool: ...
def link_file(self, path: str, prefixed_path: str, source_storage: FileSystemStorage) -> None: ...
def copy_file(self, path: str, prefixed_path: str, source_storage: FileSystemStorage) -> None: ...
def delete_file(self, path: str, prefixed_path: str, source_storage: Storage) -> bool: ...
def link_file(self, path: str, prefixed_path: str, source_storage: Storage) -> None: ...
def copy_file(self, path: str, prefixed_path: str, source_storage: Storage) -> None: ...

View File

@@ -2,6 +2,6 @@ from typing import Any, List, Optional
from django.urls.resolvers import URLPattern
urlpatterns: Any
urlpatterns: List[Any] = ...
def staticfiles_urlpatterns(prefix: Optional[str] = ...) -> List[URLPattern]: ...

View File

@@ -1,26 +1,6 @@
from typing import Any, Callable, Dict, Optional, Union
from typing import Any, Dict
from django.core.cache.backends.base import BaseCache
class LocMemCache(BaseCache):
default_timeout: int
key_func: Callable
key_prefix: str
version: int
def __init__(self, name: str, params: Dict[str, Optional[Union[Callable, Dict[str, int], int, str]]]) -> None: ...
def add(
self,
key: str,
value: Union[Dict[str, int], Dict[str, str], bytes, int, str],
timeout: Any = ...,
version: Optional[int] = ...,
) -> Any: ...
def get(
self, key: Union[int, str], default: Optional[Union[int, str]] = ..., version: Optional[int] = ...
) -> Any: ...
def set(self, key: Union[int, str], value: Any, timeout: Any = ..., version: Optional[int] = ...) -> None: ...
def touch(self, key: str, timeout: Any = ..., version: None = ...) -> Any: ...
def incr(self, key: Union[int, str], delta: int = ..., version: Optional[int] = ...) -> int: ...
def has_key(self, key: str, version: Optional[int] = ...) -> Any: ...
def delete(self, key: str, version: Optional[int] = ...) -> None: ...
def clear(self) -> None: ...
def __init__(self, name: str, params: Dict[str, Any]) -> None: ...

View File

@@ -0,0 +1,10 @@
from django.core.cache.backends.base import BaseCache
class BaseMemcachedCache(BaseCache):
def __init__(self, server, params, library, value_not_found_exception) -> None: ...
class MemcachedCache(BaseMemcachedCache):
def __init__(self, server, params): ...
class PyLibMCCache(BaseMemcachedCache):
def __init__(self, server, params): ...

View File

@@ -23,9 +23,14 @@ class FileUploadHandler:
content_type_extra = ... # type: Optional[Dict[str, str]]
request = ... # type: Optional[HttpRequest]
field_name = ... # type: str
def __init__(self, request: HttpRequest = None) -> None: ...
def __init__(self, request: Optional[HttpRequest] = ...) -> None: ...
def handle_raw_input(
self, input_data: IO[bytes], META: Dict[str, str], content_length: int, boundary: str, encoding: str = None
self,
input_data: IO[bytes],
META: Dict[str, str],
content_length: int,
boundary: str,
encoding: Optional[str] = ...,
) -> Optional[Tuple[QueryDict, MultiValueDict[str, UploadedFile]]]: ...
def new_file(
self,
@@ -33,15 +38,15 @@ class FileUploadHandler:
file_name: str,
content_type: str,
content_length: Optional[int],
charset: str = None,
content_type_extra: Dict[str, str] = None,
charset: Optional[str] = ...,
content_type_extra: Optional[Dict[str, str]] = ...,
) -> None: ...
def receive_data_chunk(self, raw_data: bytes, start: int) -> Optional[bytes]: ...
def file_complete(self, file_size: int) -> Optional[UploadedFile]: ...
def upload_complete(self) -> None: ...
class TemporaryFileUploadHandler(FileUploadHandler):
def __init__(self, request: HttpRequest = None) -> None: ...
def __init__(self, request: Optional[HttpRequest] = ...) -> None: ...
file = ... # type: TemporaryUploadedFile
def new_file(
self,
@@ -49,8 +54,8 @@ class TemporaryFileUploadHandler(FileUploadHandler):
file_name: str,
content_type: str,
content_length: Optional[int],
charset: str = None,
content_type_extra: Dict[str, str] = None,
charset: Optional[str] = ...,
content_type_extra: Optional[Dict[str, str]] = ...,
) -> None: ...
def receive_data_chunk(self, raw_data: bytes, start: int) -> Optional[bytes]: ...
def file_complete(self, file_size: int) -> Optional[UploadedFile]: ...
@@ -59,7 +64,12 @@ class MemoryFileUploadHandler(FileUploadHandler):
activated = ... # type: bool
file = ... # type: IO[bytes]
def handle_raw_input(
self, input_data: IO[bytes], META: Dict[str, str], content_length: int, boundary: str, encoding: str = None
self,
input_data: IO[bytes],
META: Dict[str, str],
content_length: int,
boundary: str,
encoding: Optional[str] = ...,
) -> Optional[Tuple[QueryDict, MultiValueDict[str, UploadedFile]]]: ...
def new_file(
self,
@@ -67,8 +77,8 @@ class MemoryFileUploadHandler(FileUploadHandler):
file_name: str,
content_type: str,
content_length: Optional[int],
charset: str = None,
content_type_extra: Dict[str, str] = None,
charset: Optional[str] = ...,
content_type_extra: Optional[Dict[str, str]] = ...,
) -> None: ...
def receive_data_chunk(self, raw_data: bytes, start: int) -> Optional[bytes]: ...
def file_complete(self, file_size: int) -> Optional[UploadedFile]: ...

View File

@@ -6,10 +6,6 @@ from django.http.response import HttpResponse, HttpResponseBase
logger: Any
class BaseHandler:
_view_middleware: None = ...
_template_response_middleware: None = ...
_exception_middleware: None = ...
_middleware_chain: None = ...
def load_middleware(self) -> None: ...
def make_view_atomic(self, view: Callable) -> Callable: ...
def get_exception_response(self, request: Any, resolver: Any, status_code: Any, exception: Any): ...

View File

@@ -65,7 +65,7 @@ class EmailMessage:
to: Optional[Union[Sequence[str], str]] = ...,
bcc: Optional[Union[Sequence[str], str]] = ...,
connection: Optional[Any] = ...,
attachments: Optional[Union[List[Tuple[str, str]], List[MIMEText]]] = ...,
attachments: Optional[Union[List[Tuple[str, Union[str, bytes], str]], List[MIMEText]]] = ...,
headers: Optional[Dict[str, str]] = ...,
cc: Optional[Union[Sequence[str], str]] = ...,
reply_to: Optional[Union[List[Optional[str]], str]] = ...,

View File

@@ -1,6 +1,6 @@
from argparse import ArgumentParser, HelpFormatter, Namespace
from io import StringIO, TextIOBase, TextIOWrapper
from typing import Any, Callable, List, Optional, Union
from typing import Any, Callable, List, Optional, Union, Tuple
from django.apps.config import AppConfig
from django.core.management.color import Style
@@ -36,8 +36,8 @@ class BaseCommand:
output_transaction: bool = ...
requires_migrations_checks: bool = ...
requires_system_checks: bool = ...
base_stealth_options: Any = ...
stealth_options: Any = ...
base_stealth_options: Tuple[str, ...] = ...
stealth_options: Tuple[str, ...] = ...
stdout: OutputWrapper = ...
stderr: OutputWrapper = ...
style: Style = ...

View File

@@ -1,3 +1,7 @@
from django.core.management.base import BaseCommand
class Command(BaseCommand): ...
class Command(BaseCommand):
default_addr: str = ...
default_addr_ipv6: str = ...
default_port: int = ...
protocol: str = ...

View File

@@ -5,6 +5,6 @@ from django.db.models.base import Model
def popen_wrapper(args: List[str], stdout_encoding: str = ...) -> Tuple[str, str, int]: ...
def handle_extensions(extensions: List[str]) -> Set[str]: ...
def find_command(cmd: str, path: None = ..., pathext: None = ...) -> Optional[str]: ...
def find_command(cmd: str, path: Optional[str] = ..., pathext: Optional[str] = ...) -> Optional[str]: ...
def get_random_secret_key(): ...
def parse_apps_and_model_labels(labels: List[str]) -> Tuple[Set[Type[Model]], Set[AppConfig]]: ...

View File

@@ -1,6 +1,7 @@
import types
from datetime import date, datetime, time
from decimal import Decimal
from typing import Any, Dict, Iterator, List, Mapping, Optional, Sequence, Tuple, Union
from typing import Any, Dict, Iterator, List, Mapping, Optional, Sequence, Tuple, Union, Type
from uuid import UUID
logger: Any
@@ -16,7 +17,12 @@ class CursorWrapper:
def __getattr__(self, attr: str) -> Any: ...
def __iter__(self) -> None: ...
def __enter__(self) -> CursorWrapper: ...
def __exit__(self, type: None, value: None, traceback: None) -> None: ...
def __exit__(
self,
exc_type: Optional[Type[BaseException]],
exc_value: Optional[BaseException],
tb: Optional[types.TracebackType],
) -> None: ...
def callproc(self, procname: str, params: List[Any] = ..., kparams: Dict[str, int] = ...) -> Any: ...
def execute(
self, sql: str, params: Optional[Union[Sequence[_SQLType], Mapping[str, _SQLType]]] = ...

View File

@@ -6,6 +6,7 @@ from django.db.models.sql.compiler import SQLCompiler
from django.db.models import Q, QuerySet
from django.db.models.fields import Field
from django.db.models.query import _BaseQuerySet
_OutputField = Union[Field, str]
@@ -125,7 +126,7 @@ class Subquery(Expression):
template: str = ...
queryset: QuerySet = ...
extra: Dict[Any, Any] = ...
def __init__(self, queryset: QuerySet, output_field: Optional[_OutputField] = ..., **extra: Any) -> None: ...
def __init__(self, queryset: _BaseQuerySet, output_field: Optional[_OutputField] = ..., **extra: Any) -> None: ...
class Exists(Subquery):
negated: bool = ...

View File

@@ -1,6 +1,5 @@
from typing import Any, Callable, List, Optional, Type, Union, Tuple, Iterable
from typing import Any, Callable, List, Optional, Type, Union, Tuple, Iterable, overload, TypeVar
from django.core.checks.messages import Error
from django.core.files.base import File
from django.core.files.images import ImageFile
from django.core.files.storage import FileSystemStorage, Storage
@@ -34,6 +33,8 @@ class FileDescriptor:
def __set__(self, instance: Model, value: Optional[Any]) -> None: ...
def __get__(self, instance: Optional[Model], cls: Type[Model] = ...) -> Union[FieldFile, FileDescriptor]: ...
_T = TypeVar("_T", bound="Field")
class FileField(Field):
storage: Any = ...
upload_to: Union[str, Callable] = ...
@@ -63,6 +64,15 @@ class FileField(Field):
validators: Iterable[_ValidatorCallable] = ...,
error_messages: Optional[_ErrorMessagesToOverride] = ...,
): ...
# class access
@overload # type: ignore
def __get__(self, instance: None, owner) -> FileDescriptor: ...
# Model instance access
@overload
def __get__(self, instance: Model, owner) -> Any: ...
# non-Model instances
@overload
def __get__(self: _T, instance, owner) -> _T: ...
def generate_filename(self, instance: Optional[Model], filename: str) -> str: ...
class ImageFileDescriptor(FileDescriptor):
@@ -82,4 +92,13 @@ class ImageField(FileField):
height_field: Optional[str] = ...,
**kwargs: Any
) -> None: ...
# class access
@overload # type: ignore
def __get__(self, instance: None, owner) -> ImageFileDescriptor: ...
# Model instance access
@overload
def __get__(self, instance: Model, owner) -> Any: ...
# non-Model instances
@overload
def __get__(self: _T, instance, owner) -> _T: ...
def update_dimension_fields(self, instance: Model, force: bool = ..., *args: Any, **kwargs: Any) -> None: ...

View File

@@ -1,4 +1,18 @@
from typing import Any, Callable, Dict, Iterable, List, Optional, Sequence, TYPE_CHECKING, Tuple, Type, TypeVar, Union
from typing import (
Any,
Callable,
Dict,
Iterable,
List,
Optional,
Sequence,
TYPE_CHECKING,
Tuple,
Type,
TypeVar,
Union,
overload,
)
from uuid import UUID
from django.db.models.expressions import Combinable
@@ -25,7 +39,7 @@ if TYPE_CHECKING:
from django.db.models.manager import RelatedManager
_T = TypeVar("_T", bound=models.Model)
_F = TypeVar("_F", bound=models.Field)
_Choice = Tuple[Any, str]
_ChoiceNamedGroup = Tuple[str, Iterable[_Choice]]
_FieldChoices = Iterable[Union[_Choice, _ChoiceNamedGroup]]
@@ -66,9 +80,9 @@ class ForeignObject(RelatedField[_ST, _GT]):
on_delete: Callable[..., None],
from_fields: Sequence[str],
to_fields: Sequence[str],
rel: None = ...,
rel: Optional[ForeignObjectRel] = ...,
related_name: Optional[str] = ...,
related_query_name: None = ...,
related_query_name: Optional[str] = ...,
limit_choices_to: Optional[Union[Dict[str, Any], Callable[[], Any]]] = ...,
parent_link: bool = ...,
db_constraint: bool = ...,
@@ -127,6 +141,15 @@ class ForeignKey(ForeignObject[_ST, _GT]):
validators: Iterable[_ValidatorCallable] = ...,
error_messages: Optional[_ErrorMessagesToOverride] = ...,
): ...
# class access
@overload # type: ignore
def __get__(self, instance: None, owner) -> ForwardManyToOneDescriptor: ...
# Model instance access
@overload
def __get__(self, instance: Model, owner) -> _GT: ...
# non-Model instances
@overload
def __get__(self: _F, instance, owner) -> _F: ...
class OneToOneField(RelatedField[_ST, _GT]):
_pyi_private_set_type: Union[Any, Combinable]
@@ -163,6 +186,15 @@ class OneToOneField(RelatedField[_ST, _GT]):
validators: Iterable[_ValidatorCallable] = ...,
error_messages: Optional[_ErrorMessagesToOverride] = ...,
): ...
# class access
@overload # type: ignore
def __get__(self, instance: None, owner) -> ForwardOneToOneDescriptor: ...
# Model instance access
@overload
def __get__(self, instance: Model, owner) -> _GT: ...
# non-Model instances
@overload
def __get__(self: _F, instance, owner) -> _F: ...
class ManyToManyField(RelatedField[_ST, _GT]):
_pyi_private_set_type: Sequence[Any]
@@ -206,7 +238,15 @@ class ManyToManyField(RelatedField[_ST, _GT]):
validators: Iterable[_ValidatorCallable] = ...,
error_messages: Optional[_ErrorMessagesToOverride] = ...,
) -> None: ...
def __get__(self, instance, owner) -> _GT: ... # type: ignore
# class access
@overload # type: ignore
def __get__(self, instance: None, owner) -> ManyToManyDescriptor: ...
# Model instance access
@overload
def __get__(self, instance: Model, owner) -> _GT: ...
# non-Model instances
@overload
def __get__(self: _F, instance, owner) -> _F: ...
def get_path_info(self, filtered_relation: None = ...) -> List[PathInfo]: ...
def get_reverse_path_info(self, filtered_relation: None = ...) -> List[PathInfo]: ...
def contribute_to_related_class(self, cls: Type[Model], related: RelatedField) -> None: ...

View File

@@ -5,7 +5,7 @@ from django.db.models.query import QuerySet
_T = TypeVar("_T", bound=Model, covariant=True)
class BaseManager(QuerySet[_T, _T]):
class BaseManager(QuerySet[_T]):
creation_counter: int = ...
auto_created: bool = ...
use_in_migrations: bool = ...
@@ -21,7 +21,7 @@ class BaseManager(QuerySet[_T, _T]):
def _get_queryset_methods(cls, queryset_class: type) -> Dict[str, Any]: ...
def contribute_to_class(self, model: Type[Model], name: str) -> None: ...
def db_manager(self, using: Optional[str] = ..., hints: Optional[Dict[str, Model]] = ...) -> Manager: ...
def get_queryset(self) -> QuerySet[_T, _T]: ...
def get_queryset(self) -> QuerySet[_T]: ...
class Manager(BaseManager[_T]): ...

View File

@@ -71,7 +71,7 @@ class Options(Generic[_M]):
abstract: bool = ...
managed: bool = ...
proxy: bool = ...
proxy_for_model: None = ...
proxy_for_model: Optional[Type[Model]] = ...
concrete_model: Optional[Type[Model]] = ...
swappable: None = ...
parents: collections.OrderedDict = ...

View File

@@ -27,10 +27,115 @@ from django.db import models
from django.db.models import Manager
from django.db.models.query_utils import Q as Q
_T = TypeVar("_T", bound=models.Model, covariant=True)
_QS = TypeVar("_QS", bound="_BaseQuerySet")
class _BaseQuerySet(Generic[_T], Sized):
query: Query
def __init__(
self,
model: Optional[Type[models.Model]] = ...,
query: Optional[Query] = ...,
using: Optional[str] = ...,
hints: Optional[Dict[str, models.Model]] = ...,
) -> None: ...
@classmethod
def as_manager(cls) -> Manager[Any]: ...
def __len__(self) -> int: ...
def __bool__(self) -> bool: ...
def __class_getitem__(cls, item: Type[_T]):
pass
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 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: ...
def create(self, *args: Any, **kwargs: Any) -> _T: ...
def bulk_create(
self, objs: Iterable[Model], batch_size: Optional[int] = ..., ignore_conflicts: bool = ...
) -> List[_T]: ...
def get_or_create(self, defaults: Optional[MutableMapping[str, Any]] = ..., **kwargs: Any) -> Tuple[_T, bool]: ...
def update_or_create(
self, defaults: Optional[MutableMapping[str, Any]] = ..., **kwargs: Any
) -> Tuple[_T, bool]: ...
def earliest(self, *fields: Any, field_name: Optional[Any] = ...) -> _T: ...
def latest(self, *fields: Any, field_name: Optional[Any] = ...) -> _T: ...
def first(self) -> Optional[_T]: ...
def last(self) -> Optional[_T]: ...
def in_bulk(self, id_list: Iterable[Any] = ..., *, field_name: str = ...) -> Dict[Any, _T]: ...
def delete(self) -> Tuple[int, Dict[str, int]]: ...
def update(self, **kwargs: Any) -> int: ...
def exists(self) -> bool: ...
def explain(self, *, format: Optional[Any] = ..., **options: Any) -> str: ...
def raw(
self,
raw_query: str,
params: Any = ...,
translations: Optional[Dict[str, str]] = ...,
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]]: ...
# 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]: ...
def datetimes(
self, field_name: str, kind: str, order: str = ..., tzinfo: Optional[datetime.tzinfo] = ...
) -> ValuesQuerySet[_T, datetime.datetime]: ...
def none(self: _QS) -> _QS: ...
def all(self: _QS) -> _QS: ...
def filter(self: _QS, *args: Any, **kwargs: Any) -> _QS: ...
def exclude(self: _QS, *args: Any, **kwargs: Any) -> _QS: ...
def complex_filter(self, filter_obj: Any) -> _QS: ...
def count(self) -> int: ...
def union(self: _QS, *other_qs: Any, all: bool = ...) -> _QS: ...
def intersection(self: _QS, *other_qs: Any) -> _QS: ...
def difference(self: _QS, *other_qs: Any) -> _QS: ...
def select_for_update(self: _QS, nowait: bool = ..., skip_locked: bool = ..., of: Tuple = ...) -> _QS: ...
def select_related(self: _QS, *fields: Any) -> _QS: ...
def prefetch_related(self: _QS, *lookups: Any) -> _QS: ...
# TODO: return type
def annotate(self, *args: Any, **kwargs: Any) -> QuerySet[Any]: ...
def order_by(self: _QS, *field_names: Any) -> _QS: ...
def distinct(self: _QS, *field_names: Any) -> _QS: ...
# extra() return type won't be supported any time soon
def extra(
self,
select: Optional[Dict[str, Any]] = ...,
where: Optional[List[str]] = ...,
params: Optional[List[Any]] = ...,
tables: Optional[List[str]] = ...,
order_by: Optional[Sequence[str]] = ...,
select_params: Optional[Sequence[Any]] = ...,
) -> QuerySet[Any]: ...
def reverse(self: _QS) -> _QS: ...
def defer(self: _QS, *fields: Any) -> _QS: ...
def only(self: _QS, *fields: Any) -> _QS: ...
def using(self: _QS, alias: Optional[str]) -> _QS: ...
@property
def ordered(self) -> bool: ...
@property
def db(self) -> str: ...
def resolve_expression(self, *args: Any, **kwargs: Any) -> Any: ...
# TODO: remove when django adds __class_getitem__ methods
def __getattr__(self, item: str) -> Any: ...
class QuerySet(_BaseQuerySet[_T], Collection[_T], Sized):
def __iter__(self) -> Iterator[_T]: ...
def __contains__(self, x: object) -> bool: ...
@overload
def __getitem__(self, i: int) -> _T: ...
@overload
def __getitem__(self: _QS, s: slice) -> _QS: ...
_Row = TypeVar("_Row", covariant=True)
class BaseIterable(Sequence[_Row]):
def __init__(self, queryset: QuerySet, chunked_fetch: bool = ..., chunk_size: int = ...): ...
def __init__(self, queryset: _BaseQuerySet, chunked_fetch: bool = ..., chunk_size: int = ...): ...
def __iter__(self) -> Iterator[_Row]: ...
def __contains__(self, x: object) -> bool: ...
def __len__(self) -> int: ...
@@ -47,109 +152,19 @@ class NamedValuesListIterable(ValuesListIterable): ...
class FlatValuesListIterable(BaseIterable):
def __iter__(self) -> Iterator[Any]: ...
_T = TypeVar("_T", bound=models.Model, covariant=True)
class QuerySet(Generic[_T, _Row], Collection[_Row], Sized):
query: Query
def __init__(
self,
model: Optional[Type[models.Model]] = ...,
query: Optional[Query] = ...,
using: Optional[str] = ...,
hints: Optional[Dict[str, models.Model]] = ...,
) -> None: ...
@classmethod
def as_manager(cls) -> Manager[Any]: ...
def __len__(self) -> int: ...
def __iter__(self) -> Iterator[_Row]: ...
class ValuesQuerySet(_BaseQuerySet[_T], Collection[_Row], Sized):
def __contains__(self, x: object) -> bool: ...
@overload
def __iter__(self) -> Iterator[_Row]: ... # type: ignore
@overload # type: ignore
def __getitem__(self, i: int) -> _Row: ...
@overload
def __getitem__(self, s: slice) -> QuerySet[_T, _Row]: ...
def __bool__(self) -> bool: ...
def __class_getitem__(cls, item: Type[_T]):
pass
def __getstate__(self) -> Dict[str, Any]: ...
# __and__ and __or__ ignore the other QuerySet's _Row type parameter because they use the same row type as the self QuerySet.
# Technically, the other QuerySet must be of the same type _T, but _T is covariant
def __and__(self, other: QuerySet[_T, Any]) -> QuerySet[_T, _Row]: ...
def __or__(self, other: QuerySet[_T, Any]) -> QuerySet[_T, _Row]: ...
def iterator(self, chunk_size: int = ...) -> Iterator[_Row]: ...
def aggregate(self, *args: Any, **kwargs: Any) -> Dict[str, Any]: ...
def get(self, *args: Any, **kwargs: Any) -> _Row: ...
def create(self, *args: Any, **kwargs: Any) -> _T: ...
def bulk_create(
self, objs: Iterable[Model], batch_size: Optional[int] = ..., ignore_conflicts: bool = ...
) -> List[_T]: ...
def get_or_create(self, defaults: Optional[MutableMapping[str, Any]] = ..., **kwargs: Any) -> Tuple[_T, bool]: ...
def update_or_create(
self, defaults: Optional[MutableMapping[str, Any]] = ..., **kwargs: Any
) -> Tuple[_T, bool]: ...
def earliest(self, *fields: Any, field_name: Optional[Any] = ...) -> _Row: ...
def latest(self, *fields: Any, field_name: Optional[Any] = ...) -> _Row: ...
# technically it's Optional[_Row], but it creates a lot of false-positives (same for last())
def first(self) -> _Row: ...
def last(self) -> _Row: ...
def in_bulk(self, id_list: Iterable[Any] = ..., *, field_name: str = ...) -> Dict[Any, _T]: ...
def delete(self) -> Tuple[int, Dict[str, int]]: ...
def update(self, **kwargs: Any) -> int: ...
def exists(self) -> bool: ...
def explain(self, *, format: Optional[Any] = ..., **options: Any) -> str: ...
def raw(
self,
raw_query: str,
params: Any = ...,
translations: Optional[Dict[str, str]] = ...,
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) -> QuerySet[_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 = ...
) -> QuerySet[_T, Any]: ...
def dates(self, field_name: str, kind: str, order: str = ...) -> QuerySet[_T, datetime.date]: ...
def datetimes(
self, field_name: str, kind: str, order: str = ..., tzinfo: None = ...
) -> QuerySet[_T, datetime.datetime]: ...
def none(self) -> QuerySet[_T, _Row]: ...
def all(self) -> QuerySet[_T, _Row]: ...
def filter(self, *args: Any, **kwargs: Any) -> QuerySet[_T, _Row]: ...
def exclude(self, *args: Any, **kwargs: Any) -> QuerySet[_T, _Row]: ...
def complex_filter(self, filter_obj: Any) -> QuerySet[_T, _Row]: ...
def count(self) -> int: ...
def union(self, *other_qs: Any, all: bool = ...) -> QuerySet[_T, _Row]: ...
def intersection(self, *other_qs: Any) -> QuerySet[_T, _Row]: ...
def difference(self, *other_qs: Any) -> QuerySet[_T, _Row]: ...
def select_for_update(self, nowait: bool = ..., skip_locked: bool = ..., of: Tuple = ...) -> QuerySet[_T, _Row]: ...
def select_related(self, *fields: Any) -> QuerySet[_T, _Row]: ...
def prefetch_related(self, *lookups: Any) -> QuerySet[_T, _Row]: ...
# TODO: return type
def annotate(self, *args: Any, **kwargs: Any) -> QuerySet[Any, Any]: ...
def order_by(self, *field_names: Any) -> QuerySet[_T, _Row]: ...
def distinct(self, *field_names: Any) -> QuerySet[_T, _Row]: ...
# extra() return type won't be supported any time soon
def extra(
self,
select: Optional[Dict[str, Any]] = ...,
where: Optional[List[str]] = ...,
params: Optional[List[Any]] = ...,
tables: Optional[List[str]] = ...,
order_by: Optional[Sequence[str]] = ...,
select_params: Optional[Sequence[Any]] = ...,
) -> QuerySet[Any, Any]: ...
def reverse(self) -> QuerySet[_T, _Row]: ...
def defer(self, *fields: Any) -> QuerySet[_T, _Row]: ...
def only(self, *fields: Any) -> QuerySet[_T, _Row]: ...
def using(self, alias: Optional[str]) -> QuerySet[_T, _Row]: ...
@property
def ordered(self) -> bool: ...
@property
def db(self) -> str: ...
def resolve_expression(self, *args: Any, **kwargs: Any) -> Any: ...
# TODO: remove when django adds __class_getitem__ methods
def __getattr__(self, item: str) -> Any: ...
def __getitem__(self: _QS, s: slice) -> _QS: ...
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
def latest(self, *fields: Any, field_name: Optional[Any] = ...) -> _Row: ... # type: ignore
def first(self) -> Optional[_Row]: ... # type: ignore
def last(self) -> Optional[_Row]: ... # type: ignore
class RawQuerySet(Iterable[_T], Sized):
query: RawQuery
@@ -184,7 +199,7 @@ class RawQuerySet(Iterable[_T], Sized):
def using(self, alias: Optional[str]) -> RawQuerySet[_T]: ...
class Prefetch(object):
def __init__(self, lookup: str, queryset: Optional[QuerySet] = None, to_attr: Optional[str] = None) -> None: ...
def __init__(self, lookup: str, queryset: Optional[QuerySet] = ..., to_attr: Optional[str] = ...) -> None: ...
def __getstate__(self) -> Dict[str, Any]: ...
def add_prefix(self, prefix: str) -> None: ...
def get_current_prefetch_to(self, level: int) -> str: ...

View File

@@ -38,5 +38,5 @@ def atomic(using: _C) -> _C: ...
# Decorator or context-manager with parameters
@overload
def atomic(using: Optional[str] = None, savepoint: bool = True) -> Atomic: ...
def atomic(using: Optional[str] = ..., savepoint: bool = True) -> Atomic: ...
def non_atomic_requests(using: Callable = ...) -> Callable: ...

View File

@@ -60,6 +60,7 @@ class BaseForm:
def full_clean(self) -> None: ...
def clean(self) -> Dict[str, Any]: ...
def has_changed(self) -> bool: ...
@property
def changed_data(self) -> List[str]: ...
@property
def media(self) -> Media: ...

View File

@@ -6,7 +6,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
from django.db.models.query import QuerySet, _BaseQuerySet
from django.db.models.query_utils import Q
from django.forms.fields import CharField, ChoiceField, Field
from django.forms.forms import BaseForm, DeclarativeFieldsMetaclass
@@ -262,7 +262,7 @@ class ModelMultipleChoiceField(ModelChoiceField):
widget: Any = ...
hidden_widget: Any = ...
default_error_messages: Any = ...
def __init__(self, queryset: QuerySet, **kwargs: Any) -> None: ...
def __init__(self, queryset: _BaseQuerySet, **kwargs: Any) -> None: ...
def _get_foreign_key(
parent_model: Type[Model], model: Type[Model], fk_name: Optional[str] = ..., can_fail: bool = ...

View File

@@ -49,7 +49,7 @@ class HttpResponseBase(Iterable[Any]):
) -> None: ...
def setdefault(self, key: str, value: str) -> None: ...
def set_signed_cookie(self, key: str, value: str, salt: str = "", **kwargs: Any) -> None: ...
def delete_cookie(self, key: str, path: str = "", domain: str = None) -> None: ...
def delete_cookie(self, key: str, path: str = "", domain: Optional[str] = ...) -> None: ...
def make_bytes(self, value: object) -> bytes: ...
def close(self) -> None: ...
def write(self, content: Union[str, bytes]) -> None: ...

View File

@@ -31,6 +31,6 @@ def redirect(
_T = TypeVar("_T", bound=Model)
def get_object_or_404(klass: Union[Type[_T], Manager[_T], QuerySet[_T, _T]], *args: Any, **kwargs: Any) -> _T: ...
def get_list_or_404(klass: Union[Type[_T], Manager[_T], QuerySet[_T, _T]], *args: Any, **kwargs: Any) -> List[_T]: ...
def get_object_or_404(klass: Union[Type[_T], Manager[_T], QuerySet[_T]], *args: Any, **kwargs: Any) -> _T: ...
def get_list_or_404(klass: Union[Type[_T], Manager[_T], QuerySet[_T]], *args: Any, **kwargs: Any) -> List[_T]: ...
def resolve_url(to: Union[Callable, Model, str], *args: Any, **kwargs: Any) -> str: ...

View File

@@ -119,15 +119,17 @@ class SimpleTestCase(unittest.TestCase):
field_kwargs: None = ...,
empty_value: str = ...,
) -> Any: ...
def assertHTMLEqual(self, html1: str, html2: str, msg: None = ...) -> None: ...
def assertHTMLNotEqual(self, html1: str, html2: str, msg: None = ...) -> None: ...
def assertHTMLEqual(self, html1: str, html2: str, msg: Optional[str] = ...) -> None: ...
def assertHTMLNotEqual(self, html1: str, html2: str, msg: Optional[str] = ...) -> None: ...
def assertInHTML(
self, needle: str, haystack: SafeText, count: Optional[int] = ..., msg_prefix: str = ...
) -> None: ...
def assertJSONEqual(self, raw: str, expected_data: Union[Dict[str, str], bool, str], msg: None = ...) -> None: ...
def assertJSONNotEqual(self, raw: str, expected_data: str, msg: None = ...) -> None: ...
def assertXMLEqual(self, xml1: str, xml2: str, msg: None = ...) -> None: ...
def assertXMLNotEqual(self, xml1: str, xml2: str, msg: None = ...) -> None: ...
def assertJSONEqual(
self, raw: str, expected_data: Union[Dict[str, str], bool, str], msg: Optional[str] = ...
) -> None: ...
def assertJSONNotEqual(self, raw: str, expected_data: str, msg: Optional[str] = ...) -> None: ...
def assertXMLEqual(self, xml1: str, xml2: str, msg: Optional[str] = ...) -> None: ...
def assertXMLNotEqual(self, xml1: str, xml2: str, msg: Optional[str] = ...) -> None: ...
class TransactionTestCase(SimpleTestCase):
reset_sequences: bool = ...
@@ -141,7 +143,7 @@ class TransactionTestCase(SimpleTestCase):
values: Union[List[None], List[Tuple[str, str]], List[date], List[int], List[str], Set[str], QuerySet],
transform: Union[Callable, Type[str]] = ...,
ordered: bool = ...,
msg: None = ...,
msg: Optional[str] = ...,
) -> None: ...
def assertNumQueries(
self, num: int, func: Optional[Union[Callable, Type[list]]] = ..., *args: Any, using: Any = ..., **kwargs: Any

View File

@@ -39,7 +39,7 @@ class MultiValueDict(MutableMapping[_K, _V]):
def __init__(self, key_to_list_mapping: Iterable[Tuple[_K, List[_V]]] = ...) -> None: ...
def getlist(self, key: _K, default: Any = ...) -> List[_V]: ...
def setlist(self, key: _K, list_: List[_V]) -> None: ...
def setlistdefault(self, key: _K, default_list: List[_V] = None) -> List[_V]: ...
def setlistdefault(self, key: _K, default_list: Optional[List[_V]] = ...) -> List[_V]: ...
def appendlist(self, key: _K, value: _V) -> None: ...
def lists(self) -> Iterable[Tuple[_K, List[_V]]]: ...
def dict(self) -> Dict[_K, Union[_V, List[_V]]]: ...

View File

@@ -1,8 +1,10 @@
from datetime import date
from typing import Any, Optional
from typing import Any, Optional, Dict
TIME_STRINGS: Any
TIME_STRINGS: Dict[str, str]
TIMESINCE_CHUNKS: Any
def timesince(d: date, now: Optional[date] = ..., reversed: bool = ..., time_strings: None = ...) -> str: ...
def timeuntil(d: date, now: Optional[date] = ..., time_strings: None = ...) -> str: ...
def timesince(
d: date, now: Optional[date] = ..., reversed: bool = ..., time_strings: Optional[Dict[str, str]] = ...
) -> str: ...
def timeuntil(d: date, now: Optional[date] = ..., time_strings: Optional[Dict[str, str]] = ...) -> str: ...

View File

@@ -39,16 +39,13 @@ ungettext = ngettext
def pgettext(context: str, message: str) -> str: ...
def npgettext(context: str, singular: str, plural: str, number: int) -> str: ...
gettext_lazy: Callable[[str], str]
gettext_lazy = gettext
ugettext_lazy = ugettext
pgettext_lazy = pgettext
ngettext_lazy = ngettext
ungettext_lazy = ungettext
npgettext_lazy = npgettext
ugettext_lazy: Callable[[str], str]
pgettext_lazy: Callable[[str], str]
def ngettext_lazy(singular: Any, plural: Any, number: Optional[Any] = ...): ...
ungettext_lazy = ngettext_lazy
def npgettext_lazy(context: Any, singular: Any, plural: Any, number: Optional[Any] = ...): ...
def activate(language: str) -> None: ...
def deactivate() -> None: ...

View File

@@ -17,7 +17,7 @@ class FormMixin(ContextMixin):
def get_initial(self) -> Dict[str, Any]: ...
def get_prefix(self) -> Optional[str]: ...
def get_form_class(self) -> Type[BaseForm]: ...
def get_form(self, form_class: Optional[Type[BaseForm]] = None) -> BaseForm: ...
def get_form(self, form_class: Optional[Type[BaseForm]] = ...) -> BaseForm: ...
def get_form_kwargs(self) -> Dict[str, Any]: ...
def get_success_url(self) -> str: ...
def form_valid(self, form: BaseForm) -> HttpResponse: ...

View File

@@ -1,7 +1,7 @@
from typing import Any, Dict, Optional, Sequence, Tuple, Type
from django.core.paginator import Paginator
from django.db.models.query import QuerySet
from django.db.models.query import QuerySet, _BaseQuerySet
from django.views.generic.base import ContextMixin, TemplateResponseMixin, View
from django.db.models import Model
@@ -22,14 +22,14 @@ class MultipleObjectMixin(ContextMixin):
object_list: QuerySet = ...
def get_queryset(self) -> QuerySet: ...
def get_ordering(self) -> Sequence[str]: ...
def paginate_queryset(self, queryset: QuerySet, page_size: int) -> Tuple[Paginator, int, QuerySet, bool]: ...
def get_paginate_by(self, queryset: QuerySet) -> Optional[int]: ...
def paginate_queryset(self, queryset: _BaseQuerySet, page_size: int) -> Tuple[Paginator, int, QuerySet, bool]: ...
def get_paginate_by(self, queryset: _BaseQuerySet) -> 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: QuerySet) -> Optional[str]: ...
def get_context_object_name(self, object_list: _BaseQuerySet) -> Optional[str]: ...
class BaseListView(MultipleObjectMixin, View):
def render_to_response(self, context: Dict[str, Any], **response_kwargs: Any) -> HttpResponse: ...

View File

@@ -1,20 +1,18 @@
import os
from collections import defaultdict
from contextlib import contextmanager
from typing import TYPE_CHECKING, Dict, Iterator, List, Optional, Tuple, Type
from typing import Dict, Iterator, Optional, Set, TYPE_CHECKING, Tuple, Type, Union
from django.core.exceptions import FieldError
from django.db.models.base import Model
from django.db.models.fields import AutoField, CharField, Field
from django.db.models.fields.related import ForeignKey, RelatedField
from django.db.models.fields.reverse_related import ForeignObjectRel
from django.db.models.sql.query import Query
from django.utils.functional import cached_property
from mypy.checker import TypeChecker
from mypy.types import AnyType, Instance
from mypy.types import Type as MypyType
from mypy.types import TypeOfAny
from mypy.types import AnyType, Instance, Type as MypyType, TypeOfAny
from django.db.models.fields import AutoField, CharField, Field
from mypy_django_plugin.lib import helpers
try:
@@ -115,11 +113,13 @@ class DjangoFieldsContext:
is_nullable = self.get_field_nullability(field, method)
if isinstance(field, RelatedField):
related_model_cls = self.django_context.fields_context.get_related_model_cls(field)
if method == 'values':
primary_key_field = self.django_context.get_primary_key_field(field.related_model)
primary_key_field = self.django_context.get_primary_key_field(related_model_cls)
return self.get_field_get_type(api, primary_key_field, method=method)
model_info = helpers.lookup_class_typeinfo(api, field.related_model)
model_info = helpers.lookup_class_typeinfo(api, related_model_cls)
if model_info is None:
return AnyType(TypeOfAny.unannotated)
@@ -128,6 +128,25 @@ class DjangoFieldsContext:
return helpers.get_private_descriptor_type(field_info, '_pyi_private_get_type',
is_nullable=is_nullable)
def get_related_model_cls(self, field: Union[RelatedField, ForeignObjectRel]) -> Type[Model]:
if isinstance(field, RelatedField):
related_model_cls = field.remote_field.model
else:
related_model_cls = field.field.model
if isinstance(related_model_cls, str):
if related_model_cls == 'self':
# same model
related_model_cls = field.model
elif '.' not in related_model_cls:
# same file model
related_model_fullname = field.model.__module__ + '.' + related_model_cls
related_model_cls = self.django_context.get_model_class_by_fullname(related_model_fullname)
else:
related_model_cls = self.django_context.apps_registry.get_model(related_model_cls)
return related_model_cls
class DjangoLookupsContext:
def __init__(self, django_context: 'DjangoContext'):
@@ -146,12 +165,12 @@ class DjangoLookupsContext:
return self.django_context.get_primary_key_field(currently_observed_model)
current_field = currently_observed_model._meta.get_field(field_part)
if not isinstance(current_field, (ForeignObjectRel, RelatedField)):
continue
currently_observed_model = self.django_context.fields_context.get_related_model_cls(current_field)
if isinstance(current_field, ForeignObjectRel):
currently_observed_model = current_field.related_model
current_field = self.django_context.get_primary_key_field(currently_observed_model)
else:
if isinstance(current_field, RelatedField):
currently_observed_model = current_field.related_model
# if it is None, solve_lookup_type() will fail earlier
assert current_field is not None
@@ -170,20 +189,26 @@ class DjangoContext:
self.settings = settings
@cached_property
def model_modules(self) -> Dict[str, List[Type[Model]]]:
def model_modules(self) -> Dict[str, Set[Type[Model]]]:
""" All modules that contain Django models. """
if self.apps_registry is None:
return {}
modules: Dict[str, List[Type[Model]]] = defaultdict(list)
for model_cls in self.apps_registry.get_models():
modules[model_cls.__module__].append(model_cls)
modules: Dict[str, Set[Type[Model]]] = defaultdict(set)
for concrete_model_cls in self.apps_registry.get_models():
modules[concrete_model_cls.__module__].add(concrete_model_cls)
# collect abstract=True models
for model_cls in concrete_model_cls.mro()[1:]:
if (issubclass(model_cls, Model)
and hasattr(model_cls, '_meta')
and model_cls._meta.abstract):
modules[model_cls.__module__].add(model_cls)
return modules
def get_model_class_by_fullname(self, fullname: str) -> Optional[Type[Model]]:
# Returns None if Model is abstract
module, _, model_cls_name = fullname.rpartition('.')
for model_cls in self.model_modules.get(module, []):
for model_cls in self.model_modules.get(module, set()):
if model_cls.__name__ == model_cls_name:
return model_cls
return None
@@ -209,7 +234,8 @@ class DjangoContext:
from django.contrib.contenttypes.fields import GenericForeignKey
expected_types = {}
# add pk
# add pk if not abstract=True
if not model_cls._meta.abstract:
primary_key_field = self.get_primary_key_field(model_cls)
field_set_type = self.fields_context.get_field_set_type(api, primary_key_field, method=method)
expected_types['pk'] = field_set_type
@@ -228,9 +254,9 @@ class DjangoContext:
expected_types[field_name] = AnyType(TypeOfAny.unannotated)
continue
related_model = field.related_model
if related_model._meta.proxy_for_model:
related_model = field.related_model._meta.proxy_for_model
related_model = self.fields_context.get_related_model_cls(field)
if related_model._meta.proxy_for_model is not None:
related_model = related_model._meta.proxy_for_model
related_model_info = helpers.lookup_class_typeinfo(api, related_model)
if related_model_info is None:

View File

@@ -1,19 +1,15 @@
from collections import OrderedDict
from typing import TYPE_CHECKING, Any, Dict, List, Optional, Set, Union, cast
from typing import Any, Dict, List, Optional, Set, TYPE_CHECKING, Union, cast
from mypy import checker
from mypy.checker import TypeChecker
from mypy.mro import calculate_mro
from mypy.nodes import (
GDEF, MDEF, Block, ClassDef, Expression, MemberExpr, MypyFile, NameExpr, StrExpr, SymbolNode, SymbolTable,
SymbolTableNode, TypeInfo, Var,
)
from mypy.nodes import (Block, ClassDef, Expression, GDEF, MDEF, MemberExpr, MypyFile, NameExpr, StrExpr, SymbolNode,
SymbolTable, SymbolTableNode, TypeInfo, Var)
from mypy.plugin import (
AttributeContext, CheckerPluginInterface, FunctionContext, MethodContext,
)
from mypy.types import AnyType, Instance, NoneTyp, TupleType
from mypy.types import Type as MypyType
from mypy.types import TypedDictType, TypeOfAny, UnionType
from mypy.types import AnyType, Instance, NoneTyp, TupleType, Type as MypyType, TypeOfAny, TypedDictType, UnionType
if TYPE_CHECKING:
from mypy_django_plugin.django.context import DjangoContext

View File

@@ -7,7 +7,7 @@ from mypy.errors import Errors
from mypy.nodes import MypyFile, TypeInfo
from mypy.options import Options
from mypy.plugin import (
AnalyzeTypeContext, AttributeContext, ClassDefContext, FunctionContext, MethodContext, Plugin,
AttributeContext, ClassDefContext, FunctionContext, MethodContext, Plugin,
)
from mypy.types import Type as MypyType
@@ -148,12 +148,14 @@ class NewSemanalDjangoPlugin(Plugin):
# forward relations
for field in self.django_context.get_model_fields(model_class):
if isinstance(field, RelatedField):
related_model_module = field.related_model.__module__
related_model_cls = self.django_context.fields_context.get_related_model_cls(field)
related_model_module = related_model_cls.__module__
if related_model_module != file.fullname():
deps.add(self._new_dependency(related_model_module))
# reverse relations
for relation in model_class._meta.related_objects:
related_model_module = relation.related_model.__module__
related_model_cls = self.django_context.fields_context.get_related_model_cls(relation)
related_model_module = related_model_cls.__module__
if related_model_module != file.fullname():
deps.add(self._new_dependency(related_model_module))
return list(deps)
@@ -233,15 +235,6 @@ class NewSemanalDjangoPlugin(Plugin):
return partial(request.set_auth_user_model_as_type_for_request_user, django_context=self.django_context)
return None
def get_type_analyze_hook(self, fullname: str
) -> Optional[Callable[[AnalyzeTypeContext], MypyType]]:
info = self._get_typeinfo_or_none(fullname)
if (info
and info.has_base(fullnames.QUERYSET_CLASS_FULLNAME)
and not info.has_base(fullnames.BASE_MANAGER_CLASS_FULLNAME)):
return partial(querysets.set_first_generic_param_as_default_for_second, fullname=fullname)
return None
def plugin(version):
return NewSemanalDjangoPlugin

View File

@@ -44,9 +44,12 @@ def fill_descriptor_types_for_related_field(ctx: FunctionContext, django_context
assert isinstance(current_field, RelatedField)
related_model = related_model_to_set = current_field.related_model
if related_model_to_set._meta.proxy_for_model:
related_model_to_set = related_model._meta.proxy_for_model
related_model_cls = django_context.fields_context.get_related_model_cls(current_field)
related_model = related_model_cls
related_model_to_set = related_model_cls
if related_model_to_set._meta.proxy_for_model is not None:
related_model_to_set = related_model_to_set._meta.proxy_for_model
typechecker_api = helpers.get_typechecker_api(ctx)

View File

@@ -5,7 +5,7 @@ from mypy.types import Type as MypyType
from mypy.types import TypeOfAny
from mypy_django_plugin.django.context import DjangoContext
from mypy_django_plugin.lib import fullnames, helpers
from mypy_django_plugin.lib import helpers
def _get_field_instance(ctx: MethodContext, field_fullname: str) -> MypyType:
@@ -20,25 +20,31 @@ def return_proper_field_type_from_get_field(ctx: MethodContext, django_context:
# Options instance
assert isinstance(ctx.type, Instance)
# bail if list of generic params is empty
if len(ctx.type.args) == 0:
return ctx.default_return_type
model_type = ctx.type.args[0]
if not isinstance(model_type, Instance):
return _get_field_instance(ctx, fullnames.FIELD_FULLNAME)
return ctx.default_return_type
model_cls = django_context.get_model_class_by_fullname(model_type.type.fullname())
if model_cls is None:
return _get_field_instance(ctx, fullnames.FIELD_FULLNAME)
return ctx.default_return_type
field_name_expr = helpers.get_call_argument_by_name(ctx, 'field_name')
if field_name_expr is None:
return _get_field_instance(ctx, fullnames.FIELD_FULLNAME)
return ctx.default_return_type
field_name = helpers.resolve_string_attribute_value(field_name_expr, ctx, django_context)
if field_name is None:
return _get_field_instance(ctx, fullnames.FIELD_FULLNAME)
return ctx.default_return_type
try:
field = model_cls._meta.get_field(field_name)
except FieldDoesNotExist as exc:
# if model is abstract, do not raise exception, skip false positives
if not model_cls._meta.abstract:
ctx.api.fail(exc.args[0], ctx.context)
return AnyType(TypeOfAny.from_error)

View File

@@ -100,7 +100,8 @@ class AddRelatedModelsId(ModelClassInitializer):
def run_with_model_cls(self, model_cls: Type[Model]) -> None:
for field in model_cls._meta.get_fields():
if isinstance(field, ForeignKey):
rel_primary_key_field = self.django_context.get_primary_key_field(field.related_model)
related_model_cls = self.django_context.fields_context.get_related_model_cls(field)
rel_primary_key_field = self.django_context.get_primary_key_field(related_model_cls)
field_info = self.lookup_class_typeinfo_or_incomplete_defn_error(rel_primary_key_field.__class__)
is_nullable = self.django_context.fields_context.get_field_nullability(field, None)
set_type, get_type = get_field_descriptor_types(field_info, is_nullable)
@@ -156,7 +157,8 @@ class AddManagers(ModelClassInitializer):
# no reverse accessor
continue
related_model_info = self.lookup_class_typeinfo_or_incomplete_defn_error(relation.related_model)
related_model_cls = self.django_context.fields_context.get_related_model_cls(relation)
related_model_info = self.lookup_class_typeinfo_or_incomplete_defn_error(related_model_cls)
if isinstance(relation, OneToOneRel):
self.add_new_node_to_model_class(attname, Instance(related_model_info, []))

View File

@@ -1,20 +1,26 @@
from collections import OrderedDict
from typing import List, Optional, Sequence, Type, Union, cast
from typing import List, Optional, Sequence, Type, Union
from django.core.exceptions import FieldError
from django.db.models.base import Model
from mypy.newsemanal.typeanal import TypeAnalyser
from mypy.nodes import Expression, NameExpr, TypeInfo
from mypy.plugin import AnalyzeTypeContext, FunctionContext, MethodContext
from mypy.types import AnyType, Instance
from mypy.types import Type as MypyType
from mypy.types import TypeOfAny
from django.db.models.fields.related import RelatedField
from mypy.nodes import Expression, NameExpr
from mypy.plugin import FunctionContext, MethodContext
from mypy.types import AnyType, Instance, Type as MypyType, TypeOfAny
from mypy_django_plugin.django.context import DjangoContext
from mypy_django_plugin.lib import fullnames, helpers
def _extract_model_type_from_queryset(queryset_type: Instance) -> Optional[Instance]:
for base_type in [queryset_type, *queryset_type.type.bases]:
if (len(base_type.args)
and isinstance(base_type.args[0], Instance)
and base_type.args[0].type.has_base(fullnames.MODEL_CLASS_FULLNAME)):
return base_type.args[0]
return None
def determine_proper_manager_type(ctx: FunctionContext) -> MypyType:
default_return_type = ctx.default_return_type
assert isinstance(default_return_type, Instance)
@@ -36,7 +42,8 @@ def get_field_type_from_lookup(ctx: MethodContext, django_context: DjangoContext
return None
if isinstance(lookup_field, RelatedField) and lookup_field.column == lookup:
lookup_field = django_context.get_primary_key_field(lookup_field.related_model)
related_model_cls = django_context.fields_context.get_related_model_cls(lookup_field)
lookup_field = django_context.get_primary_key_field(related_model_cls)
field_get_type = django_context.fields_context.get_field_get_type(helpers.get_typechecker_api(ctx),
lookup_field, method=method)
@@ -98,11 +105,10 @@ def extract_proper_type_queryset_values_list(ctx: MethodContext, django_context:
assert isinstance(ctx.type, Instance)
assert isinstance(ctx.default_return_type, Instance)
# bail if queryset of Any or other non-instances
if not isinstance(ctx.type.args[0], Instance):
model_type = _extract_model_type_from_queryset(ctx.type)
if model_type is None:
return AnyType(TypeOfAny.from_omitted_generics)
model_type = ctx.type.args[0]
model_cls = django_context.get_model_class_by_fullname(model_type.type.fullname())
if model_cls is None:
return ctx.default_return_type
@@ -148,11 +154,10 @@ def extract_proper_type_queryset_values(ctx: MethodContext, django_context: Djan
assert isinstance(ctx.type, Instance)
assert isinstance(ctx.default_return_type, Instance)
# if queryset of non-instance type
if not isinstance(ctx.type.args[0], Instance):
model_type = _extract_model_type_from_queryset(ctx.type)
if model_type is None:
return AnyType(TypeOfAny.from_omitted_generics)
model_type = ctx.type.args[0]
model_cls = django_context.get_model_class_by_fullname(model_type.type.fullname())
if model_cls is None:
return ctx.default_return_type
@@ -176,20 +181,3 @@ def extract_proper_type_queryset_values(ctx: MethodContext, django_context: Djan
row_type = helpers.make_typeddict(ctx.api, column_types, set(column_types.keys()))
return helpers.reparametrize_instance(ctx.default_return_type, [model_type, row_type])
def set_first_generic_param_as_default_for_second(ctx: AnalyzeTypeContext, fullname: str) -> MypyType:
type_analyser_api = cast(TypeAnalyser, ctx.api)
info = helpers.lookup_fully_qualified_typeinfo(type_analyser_api.api, fullname) # type: ignore
assert isinstance(info, TypeInfo)
if not ctx.type.args:
return Instance(info, [AnyType(TypeOfAny.explicit), AnyType(TypeOfAny.explicit)])
args = ctx.type.args
if len(args) == 1:
args = [args[0], args[0]]
analyzed_args = [type_analyser_api.analyze_type(arg) for arg in args]
return Instance(info, analyzed_args)

View File

@@ -28,7 +28,6 @@ IGNORED_ERRORS = {
'Cannot assign to a type',
'"HttpResponse" has no attribute',
'"HttpResponseBase" has no attribute',
# '"HttpRequest" has no attribute',
'"object" has no attribute',
'defined in the current module',
re.compile(r'"Callable\[(\[(Any(, )?)*((, )?VarArg\(Any\))?((, )?KwArg\(Any\))?\]|\.\.\.), Any\]" '
@@ -73,6 +72,12 @@ IGNORED_ERRORS = {
'Incompatible types in assignment (expression has type "Callable[',
'SimpleLazyObject'
],
'aggregation': [
re.compile(r'got "Optional\[(Author|Publisher)\]", expected "Union\[(Author|Publisher), Combinable\]"')
],
'annotations': [
'Incompatible type for "store" of "Employee" (got "Optional[Store]", expected "Union[Store, Combinable]")'
],
'apps': [
'Incompatible types in assignment (expression has type "str", target has type "type")',
],
@@ -90,7 +95,8 @@ IGNORED_ERRORS = {
'basic': [
'Unexpected keyword argument "unknown_kwarg" for "refresh_from_db" of "Model"',
'Unexpected attribute "foo" for model "Article"',
'has no attribute "touched"'
'has no attribute "touched"',
'Incompatible types in assignment (expression has type "Type[CustomQuerySet]"'
],
'backends': [
'"DatabaseError" has no attribute "pgcode"'
@@ -114,14 +120,15 @@ IGNORED_ERRORS = {
'custom_managers': [
'Unsupported dynamic base class',
'"Book" has no attribute "favorite_avg"',
'Incompatible types in assignment (expression has type "CharField'
'Incompatible types in assignment (expression has type "CharField',
'Item "Book" of "Optional[Book]" has no attribute "favorite_avg"'
],
'csrf_tests': [
'Incompatible types in assignment (expression has type "property", ' +
'base class "HttpRequest" defined the type as "QueryDict")'
],
'dates': [
'Too few arguments for "dates" of "QuerySet"',
'Too few arguments for "dates" of',
],
'defer': [
'Too many arguments for "refresh_from_db" of "Model"'
@@ -135,6 +142,7 @@ IGNORED_ERRORS = {
],
'db_functions': [
'"FloatModel" has no attribute',
'Incompatible types in assignment (expression has type "Optional[Any]", variable has type "FloatModel")'
],
'decorators': [
'"Type[object]" has no attribute "method"'
@@ -164,9 +172,9 @@ IGNORED_ERRORS = {
],
'get_object_or_404': [
'Argument 1 to "get_object_or_404" has incompatible type "str"; '
+ 'expected "Union[Type[<nothing>], QuerySet[<nothing>, <nothing>]]"',
+ 'expected "Union[Type[<nothing>], QuerySet[<nothing>]]"',
'Argument 1 to "get_list_or_404" has incompatible type "List[Type[Article]]"; '
+ 'expected "Union[Type[<nothing>], QuerySet[<nothing>, <nothing>]]"',
+ 'expected "Union[Type[<nothing>], QuerySet[<nothing>]]"',
'CustomClass'
],
'generic_relations_regress': [
@@ -181,7 +189,7 @@ IGNORED_ERRORS = {
'Argument 1 to "append" of "list" has incompatible type "None"; expected "str"'
],
'lookup': [
'Unexpected keyword argument "headline__startswith" for "in_bulk" of "QuerySet"',
'Unexpected keyword argument "headline__startswith" for "in_bulk" of',
'is called with more than one field'
],
'messages_tests': [
@@ -234,7 +242,9 @@ IGNORED_ERRORS = {
'"Dimension" has no attribute "set_component_order"',
],
'one_to_one': [
'expression has type "None", variable has type "UndergroundBar"'
'expression has type "None", variable has type "UndergroundBar"',
'Item "OneToOneField[Union[Place, Combinable], Place]" '
+ 'of "Union[OneToOneField[Union[Place, Combinable], Place], Any]"',
],
'postgres_tests': [
'DummyArrayField',
@@ -255,9 +265,10 @@ IGNORED_ERRORS = {
'"Person" has no attribute "houses_lst"',
'"Book" has no attribute "first_authors"',
'"Book" has no attribute "the_authors"',
'Incompatible types in assignment (expression has type "List[Room]", variable has type "QuerySet[Room, Room]")',
'"Room" has no attribute "main_room_of_attr"',
'"Room" has no attribute "house_attr"'
'Incompatible types in assignment (expression has type "List[Room]", variable has type "Manager[Room]")',
'Item "Room" of "Optional[Room]" has no attribute "house_attr"',
'Item "Room" of "Optional[Room]" has no attribute "main_room_of_attr"',
'Argument 2 to "Prefetch" has incompatible type "ValuesQuerySet'
],
'proxy_models': [
'Incompatible types in assignment',
@@ -266,12 +277,13 @@ IGNORED_ERRORS = {
'queries': [
'Incompatible types in assignment (expression has type "None", variable has type "str")',
'Invalid index type "Optional[str]" for "Dict[str, int]"; expected type "str"',
'Unsupported operand types for & ("QuerySet[Author, Author]" and "QuerySet[Tag, Tag]")',
'Unsupported operand types for | ("QuerySet[Author, Author]" and "QuerySet[Tag, Tag]")',
'Unsupported operand types for & ("Manager[Author]" and "Manager[Tag]")',
'Unsupported operand types for | ("Manager[Author]" and "Manager[Tag]")',
'ObjectA',
'ObjectB',
'ObjectC',
"'flat' and 'named' can't be used together",
'"Collection[Any]" has no attribute "explain"'
],
'requests': [
'Incompatible types in assignment (expression has type "Dict[str, str]", variable has type "QueryDict")'
@@ -285,6 +297,9 @@ IGNORED_ERRORS = {
'signals': [
'Argument 1 to "append" of "list" has incompatible type "Tuple[Any, Any, Optional[Any], Any]";'
],
'sites_framework': [
'expression has type "CurrentSiteManager[CustomArticle]", base class "AbstractArticle"'
],
'syndication_tests': [
'List or tuple expected as variable arguments'
],
@@ -293,6 +308,10 @@ IGNORED_ERRORS = {
'Incompatible types in assignment (expression has type "None", variable has type "int")',
'"AbstractBaseSession" has no attribute'
],
'select_related': [
'Item "ForeignKey[Union[Genus, Combinable], Genus]" '
+ 'of "Union[ForeignKey[Union[Genus, Combinable], Genus], Any]"'
],
'select_related_onetoone': [
'Incompatible types in assignment (expression has type "Parent2", variable has type "Parent1")',
'"Parent1" has no attribute'

View File

@@ -1,7 +1,7 @@
[isort]
skip =
django-sources,
django-stubs,
django-sources
django-stubs
test-data
include_trailing_comma = true
multi_line_output = 5
@@ -9,8 +9,8 @@ wrap_length = 120
[flake8]
exclude =
django-sources,
django-stubs,
django-sources
django-stubs
test-data
max_line_length = 120

View File

@@ -28,7 +28,7 @@ dependencies = [
setup(
name="django-stubs",
version="1.0.1",
version="1.1.0",
description='Mypy stubs for Django',
long_description=readme,
long_description_content_type='text/markdown',

View File

@@ -520,3 +520,55 @@
pass
class Article(Entry):
pass
- case: test_related_fields_returned_as_descriptors_from_model_class
main: |
from myapp.models import Author, Blog, Publisher, Profile
reveal_type(Author.blogs) # N: Revealed type is 'django.db.models.fields.related_descriptors.ManyToManyDescriptor'
reveal_type(Blog.publisher) # N: Revealed type is 'django.db.models.fields.related_descriptors.ForwardManyToOneDescriptor'
reveal_type(Publisher.profile) # N: Revealed type is 'django.db.models.fields.related_descriptors.ForwardOneToOneDescriptor'
installed_apps:
- myapp
files:
- path: myapp/__init__.py
- path: myapp/models.py
content: |
from django.db import models
class Profile(models.Model):
pass
class Publisher(models.Model):
profile = models.OneToOneField(Profile, on_delete=models.CASCADE)
class Blog(models.Model):
publisher = models.ForeignKey(Publisher, on_delete=models.CASCADE)
class Author(models.Model):
blogs = models.ManyToManyField(Blog)
file = models.FileField()
- case: test_foreign_key_from_superclass_inherits_correctly
main: |
from myapp.models import MyUser, Book, Article, LibraryEntity
reveal_type(Book().registered_by_user) # N: Revealed type is 'myapp.models.MyUser*'
reveal_type(Article().registered_by_user) # N: Revealed type is 'myapp.models.MyUser*'
user = MyUser()
reveal_type(user.book_set) # N: Revealed type is 'django.db.models.manager.RelatedManager[myapp.models.Book]'
reveal_type(user.article_set) # N: Revealed type is 'django.db.models.manager.RelatedManager[myapp.models.Article]'
installed_apps:
- myapp
files:
- path: myapp/__init__.py
- path: myapp/models.py
content: |
from django.db import models
class MyUser(models.Model):
pass
class LibraryEntity(models.Model):
class Meta:
abstract = True
registered_by_user = models.ForeignKey(MyUser, on_delete=models.CASCADE)
class Book(LibraryEntity):
pass
class Article(LibraryEntity):
pass

View File

@@ -3,22 +3,22 @@
from myapp.models import Blog
qs = Blog.objects.all()
reveal_type(qs) # N: Revealed type is 'django.db.models.query.QuerySet[myapp.models.Blog*, myapp.models.Blog*]'
reveal_type(qs) # N: Revealed type is 'django.db.models.manager.Manager[myapp.models.Blog]'
reveal_type(qs.get(id=1)) # N: Revealed type is 'myapp.models.Blog*'
reveal_type(iter(qs)) # N: Revealed type is 'typing.Iterator[myapp.models.Blog*]'
reveal_type(qs.iterator()) # N: Revealed type is 'typing.Iterator[myapp.models.Blog*]'
reveal_type(qs.first()) # N: Revealed type is 'myapp.models.Blog*'
reveal_type(qs.first()) # N: Revealed type is 'Union[myapp.models.Blog*, None]'
reveal_type(qs.earliest()) # N: Revealed type is 'myapp.models.Blog*'
reveal_type(qs[0]) # N: Revealed type is 'myapp.models.Blog*'
reveal_type(qs[:9]) # N: Revealed type is 'django.db.models.query.QuerySet[myapp.models.Blog*, myapp.models.Blog*]'
reveal_type(qs[:9]) # N: Revealed type is 'django.db.models.manager.Manager[myapp.models.Blog]'
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.QuerySet[myapp.models.Blog*, datetime.date]'
reveal_type(Blog.objects.datetimes("created_at", "day")) # N: Revealed type is 'django.db.models.query.QuerySet[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.query.QuerySet[myapp.models.Blog*, myapp.models.Blog*]'
reveal_type(Blog.objects.all() & Blog.objects.all()) # N: Revealed type is 'django.db.models.manager.Manager[myapp.models.Blog]'
installed_apps:
- myapp
files:
@@ -29,10 +29,3 @@
class Blog(models.Model):
created_at = models.DateTimeField()
- case: queryset_could_be_specified_with_one_type
main: |
from typing import Optional
from django.db import models
queryset: models.QuerySet[models.Model] = models.QuerySet()
reveal_type(queryset) # N: Revealed type is 'django.db.models.query.QuerySet[django.db.models.base.Model, django.db.models.base.Model]'

View File

@@ -53,7 +53,7 @@
text = models.CharField(max_length=100)
blog = models.ForeignKey(to=Blog, on_delete=models.CASCADE)
- case: values_list_flat_true
- case: values_list_flat_true_methods
main: |
from myapp.models import MyUser, MyUser2
reveal_type(MyUser.objects.values_list('name', flat=True).get()) # N: Revealed type is 'builtins.str*'
@@ -192,8 +192,10 @@
- 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.QuerySet[myapp.models.Blog, builtins.int]'
reveal_type(Blog.objects.values_list('publisher_id', flat=True)) # N: Revealed type is 'django.db.models.query.QuerySet[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:
- myapp
files:
@@ -205,3 +207,21 @@
pass
class Blog(models.Model):
publisher = models.ForeignKey(to=Publisher, on_delete=models.CASCADE)
- case: subclass_of_queryset_has_proper_typings_on_methods
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]]'
installed_apps:
- myapp
files:
- path: myapp/__init__.py
- path: myapp/models.py
content: |
from django.db import models
class TransactionQuerySet(models.QuerySet['Transaction']):
pass
class Transaction(models.Model):
total = models.IntegerField()

View File

@@ -197,10 +197,10 @@
main: |
from myapp.models import UnrelatedModel, MyModel
reveal_type(UnrelatedModel.objects) # N: Revealed type is 'django.db.models.manager.Manager[myapp.models.UnrelatedModel]'
reveal_type(UnrelatedModel.objects.first()) # N: Revealed type is 'myapp.models.UnrelatedModel*'
reveal_type(UnrelatedModel.objects.first()) # N: Revealed type is 'Union[myapp.models.UnrelatedModel*, None]'
reveal_type(MyModel.objects) # N: Revealed type is 'django.db.models.manager.Manager[myapp.models.MyModel]'
reveal_type(MyModel.objects.first()) # N: Revealed type is 'myapp.models.MyModel*'
reveal_type(MyModel.objects.first()) # N: Revealed type is 'Union[myapp.models.MyModel*, None]'
installed_apps:
- myapp
files:
@@ -218,10 +218,10 @@
main: |
from myapp.models import UnrelatedModel2, MyModel2
reveal_type(UnrelatedModel2.objects) # N: Revealed type is 'django.db.models.manager.Manager[myapp.models.UnrelatedModel2]'
reveal_type(UnrelatedModel2.objects.first()) # N: Revealed type is 'myapp.models.UnrelatedModel2*'
reveal_type(UnrelatedModel2.objects.first()) # N: Revealed type is 'Union[myapp.models.UnrelatedModel2*, None]'
reveal_type(MyModel2.objects) # N: Revealed type is 'django.db.models.manager.Manager[myapp.models.MyModel2]'
reveal_type(MyModel2.objects.first()) # N: Revealed type is 'myapp.models.MyModel2*'
reveal_type(MyModel2.objects.first()) # N: Revealed type is 'Union[myapp.models.MyModel2*, None]'
installed_apps:
- myapp
files:
@@ -239,10 +239,10 @@
main: |
from myapp.models import ParentOfMyModel3, MyModel3
reveal_type(ParentOfMyModel3.objects) # N: Revealed type is 'django.db.models.manager.Manager[myapp.models.ParentOfMyModel3]'
reveal_type(ParentOfMyModel3.objects.first()) # N: Revealed type is 'myapp.models.ParentOfMyModel3*'
reveal_type(ParentOfMyModel3.objects.first()) # N: Revealed type is 'Union[myapp.models.ParentOfMyModel3*, None]'
reveal_type(MyModel3.objects) # N: Revealed type is 'django.db.models.manager.Manager[myapp.models.MyModel3]'
reveal_type(MyModel3.objects.first()) # N: Revealed type is 'myapp.models.MyModel3*'
reveal_type(MyModel3.objects.first()) # N: Revealed type is 'Union[myapp.models.MyModel3*, None]'
installed_apps:
- myapp
files:
@@ -260,10 +260,10 @@
main: |
from myapp.models import ParentOfMyModel4, MyModel4
reveal_type(ParentOfMyModel4.objects) # N: Revealed type is 'django.db.models.manager.Manager[myapp.models.ParentOfMyModel4]'
reveal_type(ParentOfMyModel4.objects.first()) # N: Revealed type is 'myapp.models.ParentOfMyModel4*'
reveal_type(ParentOfMyModel4.objects.first()) # N: Revealed type is 'Union[myapp.models.ParentOfMyModel4*, None]'
reveal_type(MyModel4.objects) # N: Revealed type is 'django.db.models.manager.Manager[myapp.models.MyModel4]'
reveal_type(MyModel4.objects.first()) # N: Revealed type is 'myapp.models.MyModel4*'
reveal_type(MyModel4.objects.first()) # N: Revealed type is 'Union[myapp.models.MyModel4*, None]'
installed_apps:
- myapp
files:
@@ -308,7 +308,7 @@
main: |
from myapp.models import User
reveal_type(User.objects.get()) # N: Revealed type is 'myapp.models.User*'
reveal_type(User.objects.select_related()) # N: Revealed type is 'django.db.models.query.QuerySet[myapp.models.User*, myapp.models.User*]'
reveal_type(User.objects.select_related()) # N: Revealed type is 'myapp.models.User_MyManager[myapp.models.User]'
installed_apps:
- myapp
files:

View File

@@ -233,3 +233,25 @@
name = models.CharField(primary_key=True, max_length=100)
class Book(models.Model):
publisher = models.ForeignKey(Publisher, on_delete=models.CASCADE)
- case: init_in_abstract_model_classmethod_should_not_throw_error_for_valid_fields
main: |
from myapp.models import MyModel
MyModel.base_init()
installed_apps:
- myapp
files:
- path: myapp/__init__.py
- path: myapp/models.py
content: |
from django.db import models
class AbstractModel(models.Model):
class Meta:
abstract = True
text = models.CharField(max_length=100)
@classmethod
def base_init(cls) -> 'AbstractModel':
return cls(text='mytext')
class MyModel(AbstractModel):
pass

View File

@@ -36,3 +36,23 @@
name = models.CharField(max_length=100)
age = models.IntegerField()
to_user = models.ForeignKey('self', on_delete=models.SET_NULL)
- case: get_field_with_abstract_inheritance
main: |
from myapp.models import AbstractModel
class MyModel(AbstractModel):
pass
reveal_type(MyModel._meta.get_field('field')) # N: Revealed type is 'Any'
installed_apps:
- myapp
files:
- path: myapp/__init__.py
- path: myapp/models.py
content: |
from django.db import models
from django.contrib.postgres.fields import ArrayField
class AbstractModel(models.Model):
class Meta:
abstract = True
class MyModel(AbstractModel):
field = ArrayField(models.IntegerField(), default=[])