23 Aug 2025
Django community aggregator: Community blog posts
Menu improvements in django-prose-editor
Menu improvements in django-prose-editor
I have repeatedly mentioned the django-prose-editor project in my weeknotes but I haven't written a proper post about it since rebuilding it on top of Tiptap at the end of 2024.
Much has happened in the meantime. A lot of work went into the menu system (as alluded to in the title of this post), but by no means does that cover all the work. As always, the CHANGELOG is the authoritative source.
0.11 introduced HTML sanitization which only allows HTML tags and attributes which can be added through the editor interface. Previously, we used nh3 to clean up HTML and protect against XSS, but now we can be much more strict and use a restrictive allowlist.
We also switched to using ES modules and importmaps in the browser.
Last but not least 0.11 also introduced end-to-end testing using Playwright.
The main feature in 0.12 was the switch to Tiptap 3.0 which fixed problems with shared extension storage when using several prose editors on the same page.
In 0.13 we switched from esbuild to rslib. Esbuild's configuration is nicer to look at, but rslib is built on the very powerful rspack which I'm using everywhere.
In 0.14, 0.15 and 0.16 the Menu
extension was made more reusable and the way extension can register their own menu items was reworked.
The upcoming 0.17 release (alpha releases are available and I'm using them in production right now!) is a larger release again and introduces a completely reworked menu system. The menu now not only supports button groups and dialogs but also dropdowns directly in the toolbar. This allows for example showing a dropdown for block types:
The styles are the same as those used in the editor interface.
The same interface can not only be used for HTML elements, but also for HTML classes. Tiptap has a TextStyle extension which allows using inline styles; I'd rather have a more restricted way of styling spans, and the prose editor TextClass
extension does just that: It allows applying a list of predefined CSS classes to <span>
elements. Of course the dropdown also shows the resulting presentation if you provide the necessary CSS to the admin interface.
23 Aug 2025 5:00pm GMT
22 Aug 2025
Django community aggregator: Community blog posts
Django News - State of Python 2025 Results - Aug 22nd 2025
News
State of Python 2025 Is Out!
Explore the key trends and actionable ideas from the latest Python Developers Survey, which was conducted jointly by the Python Software Foundation and JetBrains PyCharm and includes insights from over 30,000 developers.
PyPI now serves project status markers in API responses
PyPI now exposes standardized project status markers through its HTML and JSON index APIs, enabling package installers to programmatically signal dependency status and manage installations.
Preventing Domain Resurrection Attacks
PyPI now checks for expired domains to prevent domain resurrection attacks, a type of supply-chain attack where someone buys an expired domain and uses it to take an account through password resets.
Updates to Django
Today "Updates to Django" is presented by Velda Kiara from Django Events Foundation North America (DEFNA)! π
Last week we had 15 pull requests merged into Django by 10 different contributors - including a first-time contributor! Congratulations to Rohit for having their first commits merged into Django - welcome on board!
Django Core Updates β¨
-
Fix to
Subquery.resolve_expression()
output field handling which corrects how Django determines theoutput_field
in subqueries. This adjustment restores consistent and predictable query behavior. -
Template partials arrive in DTL adds two new tags,
partial
andpartialdef
, that let developers define and reuse named chunks of templates. This brings cleaner organization and modularity to template design
Community Updates π¦
Want to celebrate Django's birthday with fellow Djangonauts? Head over to the Django20 website to attend one of the birthday celebrations to a city near you.
That's all for this week in Django development! ππ¦
Django Newsletter
Wagtail CMS
Wagtail Space 2025 is a go!
Wagtail Space 2025 is a free three-day Zoom event featuring lightning talks and networking to shape future Wagtail improvements.
Sponsored Link 1
AI-Powered Django Development & Consulting
REVSYS specializes in integrating powerful AI technologies, including GPT-5, directly into your Django applications. We help bring modern, intelligent features to your project that boost user engagement and streamline content workflows.
Articles
Sometimes LFU > LRU
Stop letting bot traffic evict your customers' sessions. A simple Redis configuration switch from LRU to LFU solved our crawler problem, with a Django configuration example.
Python Namespace Packages are a pain
Ensuring init.py is present in every directory prevents ambiguous namespace packages, streamlines module imports, and mitigates cryptic errors in Python packaging.
DjangoCon Africa x UbuCon 2025 Reflections: Stay For The Community
DjangoCon Africa x UbuCon 2025 underlines robust community collaboration, open-source initiative growth and challenges in sustaining African Django development through engaging sprints and talks.
Customize your IPython shell in Docker
Customize your IPython shell in Docker with tailored profiles and startup scripts that streamline Django shell_plus debugging, imports, and UUID extraction workflows.
Best Python Books (2025)
An up-to-date list of the best books for learning Python.
Events
Friends of PyCon Africa Livestream
Join the August 30th livestream celebrating the vibrant Python community across Africa! This isn't your typical webinar - it's a dynamic, fun-filled conversation where Python community members will drop in and out throughout the event, sharing their stories, projects, and passion for Python.
Guests include Carlton Gibson, Dawn Wages, Michael Kennedy, Sarah Abderemane, and more.
Be Part of Something Amazing: Volunteer at DjangoCon US 2025 in Chicago!
Join DjangoCon US 2025 as a volunteer in Chicago to gain insider event management experience, expand your network, and strengthen the Django community.
Videos
Talk Python Live Stream: Celebrating Django's 20th Birthday with its Creators
A discussion of Django's past, present, and future featuring Adrian Holovaty, Simon Willison, Thibaud Colas, Jeff Triplett, Will Vincent, and Michael Kennedy.
"How a Solo Hobbyist Learned to Love Testing" - Carl James
PyOhio talk on slowly integrating testing into Django apps and, by proxy, learning more about the underlying libraries along the way.
DjangoCon Videos
Logs, shells, caches and other strange words we use daily
This insightful talk reveals the unexpected origins of computing terms, linking historical context to modern software engineering practices relevant to Django experts.
Sponsored Link 2
Sponsor this newsletter!
Podcasts
Django Brew #6: Celebrating 20 Years of Django
A podcast episode celebrates Django's 20th anniversary using trivia, reflections, and community updates to engage developers with historical highlights and events.
Test & Code | 238: So Long, and Thanks for All the Fish
Brian Okken reflects on a decade of Python testing and podcasting, sharing lessons learned and inviting continued engagement via his Python platforms. A farewell to a fun 10 years.
Django News Jobs
Senior Python Developer at Basalt Health π
Senior Full Stack Engineer at Lyst π
Backend Python Software Engineer (Hybrid) at NVIDIA π
Senior Python Developer at Brightwater
Senior Backend Engineer at Prowler
Django Newsletter
Projects
joshuadavidthomas/mcp-django-shell
MCP server providing a stateful Django shell for AI assistants.
edelvalle/djhtmx
Interactive UI components for Django using htmx.org.
This RSS feed is published on https://django-news.com/. You can also subscribe via email.
22 Aug 2025 3:00pm GMT
18 Aug 2025
Django community aggregator: Community blog posts
Configurable UI in Software
Another short one today, that is a pattern I have noticed in a couple pieces of software I use, notably Todoist, Slack & Vivaldi. All three of these allow a user to configure the menu options to some degree. Slack has the option to customise the navigation options shown within a particular workspace to optimise the experience for a user. Todoist takes this a step further in the mobile app to allow a user to sort the menu items.
Browsers have always had a great experience of customisation, but Vivaldi takes this to an awesome extreme by allowing a user to customise each and every possible context menu, giving true flexability to their users.
Personally I have never considered the power of this and wonder if there are any efficient implementation of this for Django without creating a huge amount of complexity. The naive default solution would likely involve a model and a context processor and/or a middleware, it might be something I add in my next project, if we feel it would be beneficial to our users.
18 Aug 2025 5:00am GMT
15 Aug 2025
Django community aggregator: Community blog posts
Django News - A New Django Fellow - Aug 15th 2025
News
Jacob Tyler Walls is Our New Fellow
Jacob Tyler Walls joins the Django Fellowship, bringing Django contributions, Triage and Review experience, ORM expertise, and GIS and open source maintenance skills.
Python 3.14.0rc2 and 3.13.7 are go!
Python 3.14 release candidates provide performance enhancements, new language features, and ABI stability, urging Django maintainers to prepare for compatibility testing.
Announcing the PSF Board Candidates for 2025!
There are four board seats open for this year's PSF election. The timeline, voting details, and candidates are included in the post.
Announcing Python Software Foundation Fellow Members for Q2 2025! π
PSF announces Q2 2025 Fellows, recognizing new contributors who lead projects, maintain libraries, organize events, and mentor to grow the Python community globally.
Django Software Foundation
DSF member of the month - Jake Howard
Jake Howard, DSF member and Django contributor, leads DEP 14 and develops Django tasks, advancing native background workers and task infrastructure for Django.
Building better APIs: from Django to client libraries with OpenAPI
Combine Django REST Framework with drf-spectacular to generate OpenAPI specs and use openapi-generator plus CI to produce and maintain automated client libraries.
Django's accessibility contributing guide
The Django accessibility team published contribution guidelines detailing practical testing, design steps, reporting workflow, and resources to help contributors make Django accessible to assistive technologies.
Updates to Django
Today, 'Updates to Django' is presented by Velda Kiara from Django Events Foundation North America (DEFNA)! π
Last week we had 16 pull requests merged into Django by 12 different contributors - including two first-time contributors! Congratulations to Xdynix and Alec Kerrigan for having their first commits merged into Django - welcome on board!
Django Core Updates β¨
- The
collectstatic --clear
command now suppresses individual deleted file messages and provides a summary count of deleted files. Additionally, individual clashing name warnings in thecollectstatic
command's default have been silenced. - Added a system check for
ManyToManyField
when declared on models that utilizeCompositePrimaryKey
to prevent misconfigurations and enhance model integrity. - Django now handles
F("field__transform")
andQ(field__transform__lookup=value)
lookups correctly during constraint validation which will eliminate false positives.
Community Updates π
Djangonaut Space session 5 is open for applications. Session 5 will run from September 29th, 2025, to November 23rd, 2025. The deadline is September 14th, 2025.
Read more and apply through the link in the blog here.
That's all for this week in Django development! ππ¦
Django Newsletter
Wagtail CMS
Front-end architecture fundamentals
Revisiting the fundamentals of the Web that Wagtail relies on
Articles
pyx: a Python-native package registry, now in Beta
pyx is a Python-native package registry from the creators of uv.
Updating Python
Eric Matthes, author of Python Crash Course, covers this common question in his most recent "Mostly Python" newsletter. He shows how to clean up old pyenv and Homebrew Pythons in favor of adopting uv
to install and manage Python interpreters and virtual environments.
Get started with event sourcing today
Use the eventsourcing Python package to learn Event, Aggregate, and Application patterns, then apply event sourcing to Django app domain logic with minimal infrastructure.
Combining Django signals with in-memory LRU cache
It's easy to combine functools.lru_cache with Django signals to get a good memoization pattern on Django ORM queries.
HTTP Cats
API for HTTP Cats
Django Fellow Report
Fellow Report - Natalia Bidart
3 tickets triaged, 9 reviewed, 1 authored, and other misc.
Fellow Report - Sarah Boyce
3 tickets triaged, 26(!) reviewed, and released Django 5.2.5.
Forum
Contributing opportunities list - Django Internals
A small project to list the contributing opportunities available in the Django community.
Events
DjangoCon Africa this week!
DjangoCon Africa has been taking place this week in Zanzibar. You can follow along on Fosstodon, LinkedIn, and other socials.
Django Girls Chicago - September 6th
Django Girls Chicago is happening during DjangoCon US week! A free one-day workshop for women beginners. No coding experience needed! Applications close August 23rd, 2025 - don't wait!
Introducing Keanya Phelps, DjangoCon US 2025 Conference Chair
A chat with Keanya Phelps about her role as Conference Chair and her experience with Django as a mid-life career changer.
DjangoCon Videos
Bulletproof Data Pipelines: Django, Celery, and the Power of Idempotency
Learn how to build resilient data pipelines with Django, Celery, and idempotent consumers. We'll dive into robust error-handling techniques and the role of idempotency in ensuring reliable and consistent data processing.
How to solve a Python mystery
Using Linux observability tools to learn what your Python application is doing.
Django News Jobs
Backend Python Software Engineer (Hybrid) at NVIDIA π
Senior Python Developer at Brightwater π
Senior Backend Engineer at Prowler
Django Newsletter
Projects
dumaas/django-ulidfield
A drop-in Django model field for storing sortable, time-encoded ULIDs as 26-character strings.
adamghill/dj-lite
Configure SQLite for production use with Django.
Sponsorship
π Book for Q3 2025!
Over 4,200 Django developers read Django News every week. If you have a product, service, or job to share, sponsoring an issue is a great way to get in front of them.
It's summer. People are building cool things. This is your chance to join the conversation.
This RSS feed is published on https://django-news.com/. You can also subscribe via email.
15 Aug 2025 3:00pm GMT
14 Aug 2025
Django community aggregator: Community blog posts
Standards for third-party packages
I am starting to feel like I have written about this too much as this point, but anyway, third-party packages again!
Recently I have been wondering about why third-party packages exist within the Django ecosystem. Broadly there is a few categories
- Establishing some new functionality that doesn't exist in core (APIs, Feature flags, Payments, etc)
- Extending an existing API with a new backend (databases, caches etc)
- Utilities to help with development (perhaps this is a subset of the first point)
- Packages demonstrating functionality that would be desirable with in Django core.
Now one huge benefit to third-party packages is that it allows for choice, be it in the tools and dependencies used, the design and scope of the code. This is both for the package creator/maintainer and the user of said package. However I want to focus in on the last usage above and question whether choice is a good thing for this type of package.
If we were to build a package with the goal of it perhaps one day being merged into Django, would it not benefit our future selves and others if the overall design of the package matched Django conventions and standards whereever possible? For example, choosing unittest over pytest, or minimising the number of external dependencies that would be classed as unnecessary?
This has led to me to the idea of creating an 'official' template repo or cookiecutter template (or someother tool) that produces a repo for packages like this. Is it an extra maintanence burden? Yes, but I think it would greatly improve the new contributor story when they are told to create a package for their idea. And when I say official, ideally it would be within the Django Github organisation, but if it started in another place, say Django-commons, that would also work. I do wonder how it could be worked to lessen the maintenance burden though?
Good idea or not?
14 Aug 2025 5:00am GMT
12 Aug 2025
Django community aggregator: Community blog posts
Why Open Source Makes Sense For Your Business Web Projects
What Is Open Source?
Open source software is publicly available code that anyone can use, modify, and improve. It's the foundation of much of the modern internet and it's trusted by global enterprises, startups, nonprofits, and government agencies alike.
12 Aug 2025 11:15pm GMT
11 Aug 2025
Django community aggregator: Community blog posts
Happy 20th Birthday Django!
This Tuesday (tomorrow!), August 12th, we're teaming up with TriPython to host an informal Django Birthday event at Boxyard RTP in Durham.
11 Aug 2025 9:27pm GMT
09 Aug 2025
Django community aggregator: Community blog posts
Combining Django signals with in-memory LRU cache
It's easy to combine functools.lru_cache with Django signals to get a good memoization pattern on Django ORM queries.
09 Aug 2025 11:33pm GMT
08 Aug 2025
Django community aggregator: Community blog posts
Django News - Django 5.2.5 Released - Aug 8th 2025
News
Django bugfix release issued: 5.2.5
Django 5.2.5 provides essential bug fixes to boost application stability and performance, ensuring smooth deployments and improved developer experience.
django-rest-framework release v3.16.1
DRF v3.16.1 fixes unique_together and source field bugs, removes legacy Python support, and enhances translations, documentation, and internal testing with Django 5.2 compatibility.
Python Insider: Python 3.13.6 is now available
Python 3.13.6 incorporates extensive bug fixes, build improvements, and documentation updates that enhance overall performance and reliability essential for Django applications.
Join the Mission: Session 5 Applications Open! π
Djangonaut Space opens applications for Session 5, an eight-week group mentorship guiding contributors to make sustained contributions to Django core and related projects. Applications open August 10th, 2025.
Preventing ZIP parser confusion attacks on Python package installers
PyPI will reject malformed or ambiguous wheel ZIPs and begin enforcing RECORD consistency to prevent ZIP parser confusion attacks across Python installers.
Django Software Foundation
DSF member of the month - Jake Howard
Jake Howard, DSF member and Django contributor, details his DEP 14 background workers, community involvement, and focus on enhancing Django security and performance.
Updates to Django
Today 'Updates to Django' is presented by Velda Kiara from Django Events Foundation North America (DEFNA)! π
Last week we had 11 pull requests merged into Django by 7 different contributors - including 1 first-time contributor! Congratulations to Mridul for having their first commits merged into Django - welcome on board!β¨
This week's highlights include:
- Added forkserver support to parallel test runner that improves test isolation and stability when running tests in parallel environments.
- Resolved a crash triggered by filtering queries on composite primary keys using tuple expressions.
Community News: Djangonaut Space is looking for contributors to be mentors for the next cycle of the program. Learn more about the program and how to get involved here!
That's all for this week in Django development! ππ¦
Django Newsletter
Wagtail CMS
Enjoy better UX with Wagtail 7.1
Wagtail 7.1 improves the admin user experience with collapsible custom blocks, enhanced media listings, headless previews, keyboard navigation, and per-site permission controls.
Annotating the Parent on a Page QuerySet
Demonstrates using Django ORM subqueries and custom database functions to annotate a parent field on Wagtail Page querysets while noting built-in alternatives.
What AI tools get right and wrong with Wagtail
A general look at how useful AI tools are for building Wagtail projects
Sponsored Link 1
AI-Powered Django Development & Consulting
REVSYS specializes in seamlessly integrating powerful AI technologies, including GPT-4, into your existing Django applications. Your Django project deserves modern, intelligent features that enhance user engagement and streamline content workflows.
Articles
Boosting SEO with Django Ninja, Pydantic, and JSON-LD
Integrate SEO-friendly JSON-LD structured data in Django apps by generating and validating Schema.org schemas server-side using Django Ninja and Pydantic for maintainable, reusable code.
How JIT builds of CPython actually work
CPython JIT builds translate frequently executed bytecode into optimized native machine code using micro-ops and LLVM templates to improve runtime efficiency.
How Python Grew From a Language to a Community - The New Stack
Python's evolution into a robust, community-driven ecosystem underpins modern frameworks like Django, fueling collaboration and innovation with sustained open source support.
User Timezones in Django
Implement user-specific timezones in Django by storing times in UTC, updating tzdata regularly, using zoneinfo for accurate conversions, and ensuring reliable datetime handling.
Loopwerk: Automate Python package releases
Automate Django package releases using tbump and GitHub Actions to update versions, run tests, lint code, and publish reliably to PyPI with minimal manual intervention.
TIL: Managing Editable Dependencies in uv Without Committing Local Paths
Dynamically switch uv dependency sources in pyproject.toml to maintain editable installations for local Django package development while enforcing production git sources via pre-commit hooks.
Flask or Django: Which One Best Fits Your Python Project?
Django's full-featured framework offers rapid development, robust security, and an integrated ORM that excels in building scalable, feature-rich applications compared to minimalist Flask alternatives.
Forum
Removing non-HTML (PDF, EPUB, etc) documentation downloads
Proposal to drop Python documentation's PDF, EPUB, plain text, and texinfo builds to save resources and accelerate HTML docs updates.
Events
Announcing Our Keynotes: Carson Gross
Carson Gross is the creator of HTMX and his keynote integrates Django and HTMX with Nintendo Game Boy design principles for a hypermedia future.
Django on the Med ποΈ
A free three-day Django sprint in Palafrugell offers focused development, detailed travel logistics, venue information, and community networking opportunities.
Announcing Our Keynotes: Zags (Benjamin Zagorsky)
Zags will deliver a DjangoCon US 2025 keynote exploring innovative Django use cases, advanced ORM enhancements, and advocating built-in JavaScript integration.
Design Articles
A Friendly Introduction to SVG
Learn SVG fundamentals including inline SVG usage, shape primitives, viewBox configurations, and CSS animated strokes for creating dynamic scalable graphics within web projects.
DjangoCon Videos
Europe, Django and two-factor authentication - Agnès Haasser
A talk on why you should consider adding 2FA to your Django app, and how (quickly and easily) you can do it.
Django News Jobs
Senior Backend Engineer at Prowler
Backend Engineer at 7Learnings
Django Newsletter
Projects
loopwerk/django-generic-notifications
A flexible, multi-channel notification system for Django applications with built-in support for email digests, user preferences, and extensible delivery channels. - loopwerk/django-generic-notifica...
paduszyk/django-xlsx-serializer
Load/dump Django models from/to Excel 2007+ workbooks.
Sponsorship
π Hey Django fans, want to get noticed?
Over 4,200 Django developers read Django News every week. If you have a product, service, or job to share, sponsoring an issue is a great way to get in front of them.
It's summer. People are building cool things. This is your chance to join the conversation.
Django Newsletter
This RSS feed is published on https://django-news.com/. You can also subscribe via email.
08 Aug 2025 3:00pm GMT
Documentation that is never wrong
The iommi docs are more correct than most projects because we take a different approach to documentation: part of the test suite is the documentation. Let's look at an example:
def test_grouped_fields():
# language=rst
"""
.. _group-fields:
How do I group fields?
~~~~~~~~~~~~~~~~~~~~~~
.. uses Field.group
Use the `group` field:
"""
form = Form(
auto__model=Album,
fields__year__group='metadata',
fields__artist__group='metadata',
)
# @test
show_output(form)
# @end
This ends up as this documentation:
This is a normal test that runs with the normal test suite, with some additional markup:
- The triple quoted strings that are declared with
# language=rst
are included in the docs. - Code is by default included in the documentation
- You can exclude code with
# @test
/# @end
for checks you don't want to include in the docs show_output
renders some HTML output into a file that is then shown inline in the finished docs- The
.. uses
command is used to mark what features this test uses so the examples are automatically linked from the reference API docs
With this infrastructure in place, some fixes or features can be implemented with all the required tests written as the documentation with no additional tests. This radically incentivises writing docs compared to duplicating work across tests and docs.
08 Aug 2025 5:00am GMT
07 Aug 2025
Django community aggregator: Community blog posts
A beginner check for makemigrations
Could our tools be smarter (even without AI) and helpful to prevent footgun usage? Today I'm taking aim at the Django makemigrations
command. I feel fortunate to have been introduced to South and Django migrations at the beginning of my career so the logic of the migration files and the workflow makes sense in my head (or if I was confused I was corrected very early on by colleagues). However I wonder what other tech stack's do in regard to keeping keeping code and schema's in sync.
I question this as every so often I run into newcomer's to Django not commiting migration files to source control and running makemigrations
in every environment, which if you didn't know is a very bad idea that will lead to numerous issues as the project progresses. This led me to the following question:
Could we prevent this happening in the first place, or place a burden on those knowing the risks when taking them?
My immediate answer to this is spit out a warning if someone tries to run makemigrations
when the DEBUG
setting is False. To me this should be the minimum to add to the command. Using the DEBUG setting is a proxy for any environment that isn't development, it won't be a 100% accurate, but close enough I feel. (Side thought, what about a setting for named environments?)
Going further we could even exit the command early if DEBUG
is False or show a prompt similar to collectstatic
or even require setting a new environment variable to allow the check to be bypassed when DEBUG
is False.
Additionally when the command is run successfully could we add a message along the lines of Be sure to commit these files to your version control tool of choice
perhaps even doing a bit of introspection of directories to show the relevant commands in the message?
Any other thoughts on this? I am now half expecting to hear of all the legimate use-cases for running makemigrations
when DEBUG is False! But I think this would be a useful addition to the command to prevent a footgun that happens fairly often for beginners.
07 Aug 2025 5:00am GMT
Litestar is worth a look
A few years ago at work, I had a project which offered an opportunity to look at the new generation of async-first, type-hint-driven Python web frameworks. For reasons which aren't particularly relevant today, on that project I ended up choosing Litestar, which is the one that doesn't have a ravenous all-consuming hype machine surrounding it. And I'm very glad I did, because today I'm more convinced than ever it was the right choice, and for the last 18 months or so every new project I've started at my day job has been built with Litestar.
But even if you're someone who does Python web apps for a living, and even if you're someone who builds asynchronous type-hint-driven web apps, you might not be familiar with this absolute gem of the Python web ecosystem, and today I want to remedy that.
A taste
Here's the traditional single-file-app demo:
from litestar import Litestar, get
@get("/greet")
async def greet(name: str) -> str:
return f"Hi, {name}!"
app = Litestar([greet])
You save this as app.py
, run with litestar run
or hand it directly to the ASGI server of your choice, and it launches a web application. You go to /greet?name=Bob
and it replies "Hi, Bob!". Leave out the name
parameter and it responds with an HTTP 400 telling you the name
parameter is required.
So what. Big deal. The FastAPI Evangelism Strike Force will be along soon to bury you under rocket-ship emoji while explaining that FastAPI does the same thing but a million times better. And if you're a Java person used to Spring, or a .NET person used to ASP.NET MVC, well, there's nothing here that's new to you; you've had this style of annotation/signature-driven framework for years (and in fact one thing I like about Litestar is how often it reminds me of the good parts of those frameworks). And did anyone tell you FastAPI does this, too! πππππππππ
But there are a lot of things that make Litestar stand out to me in the Python world. I'm going to pick out three to talk about today, and one of them is hiding in plain sight in that simple example application.
What's in a name?
You might see older material referring to Litestar as "Starlite", which was its original name.
Starlette is a toolkit for doing async Python web development, which can be used standalone or as a component in a more complex library or framework. FastAPI still uses Starlette under the hood, for example. And Litestar originally was built on Starlette too, and was named "Starlite", presumably in recognition of that. Over time, though, it dropped the Starlette dependency in favor of its own implementations for that functionality, and people on social media complained that the "Starlite" name was confusing, especially since Starlette was no longer being used. So the project which had been "Starlite" was renamed to Litestar for version 2.0, released in 2023, and has had that name ever since.
Scaling (the other kind)
It's a bit unfortunate that the term "scaling" is almost always assumed to mean handling larger and larger quantities of traffic, because that's only one axis on which any given piece of of technology can "scale" (and, I'd argue, possibly the least important one). The type of scaling I want to talk about here is scaling of a codebase: how does something (in this case, a web framework) help or hinder you as you deal with different amounts of code?
Django, for example, has a reputation for not scaling "down" all that well. You can do it if you really want to, and every so often someone will come up with a new demo of doing a Django project in a single Python file, but it's just not something that comes naturally to Django. Quite the opposite: if you work through the official beginner Django tutorial and do things the "Django way", you'll have around a dozen files laid out in a specific structure of directories and subdirectories before you've written a single meaningful line of your own code.
But "micro" frameworks have often had the opposite problem: they're great at starting out with a tiny single-file application, and then get painful as your codebase grows and needs to spread out (single-file Django approaches have the same problem: you have to do a lot of work to get a "micro-Django" working, and then you have to undo all that work as soon as the code grows large enough to be worth splitting across multiple files).
Let's look at an example. Here's a FastAPI equivalent of the basic Litestar application I showed above:
from fastapi import FastAPI
app = FastAPI()
@app.get("/greet")
async def greet(name: str) -> str:
return f"Hello, {name}!"
Notice that the get()
decorator here is attached to the application object. This is a common pattern (Flask/Quart do the same thing, for example, and Starlette used to but has deprecated its entire decorator-based routing system), but it creates a problem once you have multiple files. You need to import the main application object into the other files in order to decorate the routes, but you need to import the other files into your "main" application file to make sure the route registrations are visible from there, and now you have a circular import, and that doesn't work.
The general solution these frameworks offer is some sort of alternative sub-application object which can act as a per-file route registry that's safe to import into the file where your application object is defined. FastAPI calls this object an "API router"; Flask/Quart call it a "blueprint". Either way, it's a necessary construct for those frameworks because their route decorators are always bound to some parent object, either the application object in a single-file app or an "API router"/"blueprint"/etc. object in a multi-file app.
That solves the circular-import problem, but creates a new issue: the whiz-bang quickstart demos of "micro" frameworks generally register all the example routes on the application object in a single file in order to keep everything as simple and flashy as possible, but now in order to build a real application (which will almost never stay in a single file) you'll need to use a different mechanism, or start out following the demo and then switch later on. You also have to know about that different mechanism; in one framework's documentation that I looked at, you can (at the time I'm writing this post) apparently get 40 pages into the user guide before encountering the section on how to register routes in a multi-file app πππ.
Litestar avoids this entire mess by having the route decorators be standalone functions, not bound to a parent application or application-like object. This may seem like a small thing to focus on, but if you've spent time with popular Python microframeworks you've probably had to deal with the transition from single- to multi-file applications.
More importantly, this small change in approach frees up Litestar's documentation to introduce route-grouping constructs early on and to present them as part of a coherent layered architecture/configuration concept rather than as an escape hatch for avoiding circular imports. Which is great, because Litestar's layered architecture is one of its best features: its grouping constructs, and their ability to share configuration, offer an elegant way to compose functionality. For example, a common pattern I use when writing a set of CRUD endpoints looks like this):
from litestar import Router
from litestar.di import Provide
# Imagine some CRUD routes for widgets defined here...
_write_widget_router = Router(
guards=[some_auth_function],
route_handlers=[create_widget, delete_widget, update_widget])
)
widget_router = Router(
dependencies={"widget_dependency": Provide(some_widget_dependency)},
path="/widgets",
route_handlers=[get_widget, get_widget_list, _write_widget_router]
)
This provides a single "public" Router
instance with all the widget routes, all of which have access to the same core dependencies, but with the data-modifying routes also having auth applied. That composability is extremely powerful, and is less obvious if the "router" has to be introduced initially as a way to solve circular-import problems.
Litestar's approach also means it's easy to do things like register a single route multiple times, each with different configuration. Which enables use cases like:
- Different authentication/authorization schemes for each registration. For example, a data-editing route might be written once, and registered once under a router which applies API key auth for machine-to-machine requests, then registered again under a router which uses session auth for interaction by a human user.
- Different sets of dependencies for each registration. For example, a route which queries and returns a list of widgets might just declare that it accepts an argument of type
WidgetRepository
, and leave it up to the router configuration to decide whether to dependency-inject one that sees all widgets, or perhaps only a subset, or only those which are active, etc.
If you know what you're doing, you can emulate some of this in the FastAPI/Flask style of bound route registration, but the techniques you'll end up using for that feel to me like fighting against the framework, which is something I usually want to avoid.
Not to be too Pydantic
Pydantic is a popular package for defining schema objects which perform validation and serialization/deserialization, driven by Python type annotations, and one major use case for this is the input/output schemas of web applications. FastAPI appears to use Pydantic exclusively, which comes with both upsides and downsides. Pydantic is very useful and very powerful, of course, but it also means FastAPI is somewhat limited by what Pydantic can support: mostly, this is Pydantic's own classes, and the Python standard library's dataclasses
.
One crucial limitation is an inability to derive validation/serialization behavior directly from SQLAlchemy ORM classes, even though they both support a very similar type-annotation-based declaration format. Which means that to use SQLAlchemy with a Pydantic-only framework (and SQLAlchemy is basically the standard database toolkit and ORM for Python), you either have to write out the shape of your data multiple times-once for SQLAlchemy, and then at least one more time (possibly more than one time) for Pydantic-or turn to a third-party package to help bridge the gap. FastAPI's author worked around this by writing a new DB toolkit which combines SQLAlchemy and Pydantic, and pushing it in FastAPI's documentation.
Litestar, meanwhile, supports Pydantic, but is not tightly coupled to Pydantic, which gives a bit more flexibility. By default Litestar lets you define input/output schemas using Pydantic models, dataclasses
, or msgspec; ships with plugins to enable the use of attrs and of SQLAlchemy models; and provides a protocol for writing your own serialization plugins to extend support to other kinds of objects.
That's very convenient already, but the convenience is amplified by Litestar's system for automatically deriving data-transfer objects from data-access or domain objects. Suppose, for example, that we have the following SQLAlchemy model class:
from sqlalchemy.orm import DeclarativeBase, Mapped, mapped_column
Base = DeclarativeBase()
class Widget(Base):
__tablename__ = "widget"
id: Mapped[int] = mapped_column(primary_key=True)
internal_notes: Mapped[str]
sku: Mapped[str] = mapped_column(unique=True)
name: Mapped[str]
price_cents: Mapped[int]
In a Pydantic-only world, we'd need to write multiple Pydantic models representing different use cases:
- A "read" schema for use in HTTP responses, which would probably not include the
internal_notes
field and probably also notid
(sincesku
is more likely to be the public identifier) - A "write" schema for creating widgets, which would exclude
id
since that likely is auto-generated on insert - Another "write" schema for updating widgets, setting all fields optional to allow partial update
As well as possibly more schemas like an admin-view "read" schema that does include the internal fields, etc. Even if you get clever and use inheritance to share field definitions among all these Pydantic classes, you still will write out the full set of fields for widgets at least twice, and the second time it will be fragmented across multiple Pydantic classes, creating the risk of making a change to the ORM model and forgetting to update all the corresponding field definitions in the Pydantic models.
Litestar's approach is a significant improvement on this. For example, here's how to use Litestar's DTO helpers to define the "read" schema:
from litestar.dto import DTOConfig
from litestar.plugins.sqlalchemy import SQLAlchemyDTO
class ReadWidget(SQLAlchemyDTO[Widget]):
config = DTOConfig(exclude={"id", "internal_notes"})
This will give you a DTO class containing all the fields of the Widget
ORM model except the two explicitly excluded, and will derive that set of fields, and the correct data types, from introspecting Widget
. It will also automatically handle conversion to and from instances of Widget
when you specify it as the input or return DTO type of a route. Similarly, it's possible to declare a list of fields to include, or to re-map field names for public consumption, or to declare a DTO which makes fields optional for partial updates. This means there's only one canonical definition of the fields-on the original class, which might be a SQLAlchemy ORM model, might be a dataclass
, etc.-and it doesn't have to be repeated in the DTOs because the DTOs will always derive their field definitions directly from the source class you point them at.
Of course, there are going to be cases where your DTOs are sufficiently different from your DAOs and domain objects that this isn't a big help, but my own experience is that "the DTO is a subset of the DAO's fields" is extremely common in real-world applications, so Litestar's approach really pays off in both reduced boilerplate and reduced errors from manual "transcription" of fields between different class definitions.
Alchemical architecture
I wasn't exaggerating earlier when I said that SQLAlchemy is the database toolkit and ORM for Python. While there are others out there, the only one I'm aware of that sees anything close to SQLAlchemy's usage is the Django ORM, and only because it's built into and tightly integrated with Django. So if you're going to be writing a database-backed web application in Python, and you're not doing Django, you are almost certainly going to be using SQLAlchemy.
And Litestar makes that easy. While officially remaining agnostic as to whether you even have a persistence layer, it still includes good integrations for SQLAlchemy: the serialization plugin mentioned earlier allows the direct use of SQLAlchemy ORM classes as input and output schemas; the DTO helpers can derive subsets and remappings of fields from SQLAlchemy ORM classes; and Litestar also ships with a plugin that manages a SQLAlchemy engine and per-request ORM session for you, as well as a single SQLAlchemy mega-plugin combining all the SQLAlchemy plugins' functionality.
So it's already pretty convenient to use SQLAlchemy in Litestar applications. But there's more! The Litestar team also maintains the excellent Advanced Alchemy library which provides a bunch of useful features on top of SQLAlchemy. While Advanced Alchemy is framework-agnostic, Litestar's SQLAlchemy plugin makes use of it and re-exports much of its functionality, giving you access to it automatically, and it does include Litestar-specific helpers for registering certain utility classes with Litestar's dependency injection.
Advanced Alchemy provides a lot of quality-of-life improvements for SQLAlchemy, including a variety of base classes and mixins and data types doing useful things like database-agnostic big-integer primary keys, automatic create/update timestamps, UUID-keyed models, a proper UTC timestamp type, and a JSON type which chooses the best column type for your database. There are also command-line helpers for database management (including creating and working with Alembic migrations), database dumping and seeding to/from JSON, and a lot more.
But the place where Advanced Alchemy really shines is in providing a generic repository implementation (both sync and async flavors) on top of SQLAlchemy models, along with a service-layer abstraction and helpers to integrate them into Litestar's dependency injection system.
Here's a basic example using the Widget
class from above:
from litestar.plugins.sqlalchemy import repository
class WidgetRepository(repository.SQLAlchemyAsyncRepository[Widget]):
model_type = Widget
WidgetRepository
will have all the methods you'd expect-list()
, get_one()
, add()
, delete()
, etc.-automatically derived from the Widget
model. And let me just say that having repository implementations automatically derived from any SQLAlchemy model, with not just basic CRUD operations but also things like paginated fetches, is a massive productivity boost compared to just using vanilla SQLAlchemy. It's maybe not quite on the level of Django's generic views, but it's a big step in that direction, and you probably could produce something like Django's generic views with Litestar and Advanced Alchemy if you wanted to (perhaps one day, in my copious free time, I'll even make an attempt at it).
I know it may seem strange to hear me saying this, since a few years ago I went on record as being strongly against these sorts of abstractions-specifically service layers-in Django. And I still think you absolutely should not try to retrofit repository or service-layer abstractions onto Django! They're not the native patterns of Django's architecture, and instead I think you should stick to what I recommended back then, which is to leverage Django's own architecture, especially its "manager" abstraction, rather than trying to force abstractions onto it that don't fit.
I also still think there are a lot of bad use cases for repositories and service layers that people should avoid, but that's a digression which should probably become its own post, so I'll just say for now that I think it's fine to use repositories and service layers as an organizing principle when you're using a less-structured framework which doesn't express strong opinions about how you should lay out your code. And that's exactly what I do when working with Litestar.
A lightweight star of Python
There are plenty of other features and conveniences in Litestar, many of which I use daily. Its auth system, supporting both simple guard functions and middlewares for attaching identity and complex authn/authz logic to requests. Its "stores" framework, which makes caching and similar tasks convenient. Its logging integrations which support both the Python standard library's logging
module and popular third-party tools like structlog
. Its built-in support for transforming errors to standard "problem details" structures. Its built-in support for recording and exporting metrics in standard Prometheus or OpenTelemetry formats. Its htmx support.
You can do this stuff in other microframeworks, but it typically involves a lot of tracking down of third-party add-ons and/or writing your own glue code to integrate things. Litestar manages to keep the "microframework" feel when starting a new project while also having all these nice bits optionally available with the framework itself when and if you decide you want them, and that's nothing to sneeze at. That's what I was getting at earlier when I said it reminds me of the things I like in certain frameworks from other languages. Litestar doesn't feel, to me, like it's trying to be a replacement for any pre-existing Python web framework. It's not trying to be the next Django or the next Flask or whatever; instead, it feels to me like a Pythonic take on the good parts of something like Spring Boot (and the way I like to set it up, doing things like using svcs behind the scenes as a service locator to feed things to both Litestar's and pytest's dependency injection, makes it feel even more that way).
I could go on for a lot longer listing things I like about Litestar, and probably wind up way too far into my own subjective preferences, but hopefully I've given you enough of a realistic taste of what it offers that, next time you're about to build a Python web app, you might decide to reach for π‘β to carry you to the moon πππ.
07 Aug 2025 12:25am GMT
02 Aug 2025
Django community aggregator: Community blog posts
Announcing django-generic-notifications 1.0.0
Back in 2011, I started a small Django package called django-generic-notifications. It was built for a project I was working on at the time, got seven releases over a few months⦠and then it more or less died. Once I moved on from that original project, there wasn't much reason to keep maintaining the library. It never gained a big user base, no pull requests or issues came in, and eventually I archived the repository.
Fast forward to a few weeks ago, and I found myself needing a good, flexible notification system for a new Django project. I checked out a few third-party options, but none of them quite fit what I had in mind. I wasn't super eager to revive django-generic-notifications - it was very old, still using South for migrations (yes, that old) - but in the end, I decided to bring it back to life. Or rather, to start fresh.
So here it is: version 1.0.0 of django-generic-notifications. A complete rewrite, with the same core architecture but a modern, cleaned-up implementation. It's more flexible, more powerful, and a lot more useful.
What is django-generic-notifications?
At its core, this package helps you send notifications to your users through different channels like email or your website. It's built around the idea of defining notification types in your code, and then letting the library handle how and when to deliver them to your users.
Want to show a bell icon on your website when a user gets a new comment? Easy. Want to send them a weekly email digest with grouped notifications? Also easy. Want to build your own Slack or push notification integration? Go for it.
Highlights
- Channels: The old concept of "backends" is now called "channels", and we ship two out of the box:
website
andemail
. - Website notifications: Finally a built-in way to show notifications on your site. The old version never included this, which in hindsight is kind of wild.
- Email digests: Daily or weekly summaries of all pending notifications, grouped and nicely formatted.
- Notification grouping: Avoid spamming users with multiple similar notifications: group them together automatically.
- Simplified internals: No more built-in queuing system, no assumptions about how your custom channels should process notifications. Just plug in your own logic.
- Customizable: Choose which channels a notification must go through and build your own delivery logic if needed.
- Tested: Yes, we have unit tests now π
- Example project: Included in the repo to help you get started quickly.
Get started
Check out the project on GitHub: github.com/loopwerk/django-generic-notifications.
The README walks you through installation, configuration, and how to define your own notification types. You can be up and running in just a few minutes. Let me know how you like it!
02 Aug 2025 7:09pm GMT
Solving bots registration problem by βreinventing the wheelβ
<![CDATA[ Solving bots registration problem by "reinventing the wheel"
Ever since I launched ImpressKit in 2021 I had issues with spammy bot registrations. Usually at least a few times a month these would be like twenty new user accounts that were obviously bots.
I figured that it just a cost of having product with registration but over the years I got more and more annoyed. It messed up my stats dashboard, I was sending emails to weird addresses and I think I even got complaint from someone saying the did not register after receiving a login confirmation email.
Some months ago I finally had enough and decided to try to tackle this. I did not want to use 3rd party captcha because these are annoying and it is another part that I would need to monitor and possibly updateβ¦
But what should I use? After some thinking I arrived at solution that seemed both simple and like it could work. I added required "questionnaire" to the register form asking the prospective user what the project is about. I made sure the correct answer wasn't the first radio button.
When incorrect option is selected, then the form just redirects to new page explaining that the answer is incorrect and no user account is created.
The results?
I am happy to report that ever since deploying this I did not spot any suspicious new account registration. Even though the "captcha" is super easy to defeat - I don't think anyone is going to create custom bot behavior for my small project.
]]
02 Aug 2025 2:28pm GMT
01 Aug 2025
Django community aggregator: Community blog posts
Django News - Django and AI - Aug 1st 2025
News
Djangonaut Session 5 - Officer and Organizer Interest
A form to gauge returning officer and session organizer interest for the next session!
PyPI Users Email Phishing Attack - The Python Package Index Blog
Phishing emails exploit PyPI package metadata by directing users to fake login pages that mimic PyPI, prompting developers to verify URLs and update passwords.
Djangonaut Space is looking for contributors to be mentors
Posted by Djangonaut Space session organizers on July 31, 2025
Updates to Django
Today 'Updates to Django' is presented by Pradhvan from the Djangonaut Space! π
Last week we had 16 pull requests merged into Django by 11 different contributors including 3 first-time contributors! Congratulations to Anthony Sottile, Mohamed Amine Mahmoud and Take Weiland for having their first commits merged into Django, welcome on board! π
This week's Django highlights π
Migrated django.core.mail to Python's current email API, replacing legacy email handling with modern implementation.
Added hints support for PostgreSQL contrib operations, enabling database routers to make informed decisions in multi-database migration scenarios.
Introduced accessibility guidelines and established accessibility standards that conform with the Web Content Accessibility Guidelines (WCAG) standards and along with best practices from Authoring Tool Accessibility Guidelines (ATAG).
Improved GEOS error messages and tests, enhancing developer experience with better error messages.
That's all for this week in Django development! ππ¦
Django Newsletter
Wagtail CMS
AI in the CMS: steering the ecosystem
Wagtail introduces opt-in, ethical AI enhancements that leverage Django's ecosystem for contextual alt text, content summarization, and improved admin customization.
20,000 Django packages
Detailed PyPI analysis reveals over 23,000 Django packages with active releases, while Wagtail leverages both generic and specialized packages to boost development.
Articles
Is AI Leaving the Python Community Behind?
Even as AI-focused talks dominate Python conferences, there is a corresponding lack of support from AI companies in the Python communities.
Django: split `ModelAdmin.get_queryset()` by view
Customizing get_queryset per Django admin view using resolver_match.func.name enables precise database query optimizations that reduce unnecessary joins and improve performance.
AI and Me: A Developer's Perspective
Reflections on using AI in software development-from autocomplete gripes to Claude Code breakthroughs, and why agents need seniors, not sorcery.
Our tools are still not designed for the AI future
The article explores an AI-first IDE paradigm that prioritizes agent workflows and spec-driven development to transform coding and code review processes.
Why We Need to Stop Fighting About AI Tools and Start Teaching Them
LLM-based coding agents streamline boilerplate production so that Django developers can concentrate on building innovative architecture and solving complex design challenges.
Loopwerk: Why Django's DATETIME_FORMAT ignores you (and how to fix it)
Django admin defaults to 12-hour clocks due to locale precedence, but developers can enforce 24-hour time by configuring FORMAT_MODULE_PATH
and translation overrides.
From Not Knowing Programming Was a Job to GSoC and Contributing to Django Core
Contributed inline template partials to Django core for improved component reuse and rendering via HTMX amid a journey from novice programmer to GSoC participant.
Thoughts on HTML mails
Implement HTML notification emails using Django send_mail to deliver visually appealing, skimmable messages with a clear call-to-action and enhanced accessibility.
I was wrong about robots.txt
A good lesson about unintended consequences that may come out of blocking all UserAgents in your robots.txt file.
Events
The DjangoCon US 2025 schedule has been released!
DjangoCon US 2025 schedule includes diverse Django talks covering AI deployment, project maintainability, high performance techniques, and MongoDB integration among other topics.
Getting to know folks: A pre-conference interview
DjangoCon US 2025 pre-conference interview highlights community diversity, organizer insights, and speaker experiences to foster mentorship and global excitement for Django development.
DjangoCon Videos
One Thousand and One Django Sites with Vince Salvino
Told in the storybook style of "One Thousand and One Nights" (a.k.a. "Arabian Nights") this talk will feature real-world stories about strategies and challenges we have encountered along our journey of hosting over 1,000 Django/Wagtail sites.
Django Admin at Scale: From Milliseconds to Microseconds with Sumit Singh
Is your Django Admin crawling with millions of records? Join me in exploring battle-tested optimization techniques that transform Django Admin from sluggish to lightning-fast. From smart queries to caching strategies, you'll learn practical solutions to scale your admin interface while keeping it user-friendly. Performance isn't just a feature-it's a necessity!
Just-in-Time Development with Django and HTMX: Faster, Leaner, and Smarter with Thomas De Bonnet
Learn how Just-in-Time development with Django & HTMX can make your apps faster and more efficient! Fetch only what users need, when they need it-like loading dropdown data on hover or filling a modal when opened. Boost performance, save resources, and enhance UX!
Sponsored Link 2
AI-Powered Django Development & Consulting
REVSYS specializes in seamlessly integrating powerful AI technologies, including GPT-4, into your existing Django applications. Your Django project deserves modern, intelligent features that enhance user engagement and streamline content workflows.
Podcasts
Abstractions: Semi-Coherent Output
Abstractions discusses evolving AI challenges and layered technologies affecting software development dynamics relevant to modern Django application practices.
Django News Jobs
Senior Backend Engineer at Prowler
Backend Engineer at 7Learnings
Django Newsletter
Projects
simonw/simonwillisonblog
The source code behind Simon Willison's high-traffic personal site.
ephes/django-indieweb
Just some of the indieweb endpoints for Django.
Sponsorship
Lightning-Fast Python: Mastering the uv Package Manager - Livestream on August 7th
Register for an upcoming livestream featuring Michael Kennedy, host of Talk Python to Me and Python Bytes podcasts, on August 7th at 11am Eastern Daylight Time (UTC-4).
This RSS feed is published on https://django-news.com/. You can also subscribe via email.
01 Aug 2025 3:00pm GMT
Python and AI workflow with LangGraph
In this stream, I worked on a personal AI workflow that I'm building using LangGraph. I discussed human-in-the-loop and how to bring a person into the workflow process.
01 Aug 2025 5:00am GMT