website/docs/django.mdx
{/*
Pyrefly includes experimental support for Django, a popular Python web framework. This feature aims to provide improved static type checking for Django's Object-Relational Mapping (ORM) system, which is used to interact with databases.
Note: This support is experimental and currently covers a subset of Django's ORM features. The implementation is actively evolving based on feedback and development. We currently support Django models, fields, and basic relationships.
We welcome your feedback and suggestions. Please share your thoughts and ideas by opening an issue on GitHub.
Django is a high-level Python web framework. One of its core components is the Object-Relational Mapping (ORM) system, which allows developers to interact with databases using Python classes (models) instead of writing SQL queries directly.
Django models define the structure of your database tables, and Django automatically handles the creation and management of the underlying database schema.
To use Pyrefly with Django, follow these steps:
# Install django-stubs
pip install django-stubs
# Create and activate a virtual environment
python3 -m venv .venv
source .venv/bin/activate
pip install pyrefly
That's it! Pyrefly will automatically recognize Django constructs and provide appropriate type checking.
Pyrefly provides type inference for Django's ORM without requiring any plugins or manual configuration. It:
models.ModelCharField, IntegerField, ForeignKey, etc.)id and pk)ForeignKey, ManyToManyField)The following examples showcase which Django features are currently supported by Pyrefly. This is a subset of Django's full feature set, but covers the most common use cases.
Django automatically adds certain fields to every model, even if you don't define them explicitly.
id FieldBy default, Django automatically adds an id field to serve as the primary key (unless you define a custom primary key):
from django.db import models
class Reporter(models.Model):
full_name = models.CharField(max_length=70)
# Django auto-adds: id = models.AutoField(primary_key=True)
reporter = Reporter()
reveal_type(reporter.id) # Pyrefly infers: int
If you define a field with primary_key=True, Django will not add the id field. Pyrefly correctly infers the type of custom primary keys:
from django.db import models
class Reporter(models.Model):
uuid = models.UUIDField(primary_key=True)
full_name = models.CharField(max_length=70)
reporter = Reporter()
reveal_type(reporter.uuid) # Pyrefly infers: UUID
reveal_type(reporter.pk) # Pyrefly infers: UUID (pk aliases the custom primary key)
_id Suffix FieldsFor every ForeignKey field named X, Django automatically creates a field named X_id that stores the ID of the related object:
class Article(models.Model):
reporter = models.ForeignKey(Reporter, on_delete=models.CASCADE)
# Django auto-adds: reporter_id: int
article = Article()
reveal_type(article.reporter_id) # Pyrefly infers: int
A ForeignKey creates a many-to-one relationship where each instance of one model relates to an instance of another model.
from django.db import models
class Reporter(models.Model):
full_name = models.CharField(max_length=70)
class Article(models.Model):
reporter = models.ForeignKey(Reporter, on_delete=models.CASCADE)
Accessing a ForeignKey field returns an instance of the related model:
article = Article()
reveal_type(article.reporter) # Pyrefly infers: Reporter
You can access fields on the related model:
reveal_type(article.reporter.full_name) # Pyrefly infers: str
If a ForeignKey has null=True, Pyrefly reflects this in the inferred type:
class Article(models.Model):
reporter = models.ForeignKey(Reporter, null=True, on_delete=models.CASCADE)
article = Article()
reveal_type(article.reporter) # Pyrefly infers: Reporter | None
A ManyToManyField creates a many-to-many relationship where instances of one model can be related to multiple instances of another model.
class Author(models.Model):
name = models.CharField(max_length=100)
class Book(models.Model):
title = models.CharField(max_length=200)
authors = models.ManyToManyField(Author, related_name='books')
Accessing a ManyToManyField returns a manager object that provides methods to interact with the related objects:
book = Book()
reveal_type(book.authors) # Pyrefly infers: ManyRelatedManager[Author, Model]
reveal_type(book.authors.all()) # Pyrefly infers: QuerySet[Author, Author]
The manager provides methods like .add(), .remove(), .clear(), and .all() to manage the relationship.
Pyrefly supports Django's model choices using Choices, IntegerChoices, and TextChoices. These provide type-safe enumerations for model fields.
from django.db import models
class Vehicle(models.IntegerChoices):
CAR = 1, "Car"
TRUCK = 2, "Truck"
MOTORCYCLE = 3, "Motorcycle"
class Product(models.Model):
vehicle_type = models.IntegerField(choices=Vehicle.choices)
# Pyrefly correctly infers enum types
reveal_type(Vehicle.CAR.value) # Pyrefly infers: int
reveal_type(Vehicle.CAR.label) # Pyrefly infers: str
reveal_type(Vehicle.values) # Pyrefly infers: list[int]
reveal_type(Vehicle.choices) # Pyrefly infers: list[tuple[int, str]]
Pyrefly also supports TextChoices and the base Choices class with various value types including enum.auto() for automatic value generation.
Mypy uses a plugin (mypy-django-plugin) that provides very detailed type information by accessing runtime Django internals and performing multiple passes over the code. Pyrefly takes a different approach by following the type stubs directly without runtime introspection.
In some cases, such as ManyToManyField relationships, Mypy and Pyrefly infer different types:
Example:
from django.db import models
class Author(models.Model):
name = models.CharField(max_length=100)
class Book(models.Model):
title = models.CharField(max_length=200)
authors = models.ManyToManyField(Author, related_name="books")
class Article(models.Model):
headline = models.CharField(max_length=200)
authors = models.ManyToManyField(Author, related_name="articles")
# What types do the managers have?
book = Book()
article = Article()
Mypy (with django plugin):
book.authors has type: Author_ManyRelatedManager[Book_authors]article.authors has type: Author_ManyRelatedManager[Article_authors]Pyrefly (following stubs):
book.authors has type: ManyRelatedManager[Author, Model]article.authors has type: ManyRelatedManager[Author, Model]These are some of the Django features are not yet supported in this experimental release:
Django automatically creates reverse relationships for ForeignKey and ManyToManyField. For example:
class Reporter(models.Model):
full_name = models.CharField(max_length=70)
class Article(models.Model):
reporter = models.ForeignKey(Reporter, on_delete=models.CASCADE)
# Not yet supported:
reporter = Reporter()
reveal_type(reporter.article_set) # Expected: RelatedManager[Article]
While basic .all() operations are supported, more complex QuerySet operations may not have complete type inference.
Those are not the only unsupported features, so if there are specific features you would like to see, please request them by opening a github issue and adding the Django label to it.