02 Oct 2026

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


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:

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:

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:

  • applinks for Universal Links. appIDs lists your app IDs, each your Apple developer team ID, a dot, and the app's bundle ID. components lists URL patterns, checked in order until the first match, so exclude patterns 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.
  • webcredentials for 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-type header must be application/json.
  • Each domain must serve its own file. Associating example.com doesn't cover www.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_required marks the views as public, for projects using Django's LoginRequiredMiddleware. I recommend you use this feature from Django 5.1 to secure your site by default!
  • @require_safe restricts the views to GET and HEAD requests.
  • @cache_control sets a cache-control header 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.
  • FileResponse streams each file as the response body, byte-for-byte. It also sets the content-type header based on the file extension, so both files get application/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 a 200 OK status code, which also means there's no redirect, plus the content-type and cache-control headers.
  • They then parse the body with json.loads(), which fails on invalid JSON, such as from a trailing comma. FileResponse is a streaming response, with no content attribute, so they read the body with getvalue().
  • 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() and test_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

feedDjango 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