mirror of
https://github.com/davidhalter/django-stubs.git
synced 2025-12-29 07:06:56 +08:00
Resolve all queryset methods on managers as attributes (#1028)
* Add test case reproducing Sequence name not defined issue * Resolve all manager methods as attribute This changes to logic for resolving methods from the base QuerySet class on managers from copying the methods to use the attribute approach that's already used for methods from custom querysets. This resolves the phantom type errors that stem from the copying. * Disable cache in test case Make sure the test will fail regardless of which mypy.ini file is being using. Co-authored-by: Petter Friberg <petter@5monkeys.se> * Update comments related to copying methods * Use a predefined list of manager methods to update The list of manager methods that returns a queryset, and thus need to have it's return type changed, is small and well defined. Using a predefined list of methods rather than trying to detect these at runtime makes the code much more readable and probably faster as well. Also add `extra()` to the methods tested in from_queryset_includes_methods_returning_queryset, and sort the methods alphabetically. * Revert changes in .github/workflows/tests.yml With cache_disable: true on the test case this is no longer needed to reproduce the bug. * Remove unsued imports and change type of constant - Remove unused imports left behind - Change MANAGER_METHODS_RETURNING_QUERYSET to Final[FrozenSet[str]] * Import Final from typing_extensions Was added in 3.8, we still support 3.7 * Sort imports properly * Remove explicit typing of final frozenset Co-authored-by: Nikita Sobolev <mail@sobolevn.me> * Add comment for test case * Fix typo * Rename variable Co-authored-by: Petter Friberg <petter@5monkeys.se> Co-authored-by: Nikita Sobolev <mail@sobolevn.me>
This commit is contained in:
@@ -350,24 +350,25 @@
|
||||
- case: from_queryset_includes_methods_returning_queryset
|
||||
main: |
|
||||
from myapp.models import MyModel
|
||||
reveal_type(MyModel.objects.none) # N: Revealed type is "def () -> myapp.models.MyQuerySet[myapp.models.MyModel]"
|
||||
reveal_type(MyModel.objects.alias) # N: Revealed type is "def (*args: Any, **kwargs: Any) -> myapp.models.MyQuerySet[myapp.models.MyModel]"
|
||||
reveal_type(MyModel.objects.all) # N: Revealed type is "def () -> myapp.models.MyQuerySet[myapp.models.MyModel]"
|
||||
reveal_type(MyModel.objects.filter) # N: Revealed type is "def (*args: Any, **kwargs: Any) -> myapp.models.MyQuerySet[myapp.models.MyModel]"
|
||||
reveal_type(MyModel.objects.exclude) # N: Revealed type is "def (*args: Any, **kwargs: Any) -> myapp.models.MyQuerySet[myapp.models.MyModel]"
|
||||
reveal_type(MyModel.objects.annotate) # N: Revealed type is "def (*args: Any, **kwargs: Any) -> myapp.models.MyQuerySet[myapp.models.MyModel]"
|
||||
reveal_type(MyModel.objects.complex_filter) # N: Revealed type is "def (filter_obj: Any) -> myapp.models.MyQuerySet[myapp.models.MyModel]"
|
||||
reveal_type(MyModel.objects.union) # N: Revealed type is "def (*other_qs: Any, *, all: builtins.bool =) -> myapp.models.MyQuerySet[myapp.models.MyModel]"
|
||||
reveal_type(MyModel.objects.intersection) # N: Revealed type is "def (*other_qs: Any) -> myapp.models.MyQuerySet[myapp.models.MyModel]"
|
||||
reveal_type(MyModel.objects.defer) # N: Revealed type is "def (*fields: Any) -> myapp.models.MyQuerySet[myapp.models.MyModel]"
|
||||
reveal_type(MyModel.objects.difference) # N: Revealed type is "def (*other_qs: Any) -> myapp.models.MyQuerySet[myapp.models.MyModel]"
|
||||
reveal_type(MyModel.objects.distinct) # N: Revealed type is "def (*field_names: Any) -> myapp.models.MyQuerySet[myapp.models.MyModel]"
|
||||
reveal_type(MyModel.objects.exclude) # N: Revealed type is "def (*args: Any, **kwargs: Any) -> myapp.models.MyQuerySet[myapp.models.MyModel]"
|
||||
reveal_type(MyModel.objects.extra) # N: Revealed type is "def (select: Union[builtins.dict[builtins.str, Any], None] =, where: Union[builtins.list[builtins.str], None] =, params: Union[builtins.list[Any], None] =, tables: Union[builtins.list[builtins.str], None] =, order_by: Union[typing.Sequence[builtins.str], None] =, select_params: Union[typing.Sequence[Any], None] =) -> myapp.models.MyQuerySet[myapp.models.MyModel]"
|
||||
reveal_type(MyModel.objects.filter) # N: Revealed type is "def (*args: Any, **kwargs: Any) -> myapp.models.MyQuerySet[myapp.models.MyModel]"
|
||||
reveal_type(MyModel.objects.intersection) # N: Revealed type is "def (*other_qs: Any) -> myapp.models.MyQuerySet[myapp.models.MyModel]"
|
||||
reveal_type(MyModel.objects.none) # N: Revealed type is "def () -> myapp.models.MyQuerySet[myapp.models.MyModel]"
|
||||
reveal_type(MyModel.objects.only) # N: Revealed type is "def (*fields: Any) -> myapp.models.MyQuerySet[myapp.models.MyModel]"
|
||||
reveal_type(MyModel.objects.order_by) # N: Revealed type is "def (*field_names: Any) -> myapp.models.MyQuerySet[myapp.models.MyModel]"
|
||||
reveal_type(MyModel.objects.prefetch_related) # N: Revealed type is "def (*lookups: Any) -> myapp.models.MyQuerySet[myapp.models.MyModel]"
|
||||
reveal_type(MyModel.objects.reverse) # N: Revealed type is "def () -> myapp.models.MyQuerySet[myapp.models.MyModel]"
|
||||
reveal_type(MyModel.objects.select_for_update) # N: Revealed type is "def (nowait: builtins.bool =, skip_locked: builtins.bool =, of: typing.Sequence[builtins.str] =, no_key: builtins.bool =) -> myapp.models.MyQuerySet[myapp.models.MyModel]"
|
||||
reveal_type(MyModel.objects.select_related) # N: Revealed type is "def (*fields: Any) -> myapp.models.MyQuerySet[myapp.models.MyModel]"
|
||||
reveal_type(MyModel.objects.prefetch_related) # N: Revealed type is "def (*lookups: Any) -> myapp.models.MyQuerySet[myapp.models.MyModel]"
|
||||
reveal_type(MyModel.objects.annotate) # N: Revealed type is "def (*args: Any, **kwargs: Any) -> myapp.models.MyQuerySet[myapp.models.MyModel]"
|
||||
reveal_type(MyModel.objects.alias) # N: Revealed type is "def (*args: Any, **kwargs: Any) -> myapp.models.MyQuerySet[myapp.models.MyModel]"
|
||||
reveal_type(MyModel.objects.order_by) # N: Revealed type is "def (*field_names: Any) -> myapp.models.MyQuerySet[myapp.models.MyModel]"
|
||||
reveal_type(MyModel.objects.distinct) # N: Revealed type is "def (*field_names: Any) -> myapp.models.MyQuerySet[myapp.models.MyModel]"
|
||||
reveal_type(MyModel.objects.reverse) # N: Revealed type is "def () -> myapp.models.MyQuerySet[myapp.models.MyModel]"
|
||||
reveal_type(MyModel.objects.defer) # N: Revealed type is "def (*fields: Any) -> myapp.models.MyQuerySet[myapp.models.MyModel]"
|
||||
reveal_type(MyModel.objects.only) # N: Revealed type is "def (*fields: Any) -> myapp.models.MyQuerySet[myapp.models.MyModel]"
|
||||
reveal_type(MyModel.objects.union) # N: Revealed type is "def (*other_qs: Any, *, all: builtins.bool =) -> myapp.models.MyQuerySet[myapp.models.MyModel]"
|
||||
reveal_type(MyModel.objects.using) # N: Revealed type is "def (alias: Union[builtins.str, None]) -> myapp.models.MyQuerySet[myapp.models.MyModel]"
|
||||
installed_apps:
|
||||
- myapp
|
||||
@@ -384,3 +385,44 @@
|
||||
MyManager = BaseManager.from_queryset(MyQuerySet)
|
||||
class MyModel(models.Model):
|
||||
objects = MyManager()
|
||||
|
||||
|
||||
# This tests a regression where mypy would generate phantom warnings about
|
||||
# undefined types due to unresolved types when copying methods from QuerySet to
|
||||
# a manager dynamically created using Manager.from_queryset().
|
||||
#
|
||||
# For details see: https://github.com/typeddjango/django-stubs/issues/1022
|
||||
- case: from_queryset_custom_auth_user_model
|
||||
# Cache needs to be disabled to consistenly reproduce the bug
|
||||
disable_cache: true
|
||||
main: |
|
||||
from users.models import User
|
||||
custom_settings: |
|
||||
AUTH_USER_MODEL = "users.User"
|
||||
INSTALLED_APPS = ("django.contrib.auth", "django.contrib.contenttypes", "users")
|
||||
files:
|
||||
- path: users/__init__.py
|
||||
- path: users/models.py
|
||||
content: |
|
||||
from django.contrib.auth.models import AbstractBaseUser
|
||||
from django.db import models
|
||||
|
||||
from .querysets import UserQuerySet
|
||||
|
||||
UserManager = models.Manager.from_queryset(UserQuerySet)
|
||||
|
||||
class User(AbstractBaseUser):
|
||||
email = models.EmailField(unique=True)
|
||||
objects = UserManager()
|
||||
USERNAME_FIELD = "email"
|
||||
|
||||
- path: users/querysets.py
|
||||
content: |
|
||||
from django.db import models
|
||||
from typing import Optional, TYPE_CHECKING
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from .models import User
|
||||
|
||||
class UserQuerySet(models.QuerySet["User"]):
|
||||
pass
|
||||
|
||||
Reference in New Issue
Block a user