-
-
Notifications
You must be signed in to change notification settings - Fork 496
Description
This is not a bug report, but a suggestion for a feature. This is a breaking change and requires some hackery. It's something I'm doing in my project, and something to consider for other projects. At the very least this will explain to others how to do what I'm doing.
I use this library with Pyright and without the mypy
plugin. I have found a rudimentary way of getting custom QuerySet
class methods to appear for my managers by doing something close to the following:
QS = TypeVar('QS', bound='QuerySet') # type: ignore
class TypedManager(Generic[M, QS], Manager[M]):
def all(self) -> QS: ...
# ... Many other methods are re-declared here to use `QS` as the QuerySet type ...
I can then declare my QuerySet and Manager classes like so:
# `cast` just to avoid runtime errors, as I don't monkey patch the class.
# In my codebase I have a wrapper type for this.
class FooQuerySet(cast('Type[QuerySet[Foo]]', QuerySet)):
def custom_method(self) -> 'FooQuerySet':
...
class FooManager(TypedManager['Foo', FooQuerySet]):
...
Then the type of Foo.objects.all().custom_method()
will be known. One of the caveats is that you cannot use QuerySet methods in the Manager class with from_queryset
, as there's no way to transform types like that in Python typing at the moment, especially considering the queryset_only
flag. As such, you have to use .all()
before calling custom QuerySet methods.
I suggest changing the Manager
class type arguments to include the QuerySet
type, especially if these generic parameters are ever going to end up in the Django codebase itself. It's the only way to accurately express the types when doing type checking without a plugin system.
One thing to note is that you have to disable type errors for the bound
, but the rest of the code will work in Pyright. This might not work in all type checkers for Python. Ideally, Python typing should be updated to make it possible to express the following instead:
QS = TypeVar('QS', bound='QuerySet[M]')
This is just a feature missing in Python typing at the moment. It is probably worth pushing for Python type checkers to be able to do this before implementing something like this.