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

14 KiB

name description argument-hint allowed-tools
django-6-upgrade ⚠️ 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.
--check-only | --full-upgrade
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 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

# 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

# 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:

# 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

# 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

# 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

# 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

# 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

# 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

# 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

# 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

# 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)

# 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
            ],
        },
    },
]
<!-- Template usage with nonce -->
<script nonce="{{ csp_nonce }}">
    // Inline script with CSP nonce
</script>

2. Template Partials

<!-- 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

# 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')
# 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

# 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

# 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

# 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

# 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:

# 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