Notify when Manager.from_queryset happens inside model class body (#824)

* Refactor to more easily support additional config options

* Notify when Manager.from_queryset happens inside model class body

- A warning will be emitted whenever `Manager.from_queryset` happens
  inside of a model class body

* Resolve generated default manager types before final iteration

A default manager on a model should always exist, eventually. Although,
we extend to look through dynamically generated managers on each
iteration instead of deferring until the final iteration.
This commit is contained in:
Petter Friberg
2022-01-21 17:46:56 +01:00
committed by GitHub
parent 140bb38a79
commit edec5a1c99
8 changed files with 260 additions and 156 deletions

View File

@@ -608,8 +608,14 @@
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]"
reveal_type(user.book_set) # N: Revealed type is "myapp.models.MyUser_Book_RelatedManager1"
reveal_type(user.article_set) # N: Revealed type is "myapp.models.MyUser_Article_RelatedManager1"
reveal_type(user.book_set.add) # N: Revealed type is "def (*objs: Union[myapp.models.Book*, builtins.int], *, bulk: builtins.bool =)"
reveal_type(user.article_set.add) # N: Revealed type is "def (*objs: Union[myapp.models.Article*, builtins.int], *, bulk: builtins.bool =)"
reveal_type(user.book_set.filter) # N: Revealed type is "def (*args: Any, **kwargs: Any) -> myapp.models.LibraryEntityQuerySet[myapp.models.Book*]"
reveal_type(user.article_set.filter) # N: Revealed type is "def (*args: Any, **kwargs: Any) -> myapp.models.LibraryEntityQuerySet[myapp.models.Article*]"
reveal_type(user.book_set.queryset_method()) # N: Revealed type is "builtins.int"
reveal_type(user.article_set.queryset_method()) # N: Revealed type is "builtins.int"
installed_apps:
- myapp
files:
@@ -620,11 +626,13 @@
class MyUser(models.Model):
pass
class LibraryEntityQuerySet(models.QuerySet):
pass
def queryset_method(self) -> int:
return 1
LibraryEntityManager = models.Manager.from_queryset(LibraryEntityQuerySet)
class LibraryEntity(models.Model):
class Meta:
abstract = True
objects = models.Manager.from_queryset(LibraryEntityQuerySet)()
objects = LibraryEntityManager()
registered_by_user = models.ForeignKey(MyUser, on_delete=models.CASCADE)
class Book(LibraryEntity):
pass

View File

@@ -309,3 +309,40 @@
NewManager = MyManager.from_queryset(ModelQuerySet)
class MyModel(models.Model):
objects = NewManager()
- case: from_queryset_in_model_class_body_yields_message
main: |
from myapp.models import MyModel
reveal_type(MyModel.base_manager) # N: Revealed type is "myapp.models.BaseManagerFromMyQuerySet[myapp.models.MyModel]"
reveal_type(MyModel.manager) # N: Revealed type is "myapp.models.ManagerFromMyQuerySet[myapp.models.MyModel]"
reveal_type(MyModel.custom_manager) # N: Revealed type is "myapp.models.MyManagerFromMyQuerySet[myapp.models.MyModel]"
installed_apps:
- myapp
files:
- path: myapp/__init__.py
- path: myapp/models.py
content: |
from django.db import models
from django.db.models.manager import BaseManager
class MyQuerySet(models.QuerySet["MyModel"]):
def queryset_method(self) -> int:
return 1
class MyManager(BaseManager):
...
BaseManagerFromMyQuerySet = BaseManager.from_queryset(MyQuerySet)
ManagerFromMyQuerySet = models.Manager.from_queryset(MyQuerySet)
MyManagerFromMyQuerySet = MyManager.from_queryset(MyQuerySet)
class MyModel(models.Model):
objects1 = BaseManager.from_queryset(MyQuerySet)() # E: `.from_queryset` called from inside model class body
objects2 = BaseManager.from_queryset(MyQuerySet) # E: `.from_queryset` called from inside model class body
objects3 = models.Manager.from_queryset(MyQuerySet)() # E: `.from_queryset` called from inside model class body
objects4 = models.Manager.from_queryset(MyQuerySet) # E: `.from_queryset` called from inside model class body
objects5 = MyManager.from_queryset(MyQuerySet) # E: `.from_queryset` called from inside model class body
objects6 = MyManager.from_queryset(MyQuerySet)() # E: `.from_queryset` called from inside model class body
# Initiating the manager type is fine
base_manager = BaseManagerFromMyQuerySet()
manager = ManagerFromMyQuerySet()
custom_manager = MyManagerFromMyQuerySet()