Skip to main content

Type subs 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 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:

import types
from django.db.models.manager import BaseManager
from django.db.models.query import QuerySet

def no_op(self, x):
    return self

QuerySet.__class_getitem__ = types.MethodType(no_op, QuerySet)
BaseManager.__class_getitem__ = types.MethodType(no_op, BaseManager)

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]'

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


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.1.0.tar.gz (181.4 kB view hashes)

Uploaded Source

Built Distribution

django_types-0.1.0-py3-none-any.whl (913.1 kB view hashes)

Uploaded Python 3

Supported by

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