mirror of
https://github.com/davidhalter/django-stubs.git
synced 2025-12-10 22:11:54 +08:00
Compare commits
29 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3881df188c | ||
|
|
b8379d4fe6 | ||
|
|
caa0e60743 | ||
|
|
de4fa92441 | ||
|
|
dce0c0e930 | ||
|
|
fc9a335dfd | ||
|
|
5fc39ff110 | ||
|
|
e95b40ef52 | ||
|
|
c91a6d1d5b | ||
|
|
d7e8222163 | ||
|
|
09767210ec | ||
|
|
656105bab2 | ||
|
|
ff7bf33e9c | ||
|
|
d94b7b0c6a | ||
|
|
7bf1664307 | ||
|
|
0545c2d3ea | ||
|
|
825931da9f | ||
|
|
737fd239b6 | ||
|
|
7f476057b0 | ||
|
|
d31512854a | ||
|
|
552de422dc | ||
|
|
a9978cc021 | ||
|
|
bb08212b20 | ||
|
|
5d2efdb80b | ||
|
|
27793ecd32 | ||
|
|
dddcb20fe4 | ||
|
|
ac40b80764 | ||
|
|
6b21a0476d | ||
|
|
735b58e9bf |
10
.editorconfig
Normal file
10
.editorconfig
Normal 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
35
.github/ISSUE_TEMPLATE/bug.md
vendored
Normal 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
29
.github/pull_request_template.md
vendored
Normal 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!
|
||||
-->
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
# pep484 stubs for Django framework
|
||||
|
||||
[](https://travis-ci.org/mkurnikov/django-stubs)
|
||||
[](https://travis-ci.org/typeddjango/django-stubs)
|
||||
[](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.
|
||||
@@ -42,6 +42,11 @@ django_settings_module = mysettings
|
||||
```
|
||||
where `mysettings` is a value of `DJANGO_SETTINGS_MODULE` (with or without quotes)
|
||||
|
||||
Do you have trouble with mypy / the django plugin not finding your settings module? Try adding the root path of your project to your PYTHONPATH environment variable. If you use pipenv you can add the following to an `.env` file in your project root which pipenv will run automatically before executing any commands.:
|
||||
```
|
||||
PYTHONPATH=${PYTHONPATH}:${PWD}
|
||||
```
|
||||
|
||||
New implementation uses Django runtime to extract models information, so it will crash, if your installed apps `models.py` is not correct. For this same reason, you cannot use `reveal_type` inside global scope of any Python file that will be executed for `django.setup()`.
|
||||
|
||||
In other words, if your `manage.py runserver` crashes, mypy will crash too.
|
||||
@@ -63,4 +68,4 @@ Otherwise, custom type will be created in mypy, named `MyUser__MyUserManager`, w
|
||||
|
||||
We have Gitter here https://gitter.im/mypy-django/Lobby.
|
||||
|
||||
If you think you have more generic typing issue, please refer to https://github.com/python/mypy and their Gitter.
|
||||
If you think you have more generic typing issue, please refer to https://github.com/python/mypy and their Gitter.
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
black
|
||||
pytest-mypy-plugins==1.0.3
|
||||
psycopg2
|
||||
flake8
|
||||
isort==4.3.4
|
||||
-e .
|
||||
|
||||
@@ -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: ...
|
||||
|
||||
3
django-stubs/contrib/admindocs/urls.pyi
Normal file
3
django-stubs/contrib/admindocs/urls.pyi
Normal file
@@ -0,0 +1,3 @@
|
||||
from typing import Any, List
|
||||
|
||||
urlpatterns: List[Any] = ...
|
||||
3
django-stubs/contrib/flatpages/urls.pyi
Normal file
3
django-stubs/contrib/flatpages/urls.pyi
Normal file
@@ -0,0 +1,3 @@
|
||||
from typing import Any, List
|
||||
|
||||
urlpatterns: List[Any] = ...
|
||||
@@ -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: ...
|
||||
|
||||
@@ -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: ...
|
||||
|
||||
@@ -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]: ...
|
||||
|
||||
24
django-stubs/core/cache/backends/locmem.pyi
vendored
24
django-stubs/core/cache/backends/locmem.pyi
vendored
@@ -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: ...
|
||||
|
||||
10
django-stubs/core/cache/backends/memcached.pyi
vendored
Normal file
10
django-stubs/core/cache/backends/memcached.pyi
vendored
Normal 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): ...
|
||||
@@ -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]: ...
|
||||
|
||||
@@ -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): ...
|
||||
|
||||
@@ -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]] = ...,
|
||||
|
||||
@@ -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 = ...
|
||||
|
||||
@@ -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 = ...
|
||||
|
||||
@@ -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]]: ...
|
||||
|
||||
@@ -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]]] = ...
|
||||
|
||||
@@ -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 = ...
|
||||
|
||||
@@ -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: ...
|
||||
|
||||
@@ -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: ...
|
||||
|
||||
@@ -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]): ...
|
||||
|
||||
|
||||
@@ -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 = ...
|
||||
|
||||
@@ -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: ...
|
||||
|
||||
@@ -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: ...
|
||||
|
||||
@@ -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: ...
|
||||
|
||||
@@ -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 = ...
|
||||
|
||||
@@ -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: ...
|
||||
|
||||
@@ -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: ...
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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]]]: ...
|
||||
|
||||
@@ -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: ...
|
||||
|
||||
@@ -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: ...
|
||||
|
||||
|
||||
@@ -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: ...
|
||||
|
||||
@@ -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: ...
|
||||
|
||||
@@ -1,23 +1,26 @@
|
||||
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.contrib.postgres.fields import ArrayField
|
||||
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:
|
||||
from django.contrib.postgres.fields import ArrayField
|
||||
except ImportError:
|
||||
class ArrayField: # type: ignore
|
||||
pass
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from django.apps.registry import Apps # noqa: F401
|
||||
from django.conf import LazySettings # noqa: F401
|
||||
@@ -110,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)
|
||||
|
||||
@@ -123,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'):
|
||||
@@ -141,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
|
||||
@@ -165,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
|
||||
@@ -204,10 +234,11 @@ class DjangoContext:
|
||||
from django.contrib.contenttypes.fields import GenericForeignKey
|
||||
|
||||
expected_types = {}
|
||||
# add pk
|
||||
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
|
||||
# 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
|
||||
|
||||
for field in model_cls._meta.get_fields():
|
||||
if isinstance(field, Field):
|
||||
@@ -223,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:
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
@@ -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,26 +20,32 @@ 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:
|
||||
ctx.api.fail(exc.args[0], ctx.context)
|
||||
# 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)
|
||||
|
||||
field_fullname = helpers.get_class_fullname(field.__class__)
|
||||
|
||||
@@ -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, []))
|
||||
|
||||
@@ -1,19 +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)
|
||||
@@ -34,6 +41,10 @@ def get_field_type_from_lookup(ctx: MethodContext, django_context: DjangoContext
|
||||
ctx.api.fail(exc.args[0], ctx.context)
|
||||
return None
|
||||
|
||||
if isinstance(lookup_field, RelatedField) and lookup_field.column == lookup:
|
||||
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)
|
||||
return field_get_type
|
||||
@@ -94,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
|
||||
@@ -144,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
|
||||
@@ -172,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)
|
||||
|
||||
@@ -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'
|
||||
|
||||
12
setup.cfg
12
setup.cfg
@@ -1,17 +1,17 @@
|
||||
[isort]
|
||||
skip =
|
||||
django-sources,
|
||||
django-stubs,
|
||||
test-data
|
||||
django-sources
|
||||
django-stubs
|
||||
test-data
|
||||
include_trailing_comma = true
|
||||
multi_line_output = 5
|
||||
wrap_length = 120
|
||||
|
||||
[flake8]
|
||||
exclude =
|
||||
django-sources,
|
||||
django-stubs,
|
||||
test-data
|
||||
django-sources
|
||||
django-stubs
|
||||
test-data
|
||||
max_line_length = 120
|
||||
|
||||
[metadata]
|
||||
|
||||
4
setup.py
4
setup.py
@@ -24,13 +24,11 @@ dependencies = [
|
||||
'mypy>=0.720,<0.730',
|
||||
'typing-extensions',
|
||||
'django',
|
||||
# depends on psycopg2 because of Postgres' ArrayField support
|
||||
'psycopg2'
|
||||
]
|
||||
|
||||
setup(
|
||||
name="django-stubs",
|
||||
version="1.0.0",
|
||||
version="1.1.0",
|
||||
description='Mypy stubs for Django',
|
||||
long_description=readme,
|
||||
long_description_content_type='text/markdown',
|
||||
|
||||
@@ -519,4 +519,56 @@
|
||||
class Book(Entry):
|
||||
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
|
||||
@@ -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:
|
||||
@@ -28,11 +28,4 @@
|
||||
from django.db import models
|
||||
|
||||
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]'
|
||||
created_at = models.DateTimeField()
|
||||
@@ -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*'
|
||||
@@ -188,3 +188,40 @@
|
||||
class Entry(models.Model):
|
||||
text = models.CharField(max_length=100)
|
||||
blog = models.ForeignKey(Blog, on_delete=models.CASCADE)
|
||||
|
||||
- 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]'
|
||||
# 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:
|
||||
- path: myapp/__init__.py
|
||||
- path: myapp/models.py
|
||||
content: |
|
||||
from django.db import models
|
||||
class Publisher(models.Model):
|
||||
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()
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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
|
||||
@@ -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=[])
|
||||
|
||||
Reference in New Issue
Block a user