New in 2026: Master Python for AI, Data Science

DjangoProgramming

Django 6 Is Here — What’s New and How to Get Started

Django 6 Is Here — What's New and How to Get Started

You have been putting off the Django upgrade for six months. Your project runs on Django 4.2, the security advisories are piling up, and every time you check the release calendar, another CVE lands. February 2026 made the stakes clear: three high-severity SQL injection vulnerabilities — CVE-2026-1287, CVE-2026-1207, and a third — were patched across Django 6.0, 5.2, and 4.2 in a single security release. If you were on 4.2.27 or lower, you were exposed. Django 6.0 is now the only supported branch, and it ships with features that actually change how you build. Here is what changed and how to get on it.

In this tutorial, you will learn to:

  • Understand the five headline features introduced in Django 6.0
  • Apply the February 2026 security patches and assess your exposure
  • Set up a new Django 6.0 project from scratch
  • Upgrade an existing Django 4.x/5.x project with a proven checklist
  • Identify what breaks and how to fix it using django-upgrade

Prerequisites

This article assumes you have Python 3.10+ installed and are familiar with Django’s request-response cycle, ORM, and class-based views. If you are brand new to Django, start with the Python functions guide first. Django 6.0 requires Python 3.10 or later — Python 3.9 reached end-of-life and is not supported.

Django 6.0 by the Numbers

ItemDetail
Release dateDecember 3, 2025
Python requirement3.10+
Security patches (Feb 2026)6 CVEs — 3 high-severity SQL injection (CVE-2026-1287, CVE-2026-1207, +1 more)
Patched versions4.2.28, 5.2.11, 6.0.2
Next LTSDjango 6.x (future, not yet announced)
4.2 EOLApril 30, 2026

February 2026: The Security Wake-Up Call

On February 3, 2026, the Django security team issued security releases 6.0.2, 5.2.11, and 4.2.28 addressing six CVEs. Three were high-severity SQL injection flaws. CVE-2026-1287 allowed remote code execution via crafted column aliases in FilteredRelation. CVE-2026-1207 targeted PostGIS RasterField lookups through band index parameters. A third SQL injection in template tag parsing was also patched. All Django 4.2, 5.2, and 6.0 versions below those patch levels were vulnerable. If you have not updated since January 2026, treat this as an emergency — upgrade immediately.

Action: Run pip show django in every Django project. If your version is below 4.2.28, 5.2.11, or 6.0.2, patch now before anything else.

What’s New in Django 6.0

1. Built-In Background Tasks Framework

Django 6.0 introduces a native Tasks framework for running code outside the HTTP request-response cycle. Previously you needed Celery, Dramatiq, or Huey for this. Now Django ships with a standardized task API. Tasks persist in the database by default and survive server restarts. This is the feature that makes Django 6.0 worth upgrading on its own:

# tasks.py
from django.core.tasks import app

@app.task
def send_welcome_email(user_id: int) -> None:
    user = User.objects.get(id=user_id)
    send_email(user.email, subject="Welcome!", body=f"Hi {user.first_name}...")

Trigger it from a view or signal using .enqueue():

# Inside a view
from .tasks import send_welcome_email

def register(request):
    user = User.objects.create_user(...)
    send_welcome_email.enqueue(user.id)  # fires immediately, runs async
    return Response({"status": "ok"})

Note: The Django Tasks framework does NOT use Celery’s .delay() method. Use task.enqueue(args) instead. The framework intentionally does not ship with a worker — you bring your own backend (database, Redis, RabbitMQ, SQS) for production use.

The task queue uses the Django database as its broker by default — no Redis required for simple workloads. For production scaling, you can configure a different backend later.

2. Template Partials

The Django Template Language now supports named fragments called template partials. This is a direct answer to HTMX and React component patterns — you can define a fragment in one template file and reuse it without a custom inclusion tag or a full template include:

{% partialdef form_field %}
  
{{ field }} {% if field.errors %} {{ field.errors.0 }} {% endif %}
{% endpartialdef %}

Render it with {% partial 'form_field' %}. Pass context via {% partial 'form_field' with field=my_field %}. No Python code required per fragment.

Note: The {% partial %}...{% endpartial %} syntax (without the def) comes from the third-party django-template-partials package. Django 6.0 core uses {% partialdef %} to define and {% partial 'name' %} to render. Do not mix the two approaches.

3. Native Content Security Policy (CSP) Middleware

Django 6.0 ships with built-in CSP middleware. Previously you needed django-csp or security-middleware. Now you get nonce-based CSP in settings.py:

MIDDLEWARE = [
    "django.middleware.security.SecurityMiddleware",
    "django.middleware.content_security.ContentSecurityPolicyMiddleware",  # new
    # ... your other middleware
]

# Content Security Policy
CSP_DEFAULT_SRC = ("'self'",)
CSP_SCRIPT_SRC = ("'self'", "'nonce-{{ nonce}}'")
CSP_STYLE_SRC = ("'self'", "'nonce-{{ nonce}}'")
CSP_IMG_SRC = ("'self'", "https:")

Nonce values are generated per request and injected automatically. No third-party package needed.

4. Async Pagination: AsyncPaginator and AsyncPage

Django 6.0 adds AsyncPaginator and AsyncPage for use in async views. Previously, using Django’s pagination in an async view required wrapping it with sync_to_async. Now you can paginate database queries natively in async views:

from django.core.paginator import AsyncPaginator
from myapp.models import Article

async def list_articles(request):
    queryset = Article.objects.filter(published=True).order_by("-published_at")
    paginator = AsyncPaginator(queryset, per_page=20)
    page_obj = await paginator.get_page(request.GET.get("page"))
    return Response({"articles": page_obj.object_list, "page": page_obj.number})

5. Expanded Database Function Support

Django 6.0 extends its cross-backend database function support. Functions like Coalesce, Trunc, and JSONObject now work consistently across PostgreSQL, MySQL, SQLite, and Oracle without backend-specific workarounds. This is especially relevant if you switch database backends between development and production.

Setting Up a New Django 6.0 Project

Create a fresh Django 6.0 virtual environment and project in under five minutes:

# Create and activate virtual environment
python3 -m venv dj6env
source dj6env/bin/activate

# Install Django 6 (installs latest 6.x)
pip install django>=6.0

# Verify version
python -c "import django; print(django.VERSION)"
# Start a new project
django-admin startproject myproject
cd myproject

# Start a new app
python manage.py startapp articles

# Add to INSTALLED_APPS in myproject/settings.py
INSTALLED_APPS = [
    "django.contrib.admin",
    "django.contrib.auth",
    "django.contrib.contenttypes",
    "django.contrib.sessions",
    "django.contrib.messages",
    "django.contrib.staticfiles",
    "articles",  # ← add this
    # ...
]

# Run the dev server
python manage.py runserver

Navigate to http://127.0.0.1:8000 — you have a running Django 6 project. The default admin is at /admin.

Upgrading from Django 4.x or 5.x

What Breaks in Django 6.0

Django 6.0 removes several long-deprecated APIs. Code that worked in Django 4.2 or 5.x will raise errors immediately. Here are the changes that affect most projects:

Removed FeatureReplacement
django.utils.translation.ugettext()django.utils.translation.gettext()
django.utils.translation.ungettext()django.utils.translation.ngettext()
django.test.SimpleTestCase.assertRegex()assertRegexpMatches()
django.utils.html.strip_tags() escaping behaviorVerify output in tests
Passing expires as raw string to Cookie headerUse http.cookies.SimpleCookie directly

Beyond these, the deprecation warnings you ignored in Django 5.x are now hard errors. Run your test suite before upgrading — any deprecation warning in Django 5.x will break in Django 6.0.

Automated Upgrade with django-upgrade

The django-upgrade tool from the Django team handles most of the mechanical fixes automatically. It rewrites old Django imports, method calls, and settings to their Django 6.0 equivalents:

# Install django-upgrade
pip install django-upgrade

# Run against your project (fixes 5.x → 6.0)
django-upgrade --target-version 6.0 .

# Preview changes without applying
django-upgrade --target-version 6.0 --diff .

After running django-upgrade, review the diff, run your tests, and address any remaining manual changes.

Step-by-Step Upgrade Checklist

  • Backup your database and run git status — commit or stash before anything else.
  • Update Django in a virtual environment: pip install 'django>=6.0,<6.1'
  • Run django-upgrade: django-upgrade --target-version 6.0 .
  • Run your test suite: python manage.py test — fix every failure before proceeding.
  • Check deprecation warnings: Run the dev server with python -W default manage.py runserver and look for runtime warnings.
  • Apply security patch: Confirm you are on 6.0.2 or later with pip show django.
  • Deploy to staging and smoke-test core user flows (login, forms, admin, API endpoints).
  • Monitor in production — watch logs for RemovedInDjango61Warning patterns indicating future breaking changes.

Common Mistakes / Gotchas

  • Skipping the 4.2 EOL deadline: Django 4.2 reaches end-of-life on April 30, 2026. After that, no further patches — including security patches — will be released. Upgrade now.
  • Forgetting to update third-party packages: Some packages pinned to older Django versions may break. Check pip check after upgrading.
  • Using Celery syntax with Django Tasks: Do not call .delay() on Django tasks — that is the Celery API. Use task.enqueue(args) instead. The Django Tasks framework intentionally mimics Celery but uses its own API.
  • Using the old translation aliases in new code: ugettext and ungettext have been removed since Django 6.0 alpha. Always use gettext and ngettext going forward.

Django 6.0 vs Django 4.2 — Is the Upgrade Worth It?

If you are on Django 4.2, the upgrade is not optional — it is mandatory. Django 4.2 goes end-of-life on April 30, 2026, which means no more security patches. But even if you are on a supported version, Django 6.0 delivers real value:

  • Background tasks without Celery: For most projects, the built-in Tasks framework replaces Celery. Simpler stack, fewer dependencies.
  • Template partials: HTMX and React-style component patterns without a third-party package.
  • CSP middleware without django-csp: One less third-party dependency.
  • Native async pagination: Async views no longer need sync_to_async wrappers for pagination.
  • Cross-backend database functions: Write once, run on PostgreSQL, MySQL, SQLite, or Oracle.

Frequently Asked Questions

Does Django 6.0’s Tasks framework replace Celery entirely?

For simple background jobs — sending emails, processing webhooks, generating reports — yes. Django Tasks uses the database as the default broker, so you do not need Redis or RabbitMQ just to run async tasks. However, if you need distributed task routing, complex scheduling, or rate limiting, Celery remains more powerful. For new projects, try Django Tasks first.

Can I upgrade directly from Django 4.2 to Django 6.0?

Yes. Django supports skipping minor versions. Upgrade from 4.2 directly to 6.0 — you do not need to go through 5.x. Always run django-upgrade --target-version 6.0 . and your test suite before deploying.

Is Django 6.0 an LTS release?

Not yet. Django 6.0 is the current stable release but is not designated as a Long-Term Support version. The Django team has not announced the 6.x LTS yet. Django 5.2 is also not an LTS. Django 4.2 was the most recent LTS and reached EOL on April 30, 2026.

Summary & Next Steps

Django 6.0 is a significant release: a native background task framework eliminates Celery for most projects, template partials bring component patterns to Django templates, and the February 2026 security patches make the upgrade urgent for anyone still on Django 4.2. Upgrade now — the combination of security risk and new features makes staying on 4.2 or 5.x a liability rather than a preference. To go further:

Related posts
ProgrammingPython

Production-Ready MCP Servers — Security, Testing & Deployment

ProgrammingPython

Build Your First MCP Server with Python SDK — Fundamentals

ProgrammingPython

Connect FastAPI to MCP — Two Integration Patterns

ProgrammingPython

Replace pip with uv for Faster Python Development

Leave a Reply