Type stubs for Django
Project description
django-types
Type stubs for Django.
Note: this project was forked from https://github.com/typeddjango/django-stubs with the goal of removing the
mypy
plugin dependency so thatmypy
can't crash due to Django config, and that non-mypy
type checkers likepyright
will work better with Django.
install
pip install django-types
If you're on a Django version < 3.1, you'll need to monkey patch Django's
QuerySet
and Manager
classes so we can index into them with a generic
argument. You can either use django-stubs-ext
or do this yourself manually:
# in settings.py
from django.db.models.manager import BaseManager
from django.db.models.query import QuerySet
for cls in (QuerySet, BaseManager):
cls.__class_getitem__ = classmethod(lambda cls, *args, **kwargs: cls) # type: ignore [attr-defined]
usage
getting objects
to work
By default the base Model
class doesn't have objects
defined, so you'll
have to explicitly type the property.
from django.db import connection, models
from django.db.models.manager import Manager
class User(models.Model):
title = models.CharField(max_length=255)
objects = Manager["User"]()
reveal_type(User.objects.all().first())
# note: Revealed type is 'Optional[User]'
ForeignKey ids as properties in ORM models
When defining a Django ORM model with a foreign key, like so:
class User(models.Model):
team = models.ForeignKey("Team", null=True, on_delete=models.SET_NULL)
two properties are created, team
as expected, and team_id
. In order for
mypy to know about the id property we need to define it manually as follows:
from typing import TYPE_CHECKING
class User(models.Model):
team = models.ForeignKey("Team", null=True, on_delete=models.SET_NULL)
if TYPE_CHECKING:
team_id: int
HttpRequest
's user
property
The HttpRequest
's user
property has a type of Union[AbstractBaseUser, AnonymousUser]
,
but for most of your views you'll probably want either an authed user or an
AnonymousUser
.
So we can define a subclass for each case:
class AuthedHttpRequest(HttpRequest):
user: User # type: ignore [assignment]
And then you can use it in your views:
@auth.login_required
def activity(request: AuthedHttpRequest, team_id: str) -> HttpResponse:
...
You can also get more strict with your login_required
decorator so that the
first argument of the fuction it is decorating is AuthedHttpRequest
:
from typing import Any, Union, TypeVar, cast
from django.http import HttpRequest, HttpResponse
from typing_extensions import Protocol
from functools import wraps
class RequestHandler1(Protocol):
def __call__(self, request: AuthedHttpRequest) -> HttpResponse:
...
class RequestHandler2(Protocol):
def __call__(self, request: AuthedHttpRequest, __arg1: Any) -> HttpResponse:
...
RequestHandler = Union[RequestHandler1, RequestHandler2]
# Verbose bound arg due to limitations of Python typing.
# see: https://github.com/python/mypy/issues/5876
_F = TypeVar("_F", bound=RequestHandler)
def login_required(view_func: _F) -> _F:
@wraps(view_func)
def wrapped_view(
request: AuthedHttpRequest, *args: object, **kwargs: object
) -> HttpResponse:
if request.user.is_authenticated:
return view_func(request, *args, **kwargs) # type: ignore [call-arg]
raise AuthenticationRequired
return cast(_F, wrapped_view)
Then the following will type error:
@auth.login_required
def activity(request: HttpRequest, team_id: str) -> HttpResponse:
...
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Hashes for django_types-0.4.0-py3-none-any.whl
Algorithm | Hash digest | |
---|---|---|
SHA256 | 57b5f28c61a08aff4156db5d4fc21a11033a5d515873e581c954bb8683a95e71 |
|
MD5 | 33825ee0b08b3499559fde48decb101f |
|
BLAKE2b-256 | a0e9b80ba9fe59282c9772cb041bcb0aa0cc46b96b4efea4780cf8095c1913f1 |