skills/backend/django/6.0/SKILL.md
Version-specific expert for Django 6.0 (current feature release). Covers background tasks framework (django.tasks), built-in CSP middleware, template partials, AsyncPaginator, modern email API, StringAgg cross-DB, GeneratedField auto-refresh, and breaking changes from 5.2. WHEN: "Django 6.0", "Django 6", "django.tasks", "Django background tasks", "Django CSP", "SECURE_CSP", "template partials", "partialdef", "AsyncPaginator", "Django 6 migration", "Django 6 breaking changes", "StringAgg cross-database", "Model.NotUpdated".
npx skillsauth add chrishuffman5/domain-expert backend-django-6-0Install this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
You are a specialist in Django 6.0, the current feature release. Django 6.0 introduces background tasks, built-in CSP support, template partials, and significant async improvements.
For foundational Django knowledge (ORM, views, middleware, admin, auth, DRF, settings), refer to the parent technology agent. This agent covers what is new or changed in 6.0.
| Detail | Value | |---|---| | Released | December 3, 2025 | | EOL | April 30, 2027 | | Python | 3.12, 3.13, 3.14 (dropped 3.10/3.11) | | Status | Current feature release |
Built-in framework for running code outside the request-response cycle. Replaces the need for Celery for simple background jobs.
# myapp/tasks.py
from django.tasks import task
@task
def send_welcome_email(user_id):
from django.contrib.auth.models import User
from django.core.mail import send_mail
user = User.objects.get(pk=user_id)
send_mail(
subject="Welcome!",
message=f"Hello {user.first_name}, welcome to our platform.",
from_email="[email protected]",
recipient_list=[user.email],
)
# With options:
@task(priority=2, queue_name="emails")
def send_bulk_notification(user_ids, message):
pass
# Enqueue from a view:
from myapp.tasks import send_welcome_email
from functools import partial
from django.db import transaction
def register(request):
user = User.objects.create_user(...)
# Ensure DB commit happens before task runs:
with transaction.atomic():
user.save()
transaction.on_commit(
partial(send_welcome_email.enqueue, user_id=user.pk)
)
return redirect("dashboard")
# Async enqueue:
result = await send_welcome_email.aenqueue(user_id=user.pk)
# settings.py -- backend configuration
TASKS = {
"default": {
# Development: runs tasks immediately (synchronously)
"BACKEND": "django.tasks.backends.immediate.ImmediateBackend",
# Testing: stores results without executing
# "BACKEND": "django.tasks.backends.dummy.DummyBackend",
# Production: use a third-party backend
# "BACKEND": "django_tasks_scheduler.backend.RedisBackend",
}
}
Key limitations:
ImmediateBackend, DummyBackend) are for dev/testing onlyBuilt-in CSP middleware and configuration, replacing the need for django-csp:
from django.utils.csp import CSP
MIDDLEWARE = [
"django.middleware.security.SecurityMiddleware",
"django.middleware.csp.ContentSecurityPolicyMiddleware", # NEW
# ...
]
# Enforce mode:
SECURE_CSP = {
"default-src": [CSP.SELF],
"script-src": [CSP.SELF, CSP.NONCE], # Nonce auto-generated per request
"style-src": [CSP.SELF],
"img-src": [CSP.SELF, "https:", "data:"],
"object-src": [CSP.NONE],
}
# Report-only mode (observe without blocking):
SECURE_CSP_REPORT_ONLY = {
"default-src": [CSP.SELF],
"script-src": [CSP.NONCE, CSP.STRICT_DYNAMIC],
"report-uri": ["/csp-violations/"],
}
{% load csp %}
<script nonce="{{ request.csp_nonce }}">
console.log("Inline script allowed via nonce");
</script>
Named reusable fragments within a single template file:
{% load partials %}
{# Define a partial: #}
{% partialdef product_card %}
<div class="product-card">
<h3>{{ product.name }}</h3>
<p>{{ product.price }}</p>
</div>
{% endpartialdef %}
{# Use the partial: #}
{% for product in products %}
{% partial product_card %}
{% endfor %}
# Render a partial from a view (for HTMX / partial page updates):
def product_card_fragment(request, pk):
product = get_object_or_404(Product, pk=pk)
return render(
request,
"product_list.html#product_card", # template#partial syntax
{"product": product},
)
Async equivalent of Paginator:
from django.core.paginator import AsyncPaginator
async def article_list(request):
page_number = request.GET.get("page", 1)
paginator = AsyncPaginator(
Article.objects.filter(status="published").order_by("-created_at"),
per_page=20,
)
page = await paginator.apage(page_number)
return render(request, "articles/list.html", {"page_obj": page})
Email internals migrated from legacy MIME classes to Python's modern email.message.EmailMessage API:
from django.core.mail import EmailMessage
msg = EmailMessage(
subject="Hello",
body="Message body",
from_email="[email protected]",
to=["[email protected]"],
)
raw_message = msg.message()
# isinstance(raw_message, email.message.EmailMessage) # True
Previously PostgreSQL-only, StringAgg now works across all backends:
from django.db.models import StringAgg, Value
result = Author.objects.aggregate(
all_names=StringAgg("last_name", delimiter=Value(", "))
)
# Works on PostgreSQL, MySQL, MariaDB, SQLite, Oracle
Generated fields and expression-assigned fields now auto-refresh after save() using RETURNING SQL (PostgreSQL, SQLite, Oracle). No manual refresh_from_db() needed:
product = Product(price=Decimal("9.99"), quantity=5)
product.save()
print(product.total_value) # Decimal("49.95") -- available immediately
Breaking change: New projects default to BigAutoField (64-bit) instead of AutoField (32-bit).
# If you need the old 32-bit behavior:
DEFAULT_AUTO_FIELD = "django.db.models.AutoField"
# Or per-app:
class MyAppConfig(AppConfig):
default_auto_field = "django.db.models.AutoField"
Only affects newly created projects without explicit DEFAULT_AUTO_FIELD. Existing projects with the setting explicitly configured are unaffected.
# forloop.length in templates:
{% for item in items %}
{{ forloop.counter }} of {{ forloop.length }}
{% endfor %}
# Model.NotUpdated exception for forced updates with no rows affected:
try:
obj.save(force_update=True)
except obj.NotUpdated:
pass # Object no longer exists in database
# AnyValue aggregate (SQLite, MySQL, Oracle, PostgreSQL 16+):
from django.db.models import AnyValue
result = Product.objects.aggregate(sample_name=AnyValue("name"))
settings, connection, timezone, functionstask.aenqueue()RETURNING clause for generated field refresh# 1. Custom as_sql() must return tuple, not list:
def as_sql(self, compiler, connection) -> tuple[str, tuple]:
return "CUSTOM_FUNC(%s)", (self.value,) # tuple, not list
# 2. Model.save() positional arguments removed:
# Before:
instance.save(False, False, None, None)
# After:
instance.save(force_insert=False, force_update=False, using=None, update_fields=None)
From 5.0 deprecations:
DjangoDivFormRenderer / Jinja2DivFormRenderer transitional rendererscx_Oracle database driver (use python-oracledb)ChoicesMeta aliasFORMS_URLFIELD_ASSUME_HTTPS setting (HTTPS is now the default)get_joining_columns() methodsPrefetch.get_current_queryset()From 5.1 deprecations:
Model.save() / Model.asave()CheckConstraint.check keyword argumentFieldCacheMixin.get_cache_name()GeoIP2.coords() / GeoIP2.open()FileSystemStorage.OS_OPEN_FLAGSdjango.contrib.postgres.aggregates.StringAgg (use django.db.models.StringAgg)OrderableAggMixin (use Aggregate.order_by)ADMINS / MANAGERS as list of tuples (use email strings)BadHeaderError, SafeMIMEText, SafeMIMEMultiparturlize / urlizetrunc HTTP default protocol5.2 LTS: Python 3.10 - 3.14
6.0: Python 3.12 - 3.14 <- must be on 3.12+
# Remove cx_Oracle (use python-oracledb)
# Fix as_sql() to return tuple, not list
# Use keyword arguments for Model.save()
# Remove DjangoDivFormRenderer references
# Remove FORMS_URLFIELD_ASSUME_HTTPS setting
| DB | 5.2 Minimum | 6.0 Minimum | |---|---|---| | PostgreSQL | 14 | 14 | | MariaDB | 10.5 | 10.6 | | MySQL | 8.0 | 8.0 | | SQLite | 3.31 | 3.31 |
pip install "django>=6.0,<7.0"
python manage.py migrate --check
python -W error::DeprecationWarning -m pytest
tools
kubectl command-line usage and scripting: kubeconfig and context management, output formats (jsonpath, custom-columns, go-template), all major verbs (get, describe, apply, delete, exec, logs, port-forward, rollout, scale, drain), workload resources, config/storage, networking, RBAC, node management, debugging (CrashLoopBackOff, ImagePullBackOff, OOMKilled), and scripting patterns (dry-run, diff, wait, jq, kustomize). WHEN: "kubectl", "k8s CLI", "kubeconfig", "namespace", "pod", "deployment", "service", "ingress", "configmap", "secret", "rollout", "scale", "drain", "taint", "kustomize". Do NOT use for cluster architecture, sizing, upgrades, or workload design decisions — that's the `kubernetes` skill in the `containers` plugin. This skill is command syntax and scripting kubectl against an existing cluster, not cluster ops.
tools
Bash 5.x shell scripting, Unix text processing, and command-line automation: variables, parameter expansion, quoting, control flow, functions, I/O redirection, error handling (set -euo pipefail, trap), and the Unix tool ecosystem (grep, sed, awk, jq, find, sort, uniq, cut, xargs). Covers process management, networking (curl, ssh, rsync, nc), file locking (flock), parallel execution, and production script patterns. WHEN: "Bash", "bash", "shell", "sh", ".sh", "shell script", "sed", "awk", "grep", "jq", "find", "xargs", "curl", "ssh", "rsync", "cron", "pipe", "redirect", "here-doc", "shebang", "POSIX", "set -euo pipefail", "trap".
tools
Azure CLI (az) command syntax and scripting: authentication (interactive, service principal, managed identity, SSO), output formats and JMESPath queries, resource groups, VMs, storage accounts/blobs, networking (VNets, NSGs, load balancers, DNS), Entra ID, AKS, App Service, Functions, databases (SQL, Cosmos DB, MySQL, PostgreSQL), Key Vault, Monitor/alerting, and infrastructure scripting patterns. WHEN: "az ", "Azure CLI", "az login", "az vm", "az aks", "az storage", "az keyvault", "az monitor", "az ad", "az group", "az network", "az webapp", "az functionapp", "az sql", "az cosmosdb", "JMESPath", "az account". Do NOT use for Azure architecture, landing zones, or multi-subscription strategy — that's the `cloud-platforms` plugin. This skill is about command syntax and scripting the CLI, not deciding what to provision.
tools
AWS CLI v2 command syntax and scripting: authentication (profiles, SSO, assume-role, instance profiles), output formats and JMESPath queries, pagination and waiters, IAM, S3, Lambda, RDS, CloudFormation, ECS, EKS, CloudWatch, SSM, Route 53, STS, and VPC networking. WHEN: "aws ", "AWS CLI", "aws ec2", "aws s3", "aws lambda", "aws iam", "aws cloudformation", "aws ssm", "aws ecs", "aws eks", "aws rds", "aws cloudwatch", "aws route53", "aws sts". Do NOT use for AWS architecture, service selection, multi-account strategy, or FinOps — that's the `cloud-platforms` plugin. This skill is about command syntax and scripting the CLI, not deciding what to provision.