02 Oct 2026
Django community aggregator: Community blog posts
Issue 357: Malcolm Tredinnick Prize Nominations and Django 6.2 Features
News
Nominate Someone for the 2026 Malcolm Tredinnick Memorial Prize
The annual prize honors someone who welcomes newcomers, freely helps others, and grows the community, with a stipend meant to fund travel to a DjangoCon, PyCon, or sprint. Nominate someone by October 15 (Anywhere on Earth).
Python 3.10.22, 3.11.17, 3.12.15, 3.13.16 and 3.14.8 are now available!
Security releases across all five series, with fixes for tarfile extraction filters, zipfile decompression bombs, and SSL hostname validation. Python 3.10.22 is the final 3.10 release, and 3.13.16 is the last full maintenance release of 3.13, so plan your upgrades.
Python Language Summit 2026
Seth Larson's writeups from the first summit held in Europe since 2011, where 47 core developers in Kraków covered free-threading, Rust for CPython, garbage collection, type manipulation, and an AGENTS.md for CPython. The summit will now alternate between PyCon US and EuroPython each year.
Updates to Django
Today, "Updates to Django" is presented by Raffaella from Djangonaut Space! 🚀
Last week we had 6 pull requests merged into Django by 5 different contributors
News in Django 6.2:
- The new
django.utils.asyncio.maybe_aclosingreturns a context manager that callsaclose()on a caller-provided iterator only if it defines one. - Support for GDAL 3.3 and 3.4 and for GEOS 3.10 is removed.
- Most Django-provided middleware now set
async_capable = Falseto avoid repetitive context switching inMiddlewareMixin.__acall__()under ASGI. For atypical use cases, e.g. high in-process concurrency over non-ORM I/O, where no thread is otherwise held, the repetitive context switching may be preferable to pinning a thread per request (seeasync_performance). Such deployments can restore the prior behavior by settingasync_capable = True; seeMiddlewareMixin <upgrading-middleware>.~django.contrib.auth.middleware.RemoteUserMiddlewareis unaffected, because it does not useMiddlewareMixin. Neither is~django.contrib.auth.middleware.LoginRequiredMiddlewarenordjango.contrib.admindocs.middleware.XViewMiddlewareaffected, as they did not implementprocess_request()orprocess_response().
Django Fellow Reports
Django Fellow Report - Jacob
When Paolo wasn't otherwise showing us around Abruzzo last week at Django on the Med 🏖️, I had the pleasure of supporting a number of groups there, spanning:
- frontend topics (subresource integrity and importmaps)
- backend topics (temporal constraints and
django-subatomic) - performance topics (a benchmarking group kicked off, and Carlton Gibson got me to commit to auditing our free-threading readiness)
- documentation topics, and …, and …, and …
Special thanks to Anna and Simon for taking up my suggestion to pair on some delicate issues around NULL handling in the ORM. They each have PRs I'm excited to review, and the three of us now have more context for tackling whatever comes next in this area.
Django Fellow Report - Natalia
I was mostly OoO (out-of-office) this week due to 🌸 Spring break 🌼 in Uruguay. We travelled 🚗 to visit family and had a wonderful time, including multiple rounds of ice cream eating 🍦. I still prioritized attending the Security Team meeting and doing a release notes fix.
Sponsored
Reach 4,300+ Engaged Django Developers
Sponsor this newsletter to reach an active community of Python and Django developers.

Articles
Black Python Devs has a "New" website
Black Python Devs moved its website from Render Engine to Django and opened the repo, trading a project manager that cost about $1,000 a year for automated workflows (elections and award nominations already run there). Members can now create accounts and set notification preferences, and issues and PRs are welcome.
Django, arrosticini, and the Adriatic ... Django on The Med Pescara 2026
Valentino Gagliardi's sprint recap from Pescara, where a couple of coffees on day two turned into a proposal to replace QUnit with vitest for Django's admin and GIS JavaScript tests, with browser mode, coverage reporting, and accessibility-based selectors as a complement to the Playwright end-to-end work.
Start thinking about running for the Django Steering Council
Tim Schilling, speaking for himself rather than the Council, urges people to run in the next Steering Council election, which starts when Django 6.2 ships in April. DEP 19 now values teaching and community organizing alongside code, and his advice is to start writing publicly about your ideas now, since voters pick candidates they already know and trust.
Django: serve a security.txt file
A security.txt at /.well-known/security.txt (RFC 9116) tells researchers where to report vulnerabilities instead of guessing at addresses. Adam Johnson's view serves it as UTF-8 plain text, with tests that validate the required Contact and Expires fields and a system check that warns before Expires lapses without blocking your commands.
Show and hide Wagtail admin fields without writing any JavaScript
Tim Kamanin was about to write a web component to toggle between a page chooser and a URL field, then found Wagtail's built-in w-rules Stimulus controller already does it. Pass the data-w-rules attributes through a panel's attrs argument; hiding fields needs Wagtail 7.2 or newer.
Setting Up DNS for SaaS Emails
Five years of running a SaaS taught Aidas Bendoraitis to send marketing mail from a different subdomain than transactional and direct mail, so newsletter spam complaints don't drag down password resets. The guide walks through MX, SPF, DKIM, and DMARC records for each, with working examples for FastMail, Mailjet, and Brevo.
djust 1.2: More Django-Compatible, Much Faster
djust now runs Django's own template test suite against its Rust engine and passes 98.6% of it. Render time depends only on what a template reads, so the heaviest benchmark template dropped from 215 ms to 4.7 ms (Django takes about 9 ms), and the release adds class-level components and djust init for existing projects.
Sendgrid is bad at spending their marketing money
django-anymail has dropped official SendGrid support after Twilio SendGrid disabled the project's testing account in June 2025, leaving no way to run integration tests or triage SendGrid bugs. Frank Wiles puts the savings at roughly the cost of three LinkedIn ad clicks.
Rebuilding my development setup in 2026
Six weeks of rebuilding a setup so Claude Code keeps running with the laptop lid closed and can be checked from a phone: Ghostty with the herdr multiplexer, chezmoi-managed dotfiles, a Mac mini as the agent machine (with tailnet ACLs keeping it off production), and a fresh Docker Compose stack per git worktree.
Undocumented Django: Generating a SECRET_KEY
Not everything in Django is documented. This article showcases a "hidden" way to generate a new SECRET_KEY and provides general advice around keeping them actually, well, secret.
I don't write codebase documentation anymore
Instead of keeping docs current by hand, a GitHub Actions workflow runs a headless Claude Code session on every push to main and rewrites only the wiki pages that describe the changed files. One project now has a 95-page wiki with 67 Mermaid diagrams, read mostly by coding agents, and the post includes both files (a skill and a workflow) to copy.
Events
Django Day Copenhagen 2026
It is today, October 2nd! A full day of talks for the 6th edition of this event. You can participate online and in person so it's not too late.
My Django on the Med 2026 experience 🏖️
Former Djangonaut mentee Annabelle Wiegart has a lovely write-up of the recently concluded event, highlighting the magic that comes from actually having the right people together in the room to tackle new advances for Django.
Looking back at Django on the Med 🏖️ 2026
Matthias Kestenholz took the train from Zurich to Pescara and restarted his DEP for import maps in Django core, which let ES modules import stable names even as hashed static filenames change on every deploy. Firefox still can't handle multiple import maps, which is why he argues Django should merge them itself.
Podcasts
Real Python Podcast #312: Navigating AI in Open Source: Insights From Wagtail
Wagtail's Thibaud Colas and Meagen Voss explain how the project handles the flood of AI-assisted contributions, how they compare open-weight models and inference providers, and why Wagtail 8.0 aims to be a "CMS with AI, not AI CMS."
Django Chat #206: Django on the Med
Carlton and Will are back for the fall season. This episode discusses the recent Django on the Med event as well as Django news from the summer.
Videos
Django on the Med
The video version of Django Chat's 36-minute recap of the Pescara sprints, with links to everything discussed: DEP 19, DjangoCon Europe 2027 in Innsbruck, django-bgt, django-benchmark, and the open DEP pull requests that came out of the event.
Django Job Board
Proxify AB joins the board with two senior roles, Python backend and React/Node fullstack, alongside Django work at The Developer Society and The Cruise Brothers and machine learning at Provision.
Django Developer at The Developer Society
Senior Backend Developer (Python) at Proxify AB
Senior Fullstack Developer (React.js / Node.js) at Proxify AB
Machine Learning Engineer (Hybrid) at Provision
Django Developer at The Cruise Brothers
Projects
RegioHelden/django-scrubber
django_scrubber is a django app meant to help you anonymize your project's database data. It destructively alters data directly on the DB and therefore should not be used on production.
carltongibson/django-bgt
Django-orchestrated background threads for Python, built on bgt (a great way to reliably run background threads).
Sponsor Django News
Reach 4,300+ Django developers every Friday. See sponsorship details and rates.
02 Oct 2026 3:00pm GMT
Django: serve apple-app-site-association and assetlinks.json
If your site has companion Apple or Android apps, you probably want links to your site to open in those apps, when installed. Both platforms support this, with Apple calling the feature Universal Links and Android calling it App Links.
But an app can't just claim to handle your links, or any malicious app could hijack them. Instead, both platforms require two-way association: the app declares which domains it handles, and each domain confirms which apps may handle its links. The domain's side of that handshake is a JSON file served under the reserved /.well-known/ path, one per platform:
/.well-known/apple-app-site-associationfor iOS, iPadOS, and macOS./.well-known/assetlinks.jsonfor Android.
These files can do more than link handling. Both can also associate apps with your site for password autofill and passkeys, so users can sign in to your apps with credentials saved from your site.
In this post, we'll look at serving these two files from Django, with tests. It follows the same pattern as my recent post on serving a security.txt file, but with JSON and a few gotchas covered below.
Write the Apple file
Here's an example apple-app-site-association file, following Apple's documentation:
{
"applinks": {
"details": [
{
"appIDs": ["ABCDE12345.com.example.app"],
"components": [
{
"/": "/admin/*",
"exclude": true,
"comment": "Keep the admin in the browser"
},
{
"/": "/*"
}
]
}
]
},
"webcredentials": {
"apps": ["ABCDE12345.com.example.app"]
}
}
This declares two services:
applinksfor Universal Links.appIDslists your app IDs, each your Apple developer team ID, a dot, and the app's bundle ID.componentslists URL patterns, checked in order until the first match, soexcludepatterns come first. In the example, the first pattern keeps URLs under/admin/opening in the browser, while the second sends all other URLs to the app.webcredentialsfor password autofill and passkeys.
Replace the example app ID with your own, which your iOS developers can provide.
Apple specifies the filename with no .json extension, but save your copy as apple-app-site-association.json. That extension lets Django's FileResponse detect the right content type, as we'll see below, and makes your editor recognize the file as JSON. The URL will still use Apple's name, without the extension. Put it in one of your Django apps, next to its views.py. Like the security.txt post, I'll use a "core" app within a project package called example, so example/core/apple-app-site-association.json.
Write the Android file
Here's an example assetlinks.json file, following Android's documentation:
[
{
"relation": [
"delegate_permission/common.handle_all_urls",
"delegate_permission/common.get_login_creds"
],
"target": {
"namespace": "android_app",
"package_name": "com.example.app",
"sha256_cert_fingerprints": [
"14:6D:E9:83:C5:73:06:50:D8:EE:B9:95:2F:34:FC:64:16:A0:83:42:E6:1D:BE:A8:8A:04:96:B2:3F:CF:44:E5"
]
}
}
]
This grants the app identified by target two permissions: handle_all_urls for App Links, and get_login_creds for password autofill and passkeys. The sha256_cert_fingerprints identify your app's signing certificates. If you use Play App Signing, copy the fingerprint from the Play Console's app signing page. Replace the example package name and fingerprint with your own, which your Android developers can provide.
Save this file as assetlinks.json in the same Django app, Like example/core/assetlinks.json.
Serve the files
Both platforms have similar serving requirements:
- The file must be served over HTTPS.
- The response must be
200 OK, with no redirects. - The
content-typeheader must beapplication/json. - Each domain must serve its own file. Associating
example.comdoesn't coverwww.example.com, and vice versa.
Here are views that meet these requirements:
from pathlib import Path
from django.contrib.auth.decorators import login_not_required
from django.http import FileResponse, HttpRequest, HttpResponse
from django.views.decorators.cache import cache_control
from django.views.decorators.http import require_safe
APPLE_APP_SITE_ASSOCIATION_PATH = (
Path(__file__).parent / "apple-app-site-association.json"
)
@login_not_required
@require_safe
@cache_control(max_age=60 * 5, public=True) # 5 minutes
def apple_app_site_association(request: HttpRequest) -> HttpResponse:
"""
Serve the apple-app-site-association file, per:
https://adamj.eu/tech/2026/10/01/django-app-links/
"""
return FileResponse(APPLE_APP_SITE_ASSOCIATION_PATH.open("rb"))
ASSETLINKS_JSON_PATH = Path(__file__).parent / "assetlinks.json"
@login_not_required
@require_safe
@cache_control(max_age=60 * 5, public=True) # 5 minutes
def assetlinks_json(request: HttpRequest) -> HttpResponse:
"""
Serve the assetlinks.json file, per:
https://adamj.eu/tech/2026/10/01/django-app-links/
"""
return FileResponse(ASSETLINKS_JSON_PATH.open("rb"))
…with these corresponding entries in your root URLconf:
from django.urls import path
from example.core import views as core_views
urlpatterns = [
# ...
path(
".well-known/apple-app-site-association",
core_views.apple_app_site_association,
),
path(
".well-known/assetlinks.json",
core_views.assetlinks_json,
),
# ...
]
Notes:
@login_not_requiredmarks the views as public, for projects using Django'sLoginRequiredMiddleware. I recommend you use this feature from Django 5.1 to secure your site by default!@require_saferestricts the views to GET and HEAD requests.@cache_controlsets acache-controlheader allowing clients and intermediate caches, such as a content delivery network (CDN), to cache the files for five minutes. That saves repeat requests from the various verifiers, while still letting changes roll out quickly.FileResponsestreams each file as the response body, byte-for-byte. It also sets thecontent-typeheader based on the file extension, so both files getapplication/json.
Sprinkle on some friendly tests
Tests are your friends, and these ones are extra friendly, since mistakes in these files fail silently. A broken file means links open in the browser, with no error message anywhere. Here are tests covering both views, which you could put in your Django app's tests.py:
import json
from http import HTTPStatus
from django.test import SimpleTestCase
class AppleAppSiteAssociationTests(SimpleTestCase):
"""
Test the apple-app-site-association file, per:
https://adamj.eu/tech/2026/10/01/django-app-links/
"""
url = "/.well-known/apple-app-site-association"
def test_success(self):
response = self.client.get(self.url)
assert response.status_code == HTTPStatus.OK
assert response["content-type"] == "application/json"
assert response["cache-control"] == "max-age=300, public"
data = json.loads(response.getvalue())
app_id = "ABCDE12345.com.example.app"
assert data["applinks"]["details"][0]["appIDs"] == [app_id]
assert data["webcredentials"]["apps"] == [app_id]
def test_head(self):
response = self.client.head(self.url)
assert response.status_code == HTTPStatus.OK
def test_post_disallowed(self):
response = self.client.post(self.url)
assert response.status_code == HTTPStatus.METHOD_NOT_ALLOWED
class AssetlinksJsonTests(SimpleTestCase):
"""
Test the assetlinks.json file, per:
https://adamj.eu/tech/2026/10/01/django-app-links/
"""
url = "/.well-known/assetlinks.json"
def test_success(self):
response = self.client.get(self.url)
assert response.status_code == HTTPStatus.OK
assert response["content-type"] == "application/json"
assert response["cache-control"] == "max-age=300, public"
data = json.loads(response.getvalue())
assert data[0]["target"]["package_name"] == "com.example.app"
def test_head(self):
response = self.client.head(self.url)
assert response.status_code == HTTPStatus.OK
def test_post_disallowed(self):
response = self.client.post(self.url)
assert response.status_code == HTTPStatus.METHOD_NOT_ALLOWED
Notes:
- The
test_success()methods check for a200 OKstatus code, which also means there's no redirect, plus thecontent-typeandcache-controlheaders. - They then parse the body with
json.loads(), which fails on invalid JSON, such as from a trailing comma.FileResponseis a streaming response, with nocontentattribute, so they read the body withgetvalue(). - Finally, they check for the app identifiers, so swap in your own. You could extend these checks to cover more of the structure, such as URL patterns that should or shouldn't open your app.
test_head()andtest_post_disallowed()check the effect of@require_safe.
Check in production
Tests confirm your views work, but you can go a step further to check the platforms are properly integrated. After deploying, verify each file through the platforms' own infrastructure.
For Apple, since iOS 14 and macOS 11, devices don't fetch the file from your site directly. Instead, they fetch it from Apple's CDN, which periodically downloads it from your site. You can see the CDN's current copy with curl:
$ curl https://app-site-association.cdn-apple.com/a/v1/example.com
Replace example.com with your domain. If this returns an outdated version, wait, as the CDN takes a while to pick up changes. During development, your iOS developers can bypass the CDN with an alternate mode in the app's associated domains entitlement.
For Android, Google provides the Digital Asset Links API to check the statements it sees for your site:
$ curl -G https://digitalassetlinks.googleapis.com/v1/statements:list \
--data-urlencode source.web.site=https://example.com \
--data-urlencode relation=delegate_permission/common.handle_all_urls
Again, replace example.com with your domain. Alternatively, Google's Statement List Generator and Tester lets you check your file against your app's package name and fingerprint, through a web form. On a device with your app installed, your Android developers can also check and re-run verification with commands like:
$ adb shell pm get-app-links com.example.app
$ adb shell pm verify-app-links --re-verify com.example.app
Fin
Two small JSON files to let your links open where your users want them to. Serve them, test them, and check what Apple and Google see.
May your apps be interlinked and may your heart be interlinked,
-Adam
02 Oct 2026 4:00am GMT
01 Oct 2026
Django community aggregator: Community blog posts
Undocumented Django: Generating a SECRET_KEY
Django has some of the best documentation out there, a point of pride from the beginning that continues today thanks to the heroic efforts of many volunteers. But for all …
01 Oct 2026 2:09pm GMT
