Skip to main content

Type stubs for Django

Project description

django-types PyPI

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 that mypy can't crash due to Django config, and that non-mypy type checkers like pyright 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

# NOTE: there are probably other items you'll need to monkey patch depending on
# your version.
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

AutoField

By default Django will create an AutoField for you if one doesn't exist.

For type checkers to know about the id field you'll need to declare the field explicitly.

# before
class Post(models.Model):
    ...

# after
class Post(models.Model):
    id = models.AutoField(primary_key=True)

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:
    ...

related

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

django-types-0.5.0.tar.gz (184.3 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

django_types-0.5.0-py3-none-any.whl (970.8 kB view details)

Uploaded Python 3

File details

Details for the file django-types-0.5.0.tar.gz.

File metadata

  • Download URL: django-types-0.5.0.tar.gz
  • Upload date:
  • Size: 184.3 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: poetry/0.12.11 CPython/3.7.0 Darwin/19.6.0

File hashes

Hashes for django-types-0.5.0.tar.gz
Algorithm Hash digest
SHA256 21909e8d036c60fe5c0c69f164d648a3326679041a934e36032876e74c18d6c3
MD5 9eb5d6b65aec28eb266a3c7f15d0efce
BLAKE2b-256 5741deec3741e2ea0a7c3ed03383aea8dee50aa7d78b9264fcd60ee93331bd92

See more details on using hashes here.

File details

Details for the file django_types-0.5.0-py3-none-any.whl.

File metadata

  • Download URL: django_types-0.5.0-py3-none-any.whl
  • Upload date:
  • Size: 970.8 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: poetry/0.12.11 CPython/3.7.0 Darwin/19.6.0

File hashes

Hashes for django_types-0.5.0-py3-none-any.whl
Algorithm Hash digest
SHA256 3d18681600eb069d6950a131800df394d2c39b07c09278a2d6da134f1e547d7a
MD5 d64046c132705c47aae29e72a93dda31
BLAKE2b-256 75abcada5285ca2a6f1b74fb6826dd62a1092994fe4d3a9ea2fa4b319ef64aa8

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page