Django Compatibility¶
Django Safe Migrations supports a wide range of Django and Python versions. This page documents version compatibility, known API differences we handle, and how we test across versions.
Supported Versions¶
Version Matrix¶
| Python | Django 3.2 | Django 4.2 | Django 5.0 | Django 5.1 | Django 6.0 |
|---|---|---|---|---|---|
| 3.9 | Yes | Yes | No | No | No |
| 3.10 | Yes | Yes | Yes | Yes | No |
| 3.11 | Yes | Yes | Yes | Yes | No |
| 3.12 | No | Yes | Yes | Yes | Yes |
| 3.13 | No | Yes | Yes | Yes | Yes |
| 3.14 | No | No | No | No | Yes |
Notes:
- Django 3.2 is the minimum supported version (LTS)
- Django 5.0+ requires Python 3.10+
- Django 6.0+ requires Python 3.12+
- Python 3.9 support will be dropped when Django 3.2 reaches end-of-life
Official Support Policy¶
We follow Django's supported versions policy:
- All current LTS releases (Django 3.2, 4.2)
- All current feature releases (Django 5.x, 6.x)
- We test against the latest patch version of each minor release
Known API Changes¶
Django's internal APIs change between versions. We handle these automatically so your migration checks work regardless of Django version.
CheckConstraint API (Django 5.1+)¶
The Change:
| Django Version | Parameter Name | Status |
|---|---|---|
| < 5.1 | check= |
Required |
| 5.1 | check= |
Deprecated (warning) |
| 5.1 | condition= |
New, preferred |
| 6.0+ | check= |
Removed (planned) |
| 6.0+ | condition= |
Required |
How We Handle It:
Our test suite uses version-aware fixtures to create CheckConstraint instances:
import django
from django.db import models
def create_check_constraint(condition, name):
"""Create a CheckConstraint compatible with any Django version."""
if django.VERSION >= (5, 1):
return models.CheckConstraint(condition=condition, name=name)
else:
return models.CheckConstraint(check=condition, name=name)
Impact on Users:
None. The SM017 rule correctly detects AddConstraint operations with CheckConstraint regardless of which parameter was used.
Index Creation Introspection¶
Django's index introspection changed in Django 4.1:
- Django < 4.1: Limited index metadata
- Django 4.1+: Enhanced
IndexandUniqueConstraintintrospection
Our SM010 and SM011 rules work with both APIs by checking the operation type rather than introspecting database state.
NOT_PROVIDED Sentinel¶
The location of Django's NOT_PROVIDED sentinel varies:
# Django 3.2 - 4.x
from django.db.models.fields import NOT_PROVIDED
# Django 5.x+
from django.db.models import NOT_PROVIDED
We handle this with a version-agnostic import in our SM001 rule.
How We Test¶
Automated CI Matrix¶
Every pull request runs tests against our full version matrix using GitHub Actions:
strategy:
fail-fast: false
matrix:
python-version: ["3.9", "3.10", "3.11", "3.12", "3.13", "3.14"]
django-version: ["3.2", "4.2", "5.0", "5.1", "6.0"]
exclude:
# Django 5.0+ requires Python 3.10+
- python-version: "3.9"
django-version: "5.0"
- python-version: "3.9"
django-version: "5.1"
# Django 6.0 requires Python 3.12+
- python-version: "3.9"
django-version: "6.0"
- python-version: "3.10"
django-version: "6.0"
- python-version: "3.11"
django-version: "6.0"
# Django 3.2 doesn't support Python 3.12+
- python-version: "3.12"
django-version: "3.2"
- python-version: "3.13"
django-version: "3.2"
- python-version: "3.14"
django-version: "3.2"
# Django 4.2 doesn't support Python 3.14
- python-version: "3.14"
django-version: "4.2"
# Django 5.0/5.1 don't support Python 3.14
- python-version: "3.14"
django-version: "5.0"
- python-version: "3.14"
django-version: "5.1"
Docker Multi-Database Testing¶
For database-specific rules (PostgreSQL, MySQL), we use Docker Compose:
This runs tests against:
- PostgreSQL 15
- MySQL 8.0
- SQLite (default)
Local Testing¶
To test against a specific Django version locally:
# Create a virtual environment
python -m venv .venv
source .venv/bin/activate
# Install specific Django version
pip install "Django>=5.1,<5.2"
# Install package in dev mode
pip install -e ".[dev]"
# Run tests
pytest
Writing Compatible Code¶
When contributing to django-safe-migrations, follow these patterns:
Version Checks¶
import django
if django.VERSION >= (5, 1):
# Django 5.1+ code path
pass
else:
# Legacy code path
pass
Import Guards¶
try:
from django.db.models import NOT_PROVIDED
except ImportError:
from django.db.models.fields import NOT_PROVIDED
Type Hints with Compatibility¶
from __future__ import annotations
from typing import TYPE_CHECKING
if TYPE_CHECKING:
from django.db import migrations
Reporting Compatibility Issues¶
If you encounter a compatibility issue:
-
Check the version: Run
python -c "import django; print(django.VERSION)" -
Reproduce with minimal case: Create a small migration that triggers the issue
-
Open an issue: Include:
-
Python version
- Django version
- Full error traceback
-
Migration code (if possible)
-
Link to Django release notes: If the issue relates to a Django API change, include a link to the relevant release notes
Deprecation Policy¶
When Django deprecates an API we use:
- Immediate: We add support for the new API
- Same release: We keep backward compatibility with the old API
- When Django removes it: We remove our backward compatibility code
This ensures you can upgrade Django versions without upgrading django-safe-migrations simultaneously (and vice versa).
Database Compatibility¶
While django-safe-migrations performs static analysis (no database connection required), some rules are database-specific:
| Rule | PostgreSQL | MySQL | SQLite | Other |
|---|---|---|---|---|
| SM005 | Yes | No | No | No |
| SM010 | Yes | No | No | No |
| SM011 | Yes | No | No | No |
| SM012 | Yes | No | No | No |
| SM013 | Yes | No | No | No |
| SM017 | Yes | No | No | No |
| SM018 | Yes | No | No | No |
| SM021 | Yes | No | No | No |
| SM030 | Yes | No | No | No |
| SM031 | Yes | No | No | No |
| SM034 | Yes | No | No | No |
| All others | Yes | Yes | Yes | Yes |
SM031 and SM034 are informational PostgreSQL-only rules; SM034 only fires on Django < 4.0.
Database-specific rules use the db_vendors attribute:
class NonConcurrentIndexRule(BaseRule):
rule_id = "SM010"
db_vendors = ["postgresql"] # Only applies to PostgreSQL
When analyzing migrations, pass the database vendor to enable/disable these rules: