Django: What's new in 6.0 – Adam Johnson
Source: Hacker News

Django 6.0 was released today, starting another release cycle for the long‑lived Python web framework (now 20 years old!). It comes with a mosaic of new features, contributed to by many, some of which I helped with. Below is my pick of highlights from the release notes.
Upgrade with help from django-upgrade
If you’re upgrading a project from Django 5.2 or earlier, try my tool django-upgrade (docs). It automatically updates old Django code to use new features and fixes deprecation warnings, including five fixers for Django 6.0. (One day I’ll propose django-upgrade to become an official Django project, when energy and time permit…)
Template partials
There are four headline features in Django 6.0; we’ll start with this one:
The Django Template Language now supports template partials, making it easier to encapsulate and reuse small named fragments within a template file.
Partials are sections of a template marked by the new {% partialdef %} and {% endpartialdef %} tags. They can be reused within the same template or rendered in isolation.
Reuse partials within the same template
The template below defines a filter_controls partial and reuses it twice:
{% partialdef filter_controls %}
{{ filter_form }}
{% endpartialdef %}
{% partial filter_controls %}
{% for video in videos %}
## {{ video.title }}
…
{% endfor %}
{% partial filter_controls %}
You can simplify this pattern with the inline option, which renders the definition in place:
{% partialdef filter_controls inline %}
{{ filter_form }}
{% endpartialdef %}
{% for video in videos %}
## {{ video.title }}
…
{% endfor %}
{% partial filter_controls %}
Use this pattern whenever you find yourself repeating template code within the same file. Because partials can use variables, they also help de‑duplicate similar controls with different data.
Render partials in isolation
The following template defines a view_count partial intended to be re‑rendered on its own. It uses the inline option so the partial is included when the full template is rendered. The page uses htmx (via my django‑htmx package) to refresh the view count periodically.
{% load django_htmx %}
## {{ video.title }}
Your browser does not support the video tag.
{% partialdef view_count inline %}
{{ video.view_count }} views
{% endpartialdef %}
{% htmx_script %}
Corresponding view code:
from django.shortcuts import render
def video(request, video_id):
# …
return render(request, "video.html", {"video": video})
def video_view_count(request, video_id):
# …
return render(request, "video.html#view_count", {"video": video})
The initial video view renders the full template, while video_view_count renders only the view_count fragment by appending #view_count to the template name—similar to referencing an HTML fragment by ID in a URL.
History
The feature was motivated by htmx, as described by its creator Carson Gross in a cross‑framework review post. Django’s support for template partials was initially developed by Carlton Gibson in the django‑template‑partials package, which remains available for older Django versions. Integration into Django itself was done during a Google Summer of Code project this year, worked on by student Farhan Ali and mentored by Carlton (see Ticket #36410). Read more in Farhan’s retrospective blog post. Thanks to Farhan, Carlton, Natalia Bidart, Nick Pope, and Sarah Boyce for their contributions.
Tasks framework
Django now includes a built‑in Tasks framework for running code outside the HTTP request–response cycle. This enables offloading work, such as sending emails or processing data, to background workers.
Background tasks are a common requirement (e.g., sending emails, processing images, generating reports). Historically Django relied on third‑party solutions like Celery or Django Q2, which can be complex to set up.
The new Tasks framework provides a standard API for defining background tasks that third‑party task runners can execute, giving the ecosystem a common ground.
Define tasks with the new @task decorator:
from django.tasks import task
@task
def resize_video(video_id):
...
Enqueue them for background execution with Task.enqueue():
from example.tasks import resize_video
def upload_video(request):
# …
resize_video.enqueue(video.id)
# …
Execute tasks
At present Django does not include a production‑ready task backend. Two backends are provided for development and testing:
- ImmediateBackend – runs tasks synchronously, blocking until they complete.
(docs) - DummyBackend – does nothing when tasks are enqueued but allows inspection later. Useful for testing.
(docs)
Future releases may add official backends or integrate with popular third‑party runners.