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?
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 QuerySets:
# 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]
django: https://github.com/typeddjango/django-stubsdrf: https://github.com/typeddjango/djangorestframework-stubsI 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]).