How to use Python type hints with Django QuerySet?
Solution 1
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.
Solution 2
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
Solution 3
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]
).
Solution 4
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.
Solution 5
IMHO, the proper way to do it is to define a type that inherits QuerySet
and specify a generic return type for the iterator.
from django.db.models import QuerySet
from typing import Iterator, TypeVar, Generic, Optional
T = TypeVar("T")
class QuerySetType(Generic[T], QuerySet): # QuerySet + Iterator
def __iter__(self) -> Iterator[T]:
pass
def first(self) -> Optional[T]:
pass
# ... add more refinements
Then you can use it like this:
users: QuerySetType[User] = User.objects.all()
for user in users:
print(user.email) # typing OK!
user = users.first() # typing OK!
Related videos on Youtube
Алексей Голобурдин
Updated on April 19, 2021Comments
-
Алексей Голобурдин about 3 years
Is it possible to specify type of records in Django QuerySet with Python type hints? Something like
QuerySet[SomeModel]
?For example, we have model:
class SomeModel(models.Model): smth = models.IntegerField()
And we want to pass QuerySet of that model as param in func:
def somefunc(rows: QuerySet): pass
But how to specify type of records in QuerySet, like with
List[SomeModel]
:def somefunc(rows: List[SomeModel]): pass
but with QuerySet?
-
sobolevn almost 5 years
-
JayTurnr almost 4 yearsYou might be looking for SomeModelQuerySet, import it from the same path you import SomeModel from
-
-
sobolevn almost 5 yearsThere are
stubs
for django with typedQuerySet
and models: github.com/typeddjango/django-stubs Tutorial: sobolevn.me/2019/08/typechecking-django-and-drf -
Manu Artero over 3 yearsquick note if someone is using VSCode; this solution works perfectly if response type is surronded by single quotes (don't know the reason);
def my_method(self) -> 'QueryType[MyModel]': ...