2026-02-04 15:29:11 +01:00

514 lines
14 KiB
Markdown

---
name: django-6-upgrade
description: "⚠️ DRAFT/SPEKULATIV - Django 6.0 ist noch nicht released! Diese Dokumentation basiert auf erwarteten Features. Für aktuelle Upgrades (4.2 → 5.2) bitte offizielle Django Docs verwenden."
argument-hint: [--check-only | --full-upgrade]
allowed-tools: Read, Write, Edit, Glob, Grep, Bash, WebFetch
---
> **⚠️ DRAFT - NICHT PRODUKTIONSREIF**
>
> Django 6.0 ist noch nicht released (Stand: Februar 2026).
> Diese Dokumentation basiert auf Spekulationen und erwarteten Features.
> Features wie "Background Tasks Framework", "Template Partials", "CSP Middleware" sind NICHT bestätigt.
>
> **Für aktuelle Upgrades bitte offizielle Django Dokumentation verwenden:**
> - Django 4.2 → 5.0: https://docs.djangoproject.com/en/5.0/releases/5.0/
> - Django 5.0 → 5.1: https://docs.djangoproject.com/en/5.1/releases/5.1/
> - Django 5.1 → 5.2: https://docs.djangoproject.com/en/5.2/releases/5.2/
# Django 5.2 → 6.0 Upgrade Guide (DRAFT/SPEKULATIV)
Comprehensive guide for upgrading Django projects from 5.2 LTS to 6.0, covering breaking changes, removed deprecations, and new features like background tasks, template partials, and CSP support.
## When to Use
- Upgrading a Django 5.2 project to Django 6.0
- Checking compatibility before upgrading
- Fixing deprecation warnings from Django 5.x
- Adopting new Django 6.0 features (CSP, template partials, background tasks)
## Prerequisites
- **Python 3.12+** required (Django 6.0 drops Python 3.10/3.11 support)
- Django 5.2 project with passing tests
- All third-party packages compatible with Django 6.0
## Upgrade Checklist
### Phase 1: Pre-Upgrade Preparation
```bash
# 1. Check Python version (must be 3.12+)
python --version
# 2. Run deprecation warnings check
python -Wd manage.py check
python -Wd manage.py test
# 3. Run django-upgrade tool (automatic fixes)
pip install django-upgrade
django-upgrade --target-version 6.0 **/*.py
```
### Phase 2: Breaking Changes
#### 1. Python Version Requirement
```python
# pyproject.toml or setup.py
# BEFORE
python_requires = ">=3.10"
# AFTER
python_requires = ">=3.12"
```
#### 2. DEFAULT_AUTO_FIELD Change
Django 6.0 defaults to `BigAutoField`. If your project already sets this, you can remove it:
```python
# settings.py
# REMOVE this line if it's set to BigAutoField (now the default)
# DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'
# KEEP if using a different field type
DEFAULT_AUTO_FIELD = 'django.db.models.AutoField' # Keep if intentional
```
**WARNING**: Removing `DEFAULT_AUTO_FIELD` when set to `AutoField` will cause migrations!
#### 3. Database Backend Changes
```python
# BEFORE (Django 5.2)
class MyDatabaseOperations(DatabaseOperations):
def return_insert_columns(self, fields):
...
def fetch_returned_insert_rows(self, cursor):
...
def fetch_returned_insert_columns(self, cursor):
...
# AFTER (Django 6.0)
class MyDatabaseOperations(DatabaseOperations):
def returning_columns(self, fields): # Renamed
...
def fetch_returned_rows(self, cursor): # Renamed
...
# fetch_returned_insert_columns is REMOVED
```
#### 4. Email API Changes
```python
# BEFORE (Django 5.2)
from django.core.mail import BadHeaderError, SafeMIMEText, SafeMIMEMultipart
from django.core.mail.message import sanitize_address, forbid_multi_line_headers
try:
send_mail(subject, message, from_email, [to_email])
except BadHeaderError:
pass
# AFTER (Django 6.0)
# BadHeaderError → ValueError
# SafeMIMEText/SafeMIMEMultipart → Use Python's email.mime classes directly
# sanitize_address/forbid_multi_line_headers → Removed
try:
send_mail(subject, message, from_email, [to_email])
except ValueError: # Replaces BadHeaderError
pass
```
#### 5. ADMINS/MANAGERS Settings
```python
# BEFORE (Django 5.2) - Deprecated tuple format
ADMINS = [
('Admin Name', 'admin@example.com'),
('Another Admin', 'another@example.com'),
]
# AFTER (Django 6.0) - Email strings only
ADMINS = [
'admin@example.com',
'another@example.com',
]
# Same for MANAGERS
MANAGERS = [
'manager@example.com',
]
```
#### 6. BaseConstraint Positional Arguments
```python
# BEFORE (Django 5.2)
class MyConstraint(BaseConstraint):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
# AFTER (Django 6.0) - Positional args removed
class MyConstraint(BaseConstraint):
def __init__(self, *, name, violation_error_code=None, violation_error_message=None):
super().__init__(
name=name,
violation_error_code=violation_error_code,
violation_error_message=violation_error_message,
)
```
#### 7. ModelAdmin.lookup_allowed() Signature
```python
# BEFORE (Django 5.2)
class MyModelAdmin(admin.ModelAdmin):
def lookup_allowed(self, lookup, value):
return super().lookup_allowed(lookup, value)
# AFTER (Django 6.0) - request is required
class MyModelAdmin(admin.ModelAdmin):
def lookup_allowed(self, lookup, value, request): # request added
return super().lookup_allowed(lookup, value, request)
```
#### 8. Prefetch QuerySet Method
```python
# BEFORE (Django 5.2)
class MyManager(Manager):
def get_prefetch_queryset(self, instances, queryset=None):
...
# AFTER (Django 6.0)
class MyManager(Manager):
def get_prefetch_querysets(self, instances, querysets=None): # Plural
...
```
#### 9. Form Renderer Changes
```python
# BEFORE (Django 5.2) - Transitional renderers
from django.forms.renderers import DjangoDivFormRenderer, Jinja2DivFormRenderer
# AFTER (Django 6.0) - Removed, use standard renderers
from django.forms.renderers import DjangoTemplates, Jinja2
# Or the new default which uses div-based rendering
```
#### 10. StringAgg Import Location
```python
# BEFORE (Django 5.2) - PostgreSQL only
from django.contrib.postgres.aggregates import StringAgg
# AFTER (Django 6.0) - Available for all databases
from django.db.models import StringAgg
# Note: Delimiter must be wrapped in Value() for string literals
from django.db.models import Value
result = MyModel.objects.aggregate(
names=StringAgg('name', delimiter=Value(', '))
)
```
### Phase 3: New Features to Adopt
#### 1. Content Security Policy (CSP)
```python
# settings.py
MIDDLEWARE = [
...
'django.middleware.security.ContentSecurityPolicyMiddleware', # Add
...
]
# CSP Configuration
SECURE_CSP = {
'default-src': ["'self'"],
'script-src': ["'self'", "'nonce'"], # 'nonce' enables nonce support
'style-src': ["'self'", "'unsafe-inline'"],
'img-src': ["'self'", 'data:', 'https:'],
'font-src': ["'self'"],
'connect-src': ["'self'"],
'frame-ancestors': ["'none'"],
}
# Report-only mode for testing
SECURE_CSP_REPORT_ONLY = {
'default-src': ["'self'"],
'report-uri': '/csp-report/',
}
# templates/base.html
TEMPLATES = [
{
...
'OPTIONS': {
'context_processors': [
...
'django.template.context_processors.csp', # Add for nonce support
],
},
},
]
```
```html
<!-- Template usage with nonce -->
<script nonce="{{ csp_nonce }}">
// Inline script with CSP nonce
</script>
```
#### 2. Template Partials
```html
<!-- templates/components.html -->
<!-- Define a partial -->
{% partialdef card %}
<div class="card">
<h3>{{ title }}</h3>
<p>{{ content }}</p>
</div>
{% endpartialdef %}
<!-- Define another partial -->
{% partialdef button %}
<button class="btn btn-{{ variant|default:'primary' }}">
{{ text }}
</button>
{% endpartialdef %}
<!-- templates/page.html -->
{% extends "base.html" %}
{% load partials %}
{% block content %}
<!-- Render partials -->
{% partial "components.html#card" title="Hello" content="World" %}
{% partial "components.html#button" text="Click me" variant="success" %}
<!-- Inline partial definition and use -->
{% partialdef alert %}
<div class="alert alert-{{ level }}">{{ message }}</div>
{% endpartialdef %}
{% partial alert level="warning" message="This is a warning" %}
{% endblock %}
```
#### 3. Background Tasks Framework
```python
# myapp/tasks.py
from django.tasks import task, TaskResult
@task
def send_welcome_email(user_id: int) -> TaskResult:
"""Send welcome email to user."""
from django.contrib.auth import get_user_model
from django.core.mail import send_mail
User = get_user_model()
user = User.objects.get(pk=user_id)
send_mail(
subject='Welcome!',
message=f'Welcome to our site, {user.username}!',
from_email='noreply@example.com',
recipient_list=[user.email],
)
return TaskResult(success=True, result={'user_id': user_id})
@task(priority=10, queue='high-priority')
def process_order(order_id: int) -> TaskResult:
"""Process an order in the background."""
from myapp.models import Order
order = Order.objects.get(pk=order_id)
order.process()
return TaskResult(success=True, result={'order_id': order_id})
# views.py - Enqueue tasks
from myapp.tasks import send_welcome_email, process_order
def register_user(request):
user = User.objects.create_user(...)
# Enqueue background task
send_welcome_email.enqueue(user.pk)
return redirect('home')
def checkout(request):
order = Order.objects.create(...)
# Enqueue with options
process_order.enqueue(
order.pk,
delay=60, # Delay execution by 60 seconds
)
return redirect('order-confirmation')
```
```python
# settings.py - Task backend configuration
TASKS = {
'BACKEND': 'django.tasks.backends.database.DatabaseBackend',
# Or for development:
# 'BACKEND': 'django.tasks.backends.immediate.ImmediateBackend',
}
# For production, you'll need a task runner (not included in Django)
# See django-tasks-scheduler or implement your own worker
```
#### 4. Async Pagination
```python
# views.py
from django.core.paginator import AsyncPaginator
async def async_list_view(request):
queryset = MyModel.objects.all()
paginator = AsyncPaginator(queryset, per_page=25)
page_number = request.GET.get('page', 1)
page = await paginator.aget_page(page_number)
return render(request, 'list.html', {'page': page})
```
### Phase 4: Automated Fixes with django-upgrade
```bash
# Install django-upgrade
pip install django-upgrade
# Run on entire project
django-upgrade --target-version 6.0 $(find . -name "*.py" -not -path "./.venv/*")
# Or with pre-commit
# .pre-commit-config.yaml
repos:
- repo: https://github.com/adamchainz/django-upgrade
rev: "1.21.0"
hooks:
- id: django-upgrade
args: [--target-version, "6.0"]
```
**django-upgrade 6.0 Fixers:**
1. `mail_api_kwargs` - Rewrites positional arguments to keyword arguments for mail APIs
2. `default_auto_field` - Removes redundant BigAutoField settings
3. `stringagg` - Moves StringAgg imports and wraps delimiter in Value()
4. `settings_admins_managers` - Converts ADMINS/MANAGERS to string format
### Phase 5: Testing & Verification
```bash
# 1. Run full test suite with deprecation warnings
python -Wd manage.py test
# 2. Check for system issues
python manage.py check --deploy
# 3. Verify migrations
python manage.py makemigrations --check --dry-run
# 4. Test CSP in browser
# Check browser console for CSP violations
# Use Report-Only mode first
# 5. Verify background tasks
python manage.py shell
>>> from myapp.tasks import send_welcome_email
>>> result = send_welcome_email.enqueue(1)
>>> print(result.status)
```
## Search Patterns for Common Issues
```python
# Find BadHeaderError usage
# grep -r "BadHeaderError" --include="*.py"
# Find SafeMIMEText/SafeMIMEMultipart
# grep -r "SafeMIME" --include="*.py"
# Find ADMINS/MANAGERS tuples
# grep -r "ADMINS\s*=\s*\[" --include="*.py" -A 3
# Find get_prefetch_queryset
# grep -r "get_prefetch_queryset" --include="*.py"
# Find lookup_allowed without request
# grep -r "def lookup_allowed" --include="*.py"
# Find StringAgg from postgres
# grep -r "from django.contrib.postgres.aggregates import.*StringAgg" --include="*.py"
# Find DjangoDivFormRenderer
# grep -r "DjangoDivFormRenderer\|Jinja2DivFormRenderer" --include="*.py"
```
## Third-Party Package Compatibility
Check these common packages for Django 6.0 compatibility:
| Package | Status | Notes |
|---------|--------|-------|
| django-rest-framework | ✅ 3.16+ | Check for DRF-specific changes |
| celery | ✅ 5.5+ | Consider migrating to Django Tasks |
| django-debug-toolbar | ✅ Check version | |
| django-crispy-forms | ✅ 2.x | |
| django-allauth | ✅ Check version | |
| django-filter | ✅ Check version | |
| django-cors-headers | ✅ Check version | |
## Common Pitfalls
- **Python version**: Django 6.0 requires Python 3.12+, no exceptions
- **DEFAULT_AUTO_FIELD migrations**: Removing this setting can trigger migrations
- **Email exceptions**: Replace `BadHeaderError` with `ValueError`
- **ADMINS format**: Must be strings, not tuples
- **Background Tasks**: Django provides task definition, not task execution (no built-in worker)
- **CSP nonce**: Remember to add context processor for nonce support
- **Template partials**: New templatetags need `{% load partials %}`
## Rollback Plan
If issues arise:
```bash
# Pin Django version
pip install "Django>=5.2,<6.0"
# Or in requirements.txt
Django>=5.2,<6.0
```
Django 5.2 LTS is supported until April 2028.
## Sources
- [Django 6.0 Release Notes](https://docs.djangoproject.com/en/6.0/releases/6.0/)
- [Django Deprecation Timeline](https://docs.djangoproject.com/en/dev/internals/deprecation/)
- [django-upgrade Tool](https://github.com/adamchainz/django-upgrade)
- [Django 6.0 Deep Dive - Adam Johnson](https://adamj.eu/tech/2025/12/03/django-whats-new-6.0/)