mirror of
https://github.com/davidhalter/django-stubs.git
synced 2025-12-21 19:32:16 +08:00
Add support for inline from_queryset in model classes (#1045)
* Add support for inline from_queryset in model classes
This adds support for calling <Manager>.from_queryset(<QuerySet>)()
inline in models, for example like this:
class MyModel(models.Model):
objects = MyManager.from_queryset(MyQuerySet)()
This is done by inspecting the class body in the transform_class_hook
* Fix missing methods on copied manager
* Add test and other minor tweaks
* Always create manager at module level
When the manager is added at the class level, which happened when it was
created inline in the model body, it's not possible to retrieve the
manager again based on fullname. That lead to problems with inheritance
and the default manager.
This commit is contained in:
@@ -678,7 +678,6 @@
|
||||
def custom(self) -> None:
|
||||
pass
|
||||
|
||||
# Note, that we cannot resolve dynamic calls for custom managers:
|
||||
class Transaction(models.Model):
|
||||
objects = BaseManager.from_queryset(TransactionQuerySet)
|
||||
def test(self) -> None:
|
||||
@@ -689,9 +688,8 @@
|
||||
class TransactionLog(models.Model):
|
||||
transaction = models.ForeignKey(Transaction, on_delete=models.CASCADE)
|
||||
out: |
|
||||
myapp/models:9: error: `.from_queryset` called from inside model class body
|
||||
myapp/models:11: note: Revealed type is "django.db.models.manager.RelatedManager[myapp.models.TransactionLog]"
|
||||
myapp/models:13: note: Revealed type is "Any"
|
||||
myapp/models:10: note: Revealed type is "django.db.models.manager.RelatedManager[myapp.models.TransactionLog]"
|
||||
myapp/models:12: note: Revealed type is "Any"
|
||||
|
||||
|
||||
- case: resolve_primary_keys_for_foreign_keys_with_abstract_self_model
|
||||
|
||||
@@ -359,12 +359,17 @@
|
||||
class MyModel(models.Model):
|
||||
objects = NewManager()
|
||||
|
||||
- case: from_queryset_in_model_class_body_yields_message
|
||||
- case: test_queryset_in_model_class_body
|
||||
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]"
|
||||
reveal_type(MyModel.objects) # N: Revealed type is "myapp.models.MyManagerFromMyQuerySet[myapp.models.MyModel]"
|
||||
reveal_type(MyModel._default_manager) # N: Revealed type is "myapp.models.MyManagerFromMyQuerySet[myapp.models.MyModel]"
|
||||
reveal_type(MyModel.objects.all) # N: Revealed type is "def () -> myapp.models.MyQuerySet[myapp.models.MyModel]"
|
||||
reveal_type(MyModel.objects.custom) # N: Revealed type is "def () -> myapp.models.MyQuerySet"
|
||||
reveal_type(MyModel.objects.all().filter) # N: Revealed type is "def (*args: Any, **kwargs: Any) -> myapp.models.MyQuerySet[myapp.models.MyModel]"
|
||||
reveal_type(MyModel.objects.custom().filter) # N: Revealed type is "def (*args: Any, **kwargs: Any) -> myapp.models.MyQuerySet"
|
||||
reveal_type(MyModel.objects2) # N: Revealed type is "myapp.models.MyManagerFromMyQuerySet[myapp.models.MyModel]"
|
||||
reveal_type(MyModel._default_manager) # N: Revealed type is "myapp.models.MyManagerFromMyQuerySet[myapp.models.MyModel]"
|
||||
installed_apps:
|
||||
- myapp
|
||||
files:
|
||||
@@ -372,29 +377,42 @@
|
||||
- path: myapp/models.py
|
||||
content: |
|
||||
from django.db import models
|
||||
from django.db.models.manager import BaseManager
|
||||
|
||||
class MyManager(models.Manager["MyModel"]):
|
||||
pass
|
||||
|
||||
class MyQuerySet(models.QuerySet["MyModel"]):
|
||||
def queryset_method(self) -> int:
|
||||
return 1
|
||||
def custom(self) -> "MyQuerySet":
|
||||
pass
|
||||
|
||||
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()
|
||||
objects = MyManager.from_queryset(MyQuerySet)()
|
||||
objects2 = MyManager.from_queryset(MyQuerySet)()
|
||||
|
||||
- case: test_queryset_in_model_class_body_subclass
|
||||
main: |
|
||||
from myapp.models import MyModel
|
||||
reveal_type(MyModel.objects) # N: Revealed type is "myapp.models.BaseManagerFromBaseQuerySet[myapp.models.MyModel]"
|
||||
installed_apps:
|
||||
- myapp
|
||||
files:
|
||||
- path: myapp/__init__.py
|
||||
- path: myapp/models.py
|
||||
content: |
|
||||
from django.db import models
|
||||
|
||||
class BaseManager(models.Manager["BaseModel"]):
|
||||
pass
|
||||
|
||||
class BaseQuerySet(models.QuerySet["BaseModel"]):
|
||||
def custom(self) -> "BaseQuerySet":
|
||||
pass
|
||||
|
||||
class BaseModel(models.Model):
|
||||
objects = BaseManager.from_queryset(BaseQuerySet)()
|
||||
|
||||
class MyModel(BaseModel):
|
||||
pass
|
||||
|
||||
- case: from_queryset_includes_methods_returning_queryset
|
||||
main: |
|
||||
|
||||
@@ -332,14 +332,14 @@
|
||||
- case: custom_manager_returns_proper_model_types
|
||||
main: |
|
||||
from myapp.models import User
|
||||
reveal_type(User.objects) # N: Revealed type is "myapp.models.User_MyManager2[myapp.models.User]"
|
||||
reveal_type(User.objects) # N: Revealed type is "myapp.models.User_MyManager[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.get()) # N: Revealed type is "myapp.models.User"
|
||||
reveal_type(User.objects.get_instance()) # N: Revealed type is "builtins.int"
|
||||
reveal_type(User.objects.get_instance_untyped('hello')) # N: Revealed type is "Any"
|
||||
|
||||
from myapp.models import ChildUser
|
||||
reveal_type(ChildUser.objects) # N: Revealed type is "myapp.models.ChildUser_MyManager2[myapp.models.ChildUser]"
|
||||
reveal_type(ChildUser.objects) # N: Revealed type is "myapp.models.ChildUser_MyManager[myapp.models.ChildUser]"
|
||||
reveal_type(ChildUser.objects.select_related()) # N: Revealed type is "django.db.models.query._QuerySet[myapp.models.ChildUser, myapp.models.ChildUser]"
|
||||
reveal_type(ChildUser.objects.get()) # N: Revealed type is "myapp.models.ChildUser"
|
||||
reveal_type(ChildUser.objects.get_instance()) # N: Revealed type is "builtins.int"
|
||||
|
||||
Reference in New Issue
Block a user