How to use Python type hints with Django QuerySet?
One solution may be using Union typing class.
from typing import Union, List
from django.db.models import QuerySet
from my_app.models import MyModel
def somefunc(row: Union[QuerySet, List[MyModel]]):
pass
Now when you slice the row
argument it will know that the returned type is either another list of MyModel or an instance of MyModel, whilst also hinting that the methods of the QuerySet
class are available on the row
argument too.
There's a special package called django-stubs
(the name follows PEP561
) to type your django
code.
That's how it works:
# server/apps/main/views.py
from django.http import HttpRequest, HttpResponse
from django.shortcuts import render
def index(request: HttpRequest) -> HttpResponse:
reveal_type(request.is_ajax)
reveal_type(request.user)
return render(request, 'main/index.html')
Output:
» PYTHONPATH="$PYTHONPATH:$PWD" mypy server
server/apps/main/views.py:14: note: Revealed type is 'def () -> builtins.bool'
server/apps/main/views.py:15: note: Revealed type is 'django.contrib.auth.models.User'
And with models and QuerySet
s:
# server/apps/main/logic/repo.py
from django.db.models.query import QuerySet
from server.apps.main.models import BlogPost
def published_posts() -> 'QuerySet[BlogPost]': # works fine!
return BlogPost.objects.filter(
is_published=True,
)
Output:
reveal_type(published_posts().first())
# => Union[server.apps.main.models.BlogPost*, None]
- Complete tutorial: https://sobolevn.me/2019/08/typechecking-django-and-drf
- Types for
django
: https://github.com/typeddjango/django-stubs - Types for
drf
: https://github.com/typeddjango/djangorestframework-stubs
I made this helper class to get a generic type hint:
from django.db.models import QuerySet
from typing import Iterator, Union, TypeVar, Generic
T = TypeVar("T")
class ModelType(Generic[T]):
def __iter__(self) -> Iterator[Union[T, QuerySet]]:
pass
Then use it like this:
def somefunc(row: ModelType[SomeModel]):
pass
This reduces the noise everytime I use this type and it make it usable between models (like ModelType[DifferentModel]
).
This is an improved helper class of Or Duan.
from django.db.models import QuerySet
from typing import Iterator, TypeVar, Generic
_Z = TypeVar("_Z")
class QueryType(Generic[_Z], QuerySet):
def __iter__(self) -> Iterator[_Z]: ...
This class is used specifically for QuerySet
object such as when you use filter
in a query.
Sample:
from some_file import QueryType
sample_query: QueryType[SampleClass] = SampleClass.objects.filter(name=name)
Now the interpreter recognizes the sample_query
as a QuerySet
object and you will get suggestions such as count()
and while looping through the objects, you will get suggestions for the SampleClass
Note
This format of type hinting is available from python3.6
onwards.
You can also use django_hint which has hinting classes specifically for Django.