django-stubs

repository·master·Indexed 23 days ago

https://github.com/typeddjango/django-stubs

Mypy stubs and a custom mypy plugin for the Django framework, providing precise static typing and type inference. Includes the django-stubs-ext package for runtime monkey-patching to support generic Django classes and features like WithAnnotations for annotated QuerySet results.

Tokens
7.3K
Snippets
17
Records
49
Agent score
81%

What's inside django-stubs

  1. How django-stubs-ext monkeypatching works

    master

    The django-stubs-ext package detects your installed Django version at runtime and applies only the necessary patches. The primary patch adds a __class_getitem__ method to generic Django classes to support type hinting syntax. This method is a no-op and is safe to use in production:

    @classmethod
    def __class_getitem__(cls, *args, **kwargs):
        return cls
  2. Install django-stubs-ext

    master

    Install the django-stubs-ext package via pip. This package provides necessary runtime monkey-patching for django-stubs features that cannot be handled by type stubs alone (such as generic Django classes lacking a __class_getitem__ method).

    pip install django-stubs-ext
  3. Type an authenticated `HttpRequest`

    master

    By default, django.http.HttpRequest.user is typed as User | AnonymousUser. If you are in a context (like a view decorated with @login_required) where you know the user is authenticated, you can create a specialized subclass to avoid type errors:

    from django.http import HttpRequest
    from my_user_app.models import MyUser
    
    class AuthenticatedHttpRequest(HttpRequest):
        user: MyUser

    Use AuthenticatedHttpRequest in your type annotations for views where authentication is guaranteed.

    from django.http import HttpRequest
    from my_user_app.models import MyUser
    
    
    class AuthenticatedHttpRequest(HttpRequest):
        user: MyUser
  4. Configure the mypy plugin for django-stubs

    master

    To enable the django-stubs plugin in mypy, you must explicitly list it in your configuration file. You must also specify the django_settings_module or ensure the DJANGO_SETTINGS_MODULE environment variable is set.

    # mypy.ini or setup.cfg
    [mypy]
    plugins =
        mypy_django_plugin.main
    
    [mypy.plugins.django-stubs]
    django_settings_module = "myproject.settings"
  5. Install django-stubs

    master

    Install django-stubs with the compatible-mypy extra to provide type stubs and a custom mypy plugin for the Django framework. This helps resolve type inference issues caused by Django's dynamic Python patterns.

    pip install 'django-stubs[compatible-mypy]'
  6. Configure django-stubs in pyproject.toml

    master

    If using pyproject.toml, add the plugin to the [tool.mypy] section and the settings to the [tool.django-stubs] section.

    [tool.mypy]
    plugins = ["mypy_django_plugin.main"]
    
    [tool.django-stubs]
    django_settings_module = "myproject.settings"
  7. Use django-stubs-ext monkeypatching

    master

    To enable the required runtime extensions for django-stubs, call django_stubs_ext.monkeypatch() in your Django application. This call should be made exactly once, typically in your top-level settings file.

    import django_stubs_ext
    
    django_stubs_ext.monkeypatch()
  8. Handle incompatible return types in custom Managers

    master

    When overriding built-in methods in a custom Manager without specifying generics, you may encounter an error stating the return type is incompatible with the supertype BaseManager (e.g., error: Return type "MyModel" of "create" incompatible with return type "_T" in supertype "BaseManager").

    To fix this, declare your manager with your model as the type variable:

    class MyManager(models.Manager["MyModel"]):
        def create(self, **kwargs) -> "MyModel":
            ...
    from django.db import models
    
    class MyManager(models.Manager["MyModel"]):
        def create(self, **kwargs) -> "MyModel":
            pass
  9. Fix 'TypeError: type object is not subscriptable' for QuerySet and Manager

    master

    Django's QuerySet and Manager classes do not support the __class_getitem__ magic method at runtime, which causes a TypeError when using type annotations like QuerySet[MyModel] or Manager[MyModel].

    You can resolve this using the django-stubs-ext helper package.

    Option 1: Use django-stubs-ext monkeypatching

    1. Install the package:

      pip install django-stubs-ext
    2. Apply the monkeypatch in your top-level settings file:

      import django_stubs_ext
      
      django_stubs_ext.monkeypatch()
    3. Handling django.contrib.auth.forms: Because these forms cannot be imported until Django is initialized, you must perform manual monkeypatching in your first AppConfig.ready() method.

    Note for Django < 5.1: Use extra_classes=[SetPasswordForm, AdminPasswordChangeForm] instead of mixins.

    Option 2: Use string annotations

    Use strings for the type hints to avoid runtime errors:

    query: 'QuerySet[MyModel]'
    pip install django-stubs-ext
  10. Configure `strict_settings` for custom settings loaders

    master

    If you use custom settings management (e.g., django-split-settings or django-configurations), mypy may fail to infer attributes on the django.conf.settings object.

    You can disable strict checking by setting strict_settings = false in your configuration file. This causes mypy to treat unknown settings attributes as Any instead of raising errors.

    Configuration via pyproject.toml:

    [tool.django-stubs]
    strict_settings = false

    Configuration via mypy.ini:

    [mypy.plugins.django-stubs]
    strict_settings = false
  11. How django-stubs provides type safety for Django

    master

    The django-stubs Mypy plugin works by intercepting various Mypy lifecycle events to inject Django-specific type information. It handles several key areas:

    • Models: It adjusts model metaclasses, processes model class definitions, and ensures that __init__ methods are correctly typed based on model fields.
    • QuerySets and Managers: It provides hooks for common methods like .filter(), .get(), .annotate(), .values(), and .prefetch_related() to ensure the returned types reflect the actual fields and annotations being used.
    • Fields and Relations: It refines the types of related managers (e.g., ManyToManyField) and ensures that reverse relations and forward relations are correctly typed.
    • Settings: It allows for type-safe access to Django settings by looking up attributes on the settings module.
    • Forms: It handles nested Meta classes in Django Forms to ensure proper inheritance and typing.

    By using these hooks, the plugin transforms generic Django objects into specific, typed entities that Mypy can validate against your actual database schema and model definitions.