17 Sep 2026
Django community aggregator: Community blog posts
September Python Leiden meetup summaries
Two summaries from the September 2026 https://pythonleiden.nl/.
Maintaining Python packages to attract free and open source software contributors - Steve Piercy
Steve is in the Netherlands for next week's Plone conference in Maastricht (NL). He's been involved in open source software for over two decades.
What are contributors? Contributors to open source software? Why do you do it? Learning (I myself got a good programming education out of contributing to Zope/Plone while still at the university). You might want to give back. You might like the community, you like to belong to the community. Finding your tribe. Perhaps build a resume or CV. Help other people.
Why not? Negative online reactions. You want to really make money out of it. AI slop. You might not have time. Too busy. Life happens. You might enjoy walking your dog or doing gardening more. Or perhaps you just don't know how! Or you don't feel experienced enough (imposter syndrome). Perhaps you don't think your English is good enough (he advocates just using Google Translate).
(He asked how many people had already contributed to open source: about half the room raised their hands.)
There are actually lots of ways to contribute. It is not all code! Read this page for some examples. You can report security vulnerabilities or issues. You can comment on issues and help getting them forward. Review pull requests. Extend the documentation. You can join discussions in forums or on Matrix. Write a blog post on a project. And... you can financially sponsor a project.
(He again asked who had contributed: now most hands went up!)
Where to start? Start with what you use and what you find interesting. This helps staying involved and getting into the community more and more. He himself liked web applications and especially forms: making sure they're intuitive and safe and well-validated. And he liked documentation, so some of his first contributions were documentation fixes for open source projects.
Recently, he volunteered to take over maintainership of an open source project (a sphinx extension). There were lots of open pull requests and he asked the owners to look at it again and perhaps tweak it a bit and... everybody responded and a few weeks later he could make a new release with lots of fixes.
What is a maintainer? Well, basically maintainers are contributors who make releases. But ideally you also have to "tend your garden" and try to get your small community to thrive. One thing to keep in mind: you have to put your ego aside. If it is open source, it is really owned by your users, not by you. What do you have to do? Show up when someone has a question. Document your project (how to contribute, how to report bugs and security issues, etc.).
He especially mentioned https://djangonaut.space , that's a great initiative to help people get involved with contributing. Same with https://djangogirls.org/ .
As a maintainer, you might also have to look for funding. There are options like "github sponsors". But also NLnet (from the Netherlands) and the German sovereign tech agency that sponsor lots of projects.
A best practice of good maintainers is to have good tooling. Automatic tests + coverage. Build documentation. Formatting checks. zest.releaser for good releases.
The most important part of being a maintainer: how do you treat people.
There's one big problem: AI. There are just too many pull requests and too many issues. You can never get through them if your project is reasonably project. Dealing with it is hard. Several projects made the choice to disallow AI contributions. Also read https://leidendeclaration.ai/ , for the same problems are entering math and science, too.
But on the other hand... in the icalendar project that he helps maintain, they got a new contributor that made his first contribution based on AI. He liked that: the person got enough confidence through AI. They now have an AI guideline in their contributor guide now.
There's are automated tools on github that can help you check pull requests and identify slop-generating accounts, for instance. A tip when confronted with suspected AI work: ask vague, clarifying questions. Just ask for a bit of clarification in a vague way: AIs can't respond to vague questions, but humans can.
"Once men turned their thinking over to machines in the hope that this would set them free. But that only permitted other men with machines to enslave them." - Frank Herbert, Dune. Funny that he could write that 50 years ago.
Something mentioned in the discussion: people are now bidding on real github accounts in order to let AI bots use accounts that appear real...
Watch out with AI. One of the links he shared: https://pivot-to-ai.com/2026/09/08/students-who-dont-use-ai-are-ahead-on-every-measure/ . And, look at the kids: it was bad enough with social media, but AI is much worse. And freelancers are getting buried with soulless AI slop cleanup: artists get to clean up AI-generated images, for instance.
Choose wisely. A quote he wanted to give us to think about: teach the world you want.
Monitoring my washing machine - Michiel Beijen
Michiel has a 2021 Samsung washing machine that plays Die Forelle by Schubert when it is finished washing... But the washing machine sits in the garage so he can't hear the sound. It is a smart machine, so there's an iphone app for that! "SmartThings". But the list of data it wants to collect about you is horrendeous. And the app is almost 1GB! No...
There's also "home assistant", written in python. There's even a SmartThings API connection. But... from October 2026 onwards you need a $5/month samsung subscription!?!
There's also "Matter". They call it vendor neutral, standards compliant. Samsung supports it. But only for connecting Matter devices to SmartThings, not the other way around.
Next try: put a small camera in front of the washing machine's display, add a bit of OCR text recognition to detect the remaining time from the image and add an http interface to it. An old webcam couldn't provide a good camera. A raspberry Pi Camera 3 did the trick. (He later thought it would perhaps have been better to pick some old smartphone.)
He showed some graphs detailing how the washing machine estimates the time it will finish, including the adjustments it made halfway (adjusting for load or so).
Another approach that a colleague of him took: use a monitoring plug that measures the electricity used. Once the electricity usage drops off, the wash is finished.
17 Sep 2026 4:00am GMT
16 Sep 2026
Django community aggregator: Community blog posts
Django: serve the change password well-known URL
When a password manager detects that a user's password has been leaked or reused, it can prompt them to change it, but the password change URL varies by site. The web's answer to such discovery problems is the reserved /.well-known/ URL namespace (RFC 8615), home to machine-readable files and endpoints like security.txt. A Well-Known URL for Changing Passwords is the web specification that uses this namespace to fix password page discovery. It reserves the URL path /.well-known/change-password to redirect to your actual change password page, wherever that lives.
Password managers that use this URL include Apple's iCloud Keychain (in Safari since 2019), Google Password Manager (since Chrome 86, 2020), and 1Password. web.dev has an excellent article explaining the specification and showing the Google Password Manager feature in action.
In this post, we'll look at implementing the change password URL in a Django project.
Add the redirect
The specification asks that /.well-known/change-password redirect to your change password page with a temporary redirect status code, for which you can use Django's RedirectView. The pattern_name argument looks up the target URL by name, so the redirect stays correct even if you move the page.
So, to add a redirect, plop this path in your root URLconf:
from django.urls import path
from django.views.generic import RedirectView
urlpatterns = [
# ...
path(
".well-known/change-password",
RedirectView.as_view(pattern_name="password_change"),
),
# ...
]
Note the path has no trailing slash, per the specification and counter to Django's default pattern. There's also no need for a name= argument, since nothing on your site should link to the URL.
password_change is the URL name provided by django.contrib.auth.urls, which serves Django's built-in PasswordChangeView. This assumes your URLconf includes those URLs, conventionally mounted at accounts/:
path("accounts/", include("django.contrib.auth.urls")),
If you serve your change password page some other way, swap in the appropriate URL name for pattern_name, such as account_change_password if you use django-allauth. Django doesn't check pattern_name until a request arrives, so a wrong name here fails only when the URL is visited, hence the test below.
Check with runserver and visit http://localhost:8000/.well-known/change-password - you should land on your change password page (or the login page redirecting you there with ?next).
Password managers only ever visit the URL on your live site (with HTTPS), so after deploying, repeat the check on your production domain.
For an end-to-end check, Chrome's password checkup tool (under Settings → Passwords) shows a "Change password" button for compromised entries, which should open your page directly once the redirect is deployed. To make the button appear without waiting for a real breach, temporarily change your saved password for the site to a deliberately weak one, like password123, which Chrome flags in the check.
Public users only
By the way, this feature is for public users and their password change pages, not admins. So don't use this feature to redirect to your Django admin's password change view, or any other private page. That would advertise those URLs to the world, undoing the common hardening of hosting Django's admin at a non-default path.
Check the resource that should not exist
There's a second URL to be aware of, a check for whether your server is broken. Some misconfigured servers respond with 200 to every request, serving an error page instead of using a proper 404 status code. On such a server, a client fetching /.well-known/change-password can't tell whether it found a real change password page or an error page.
The specification solves this with a second reserved path, gloriously named:
/.well-known/resource-that-should-not-exist-whose-status-code-should-not-be-200
Clients may request this URL to detect broken servers. If it responds with a 200, the server's status codes are deemed meaningless, so the client ignores the change password URL and falls back to something cruder, like opening your homepage.
Django responds with a 404 for unmatched URLs, so your site should pass this check, with nothing to implement. But a catch-all URL pattern could break it, such as one serving pages from a CMS, or a single-page application fallback that serves index.html with a 200 for any path. So it's worth covering with a test, included below.
Add tests
As ever, it's best to include tests to guard against accidental breakage, such as removal of the URL. Here's a test case covering both URLs:
from http import HTTPStatus
from django.test import SimpleTestCase
from django.urls import resolve
class ChangePasswordWellKnownTests(SimpleTestCase):
"""
Test the well-known URLs for changing passwords, per:
https://adamj.eu/tech/2026/09/16/django-change-password-url/
"""
def test_change_password(self):
response = self.client.get("/.well-known/change-password")
self.assertRedirects(
response,
"/accounts/password_change/",
fetch_redirect_response=False,
)
resolve(response["Location"]) # Check it's a real URL
def test_resource_that_should_not_exist(self):
response = self.client.get(
"/.well-known/resource-that-should-not-exist-whose-status-code-should-not-be-200"
)
self.assertEqual(response.status_code, HTTPStatus.NOT_FOUND)
Notes:
- Neither view uses the database, so the test case uses
SimpleTestCase, which blocks database access and runs a little faster. assertRedirectschecks both the status code, 302 by default, and the target URL.- The target URL is hardcoded, matching where the auth URLs were mounted earlier. If yours live elsewhere, adjust it. Hardcoding, rather than using
reverse(), makes the test check what clients see, rather than using any internal details of your system. - Passing
fetch_redirect_response=Falsestops the test client from following the redirect. Without it,assertRedirectswould fetch the change password page and fail, because when logged out that page responds with a second redirect, to the login page. resolve()raisesResolver404if the target URL doesn't map to a view. Calling it makes up for the skipped fetch above, checking that the redirect points at a real page rather than a typo.
Check your form's autocomplete attributes
The web.dev article also recommends annotating your change password form fields with autocomplete attributes, so password managers can fill in the current password and suggest a generated replacement:
autocomplete="current-password"on the current password fieldautocomplete="new-password"on the new password field(s)
If you use Django's built-in PasswordChangeForm, it's done for you, as the widgets there have included these attributes since Django 3.0.
But if you've built a custom form, it's worth checking that its fields carry the right attributes. You can set them through the attrs argument of each field's widget, for example:
from django import forms
class ChangePasswordForm(forms.Form):
current_password = forms.CharField(
widget=forms.PasswordInput(attrs={"autocomplete": "current-password"}),
)
new_password = forms.CharField(
widget=forms.PasswordInput(attrs={"autocomplete": "new-password"}),
)
...
If you're customizing Django's flow, prefer subclassing PasswordChangeForm, which carries those attributes already.
Fin
So there we go, a nice little standard to make your user's security a little easier. Add one URL entry and password managers can shepherd your users away from compromised passwords.
May your data never leak and your users passwords always be strong,
-Adam
16 Sep 2026 4:00am GMT
15 Sep 2026
Django community aggregator: Community blog posts
Duff's device, part 2: copying within an array
Duff's device in JavaScript raced hand-written loops that copy one array into another. A reader asked the follow-up: how do they compare with Array#copyWithin, the built-in that copies a range inside a single array? That is a different workload, so it needs its own measurement. The short answer: on every Node and Deno we can install today, the built-in runs 47 to 84 times slower than the loop.

15 Sep 2026 10:00am GMT