The Django webloghttps://www.djangoproject.com/weblog/Latest news about Django, the Python web framework.enMon, 30 Jun 2025 05:00:00 -0500Our 2024 Annual Impact Reporthttps://www.djangoproject.com/weblog/2025/jun/30/django-2024-annual-impact-report/<p>Django has always been more than just a web framework; it’s a testament to what a dedicated community can build together. Behind every Django release, bug fix, or DjangoCon is a diverse network of people working steadily to strengthen our open-source ecosystem. To celebrate our collective effort, the Django Software Foundation (DSF) is excited to share our <a href="https://www.djangoproject.com/foundation/reports/2024/">2024 Annual Impact Report</a> 🎉</p> <p>In this report, you’ll discover key milestones, narratives of community folks, the impact of the events running throughout the year, and much more, ramping up to how we’re laying the groundwork for an even more resilient and inclusive Django community.</p> <p><a class="cta" href="https://www.djangoproject.com/foundation/reports/2024/">2024 Annual Impact Report</a></p> <h3 id="s-why-we-publish-this-report">Why we publish this report</h3> <p>Transparency is essential for our community-driven organization. Everyone deserves to know how our work and investments translate into real impact. It’s more than just statistics. It’s our way to:</p> <ul> <li><strong>Show how your contributions make a difference</strong>, with vibrant highlights from the past year. </li> <li><strong>Reflect on community progress</strong>, recognizing the people and ideas that keep Django thriving.</li> <li><strong>Invite more individuals and organizations to get involved</strong>.</li> </ul> <h3 id="s-looking-ahead-call-to-action">Looking ahead: call to action</h3> <p>As we make progress through 2025, the Django Software Foundation remains dedicated to strengthening the ecosystem that supports developers, contributors, and users around the world. With a growing network of working groups, community initiatives, and the commitment of volunteers, we’re focused on nurturing the people and executing ideas that make Django what it is: <em>the web framework for perfectionists with deadlines</em>. </p> <p>Help keep this momentum strong by supporting Django through any of the following ways:</p> <ul> <li><a href="https://www.djangoproject.com/fundraising/">Donate to Django</a> to support future development</li> <li>Convince your company to <a href="https://www.djangoproject.com/foundation/corporate-members/">become a Corporate Member</a></li> <li>Join the Foundation as an <a href="https://www.djangoproject.com/foundation/individual-members/">Individual Member</a></li> <li>Get involved with the <a href="https://github.com/django/dsf-working-groups">working groups</a></li> <li>Join our community on the <a href="https://forum.djangoproject.com/">Django Forum</a> or <a href="https://chat.djangoproject.com/">Discord server</a>.</li> <li>Follow and re-share our posts <a href="https://fosstodon.org/@django">on Mastodon</a>, <a href="https://bsky.app/profile/djangoproject.com">on Bluesky</a>, or <a href="https://x.com/djangoproject">on X</a>.</li> <li>Follow <a href="https://www.linkedin.com/company/django-software-foundation/">our page on LinkedIn</a>.</li> </ul> <p><a class="cta" href="https://www.djangoproject.com/fundraising">Donate to Django</a></p> <p>Thank you, everyone, for your dedication and efforts. Here’s to another year of collaboration, contribution, and shared success!</p> <hr> <p>Any feedback or questions about our report? Come say hi in our <a href="https://forum.djangoproject.com/t/our-2024-annual-impact-report/41622">feedback thread on the Django forum</a>.</p>Priya PahwaMon, 30 Jun 2025 05:00:00 -0500https://www.djangoproject.com/weblog/2025/jun/30/django-2024-annual-impact-report/Watch the DjangoCon Europe 2025 talkshttps://www.djangoproject.com/weblog/2025/jun/27/watch-the-djangocon-europe-2025-talks/<p>They’re now all available to watch on YouTube, with a dedicated playlist ⭐️ <a href="https://www.youtube.com/watch?v=6XqVXwsBRCQ&amp;list=PLY_che_OEsX19RNHHqERLODTtYs1YAtMA&amp;index=1">DjangoCon Europe 2025 Dublin</a>. For more quality Django talks in 2025, check out our next upcoming events!</p> <ul class="list-events"> <li> <a href="https://2025.djangocon.africa" target="_blank">DjangoCon Africa 2025</a> <span class="meta"><span>Aug. 11, 2025</span> | Arusha, Tanzania 🇹🇿</span> <i class="icon icon-calendar"></i> </li> <li> <a href="https://2025.djangocon.us" target="_blank">DjangoCon US 2025</a> <span class="meta"><span>Sept. 8, 2025</span> | Chicago, Illinois, USA 🇺🇸</span> <i class="icon icon-calendar"></i> </li> <li> <a href="https://2025.djangoday.in/" target="_blank">DjangoDay India</a> <span class="meta"><span>Nov. 2, 2025 (tentative)</span> | Bangalore, India 🇮🇳</span> <i class="icon icon-calendar"></i> </li> </ul> <h4 id="s-all-the-djangocon-europe-talks">All the DjangoCon Europe talks</h4> <div style="display: grid; text-align: center; gap: 1rem; row-gap: 3vw; margin-top: 3vw; grid-template-columns: repeat(auto-fit, minmax(min-content, 320px));"> <a href="https://www.youtube.com/watch?v=6XqVXwsBRCQ&list=PLY_che_OEsX19RNHHqERLODTtYs1YAtMA&index=1&pp=iAQB"> <figure> <img src="https://i.ytimg.com/vi_webp/6XqVXwsBRCQ/sddefault.webp" width="256" height="192" alt="" loading="lazy"> <figcaption>Welcome Session</figcaption> </figure> </a> <a href="https://www.youtube.com/watch?v=MBqampUQ1o0&list=PLY_che_OEsX19RNHHqERLODTtYs1YAtMA&index=2&pp=iAQB"> <figure> <img src="https://i.ytimg.com/vi_webp/MBqampUQ1o0/sddefault.webp" width="256" height="192" alt="" loading="lazy"> <figcaption>Keynote: Django needs you! (to do code review)</figcaption> </figure> </a> <a href="https://www.youtube.com/watch?v=JFNxRzlq_V0&list=PLY_che_OEsX19RNHHqERLODTtYs1YAtMA&index=3&pp=iAQB"> <figure> <img src="https://i.ytimg.com/vi_webp/JFNxRzlq_V0/sddefault.webp" width="256" height="192" alt="" loading="lazy"> <figcaption>End-to-end testing Django applications using Pytest with Playwright</figcaption> </figure> </a> <a href="https://www.youtube.com/watch?v=kZ4q0k_FNhw&list=PLY_che_OEsX19RNHHqERLODTtYs1YAtMA&index=4&pp=iAQB"> <figure> <img src="https://i.ytimg.com/vi_webp/kZ4q0k_FNhw/sddefault.webp" width="256" height="192" alt="" loading="lazy"> <figcaption>Turn back time: Converting integer fields to bigint using Django migrations at scale</figcaption> </figure> </a> <a href="https://www.youtube.com/watch?v=L7WXcWuH21U&list=PLY_che_OEsX19RNHHqERLODTtYs1YAtMA&index=5&pp=iAQB"> <figure> <img src="https://i.ytimg.com/vi_webp/L7WXcWuH21U/sddefault.webp" width="256" height="192" alt="" loading="lazy"> <figcaption>Data-Oriented Django Drei</figcaption> </figure> </a> <a href="https://www.youtube.com/watch?v=r5c_gxuMcL4&list=PLY_che_OEsX19RNHHqERLODTtYs1YAtMA&index=6&pp=iAQB"> <figure> <img src="https://i.ytimg.com/vi_webp/r5c_gxuMcL4/sddefault.webp" width="256" height="192" alt="" loading="lazy"> <figcaption>The fine print in Django release notes</figcaption> </figure> </a> <a href="https://www.youtube.com/watch?v=SDuqa82nx90&list=PLY_che_OEsX19RNHHqERLODTtYs1YAtMA&index=7&pp=iAQB"> <figure> <img src="https://i.ytimg.com/vi_webp/SDuqa82nx90/sddefault.webp" width="256" height="192" alt="" loading="lazy"> <figcaption>Django + HTMX: Patterns to Success</figcaption> </figure> </a> <a href="https://www.youtube.com/watch?v=lYZHDP0j1Qw&list=PLY_che_OEsX19RNHHqERLODTtYs1YAtMA&index=8&pp=iAQB"> <figure> <img src="https://i.ytimg.com/vi_webp/lYZHDP0j1Qw/sddefault.webp" width="256" height="192" alt="" loading="lazy"> <figcaption>How to solve a Python mystery</figcaption> </figure> </a> <a href="https://www.youtube.com/watch?v=fjcNUE_7meU&list=PLY_che_OEsX19RNHHqERLODTtYs1YAtMA&index=9&pp=iAQB"> <figure> <img src="https://i.ytimg.com/vi_webp/fjcNUE_7meU/sddefault.webp" width="256" height="192" alt="" loading="lazy"> <figcaption>Bulletproof Data Pipelines: Django, Celery, and the Power of Idempotency</figcaption> </figure> </a> <a href="https://www.youtube.com/watch?v=PV7JFQPYQyI&list=PLY_che_OEsX19RNHHqERLODTtYs1YAtMA&index=10&pp=iAQB"> <figure> <img src="https://i.ytimg.com/vi_webp/PV7JFQPYQyI/sddefault.webp" width="256" height="192" alt="" loading="lazy"> <figcaption>Logs, shells, caches and other strange words we use daily</figcaption> </figure> </a> <a href="https://www.youtube.com/watch?v=ro_LaEe9w1A&list=PLY_che_OEsX19RNHHqERLODTtYs1YAtMA&index=11&pp=iAQB"> <figure> <img src="https://i.ytimg.com/vi_webp/ro_LaEe9w1A/sddefault.webp" width="256" height="192" alt="" loading="lazy"> <figcaption>Day 1 Lightning Talks</figcaption> </figure> </a> <a href="https://www.youtube.com/watch?v=BUOIPfWjoYM&list=PLY_che_OEsX19RNHHqERLODTtYs1YAtMA&index=13&pp=iAQB"> <figure> <img src="https://i.ytimg.com/vi_webp/BUOIPfWjoYM/sddefault.webp" width="256" height="192" alt="" loading="lazy"> <figcaption>How to Enjoy Debugging in Production</figcaption> </figure> </a> <a href="https://www.youtube.com/watch?v=PBbM9pH8rQY&list=PLY_che_OEsX19RNHHqERLODTtYs1YAtMA&index=14&pp=iAQB"> <figure> <img src="https://i.ytimg.com/vi_webp/PBbM9pH8rQY/sddefault.webp" width="256" height="192" alt="" loading="lazy"> <figcaption>KEYNOTE: The Most Bizarre Software Bugs in History</figcaption> </figure> </a> <a href="https://www.youtube.com/watch?v=V28q8n3MBbk&list=PLY_che_OEsX19RNHHqERLODTtYs1YAtMA&index=15&pp=iAQB"> <figure> <img src="https://i.ytimg.com/vi_webp/V28q8n3MBbk/sddefault.webp" width="256" height="192" alt="" loading="lazy"> <figcaption>Passkeys in Django: the best of all possible worlds</figcaption> </figure> </a> <a href="https://www.youtube.com/watch?v=ZxwP7xzYz1k&list=PLY_che_OEsX19RNHHqERLODTtYs1YAtMA&index=16&pp=iAQB"> <figure> <img src="https://i.ytimg.com/vi_webp/ZxwP7xzYz1k/sddefault.webp" width="256" height="192" alt="" loading="lazy"> <figcaption>How we make decisions in Django</figcaption> </figure> </a> <a href="https://www.youtube.com/watch?v=TUv9u8Uc_uk&list=PLY_che_OEsX19RNHHqERLODTtYs1YAtMA&index=17&pp=iAQB"> <figure> <img src="https://i.ytimg.com/vi_webp/TUv9u8Uc_uk/sddefault.webp" width="256" height="192" alt="" loading="lazy"> <figcaption>100 Million Parking Transactions Per Year with Django</figcaption> </figure> </a> <a href="https://www.youtube.com/watch?v=XlLpngKyV-c&list=PLY_che_OEsX19RNHHqERLODTtYs1YAtMA&index=18&pp=iAQB"> <figure> <img src="https://i.ytimg.com/vi_webp/XlLpngKyV-c/sddefault.webp" width="256" height="192" alt="" loading="lazy"> <figcaption>One more time about µDjango</figcaption> </figure> </a> <a href="https://www.youtube.com/watch?v=dVqzyd1jXF0&list=PLY_che_OEsX19RNHHqERLODTtYs1YAtMA&index=19&pp=iAQB"> <figure> <img src="https://i.ytimg.com/vi_webp/dVqzyd1jXF0/sddefault.webp" width="256" height="192" alt="" loading="lazy"> <figcaption>Steering Council introduction</figcaption> </figure> </a> <a href="https://www.youtube.com/watch?v=jLiUkxYanBc&list=PLY_che_OEsX19RNHHqERLODTtYs1YAtMA&index=20&pp=iAQB"> <figure> <img src="https://i.ytimg.com/vi_webp/jLiUkxYanBc/sddefault.webp" width="256" height="192" alt="" loading="lazy"> <figcaption>Supporting Adult Career Switchers: The Unbootcamp Method</figcaption> </figure> </a> <a href="https://www.youtube.com/watch?v=l1xi_yKnhbE&list=PLY_che_OEsX19RNHHqERLODTtYs1YAtMA&index=21&pp=iAQB"> <figure> <img src="https://i.ytimg.com/vi_webp/l1xi_yKnhbE/sddefault.webp" width="256" height="192" alt="" loading="lazy"> <figcaption>How to get Foreign Keys horribly wrong in Django</figcaption> </figure> </a> <a href="https://www.youtube.com/watch?v=kdLG5Z2PgsQ&list=PLY_che_OEsX19RNHHqERLODTtYs1YAtMA&index=22&pp=iAQB"> <figure> <img src="https://i.ytimg.com/vi_webp/kdLG5Z2PgsQ/sddefault.webp" width="256" height="192" alt="" loading="lazy"> <figcaption>Zango: Accelerating Business App Development with an Opinionated Django Meta</figcaption> </figure> </a> <a href="https://www.youtube.com/watch?v=wY7dpWmUQAY&list=PLY_che_OEsX19RNHHqERLODTtYs1YAtMA&index=23&pp=iAQB"> <figure> <img src="https://i.ytimg.com/vi_webp/wY7dpWmUQAY/sddefault.webp" width="256" height="192" alt="" loading="lazy"> <figcaption>Dynamic models without dynamic models</figcaption> </figure> </a> <a href="https://www.youtube.com/watch?v=jh3mSYoQCJY&list=PLY_che_OEsX19RNHHqERLODTtYs1YAtMA&index=24&pp=iAQB"> <figure> <img src="https://i.ytimg.com/vi_webp/jh3mSYoQCJY/sddefault.webp" width="256" height="192" alt="" loading="lazy"> <figcaption>Evolving Django: What We Learned by Integrating MongoDB</figcaption> </figure> </a> <a href="https://www.youtube.com/watch?v=gt19rN6HS6M&list=PLY_che_OEsX19RNHHqERLODTtYs1YAtMA&index=25&pp=iAQB"> <figure> <img src="https://i.ytimg.com/vi_webp/gt19rN6HS6M/sddefault.webp" width="256" height="192" alt="" loading="lazy"> <figcaption>Feature Flags: Deploy to some of the people all of the time, and all of the</figcaption> </figure> </a> <a href="https://www.youtube.com/watch?v=80RKCH2PmEU&list=PLY_che_OEsX19RNHHqERLODTtYs1YAtMA&index=12&pp=iAQB0gcJCcMJAYcqIYzv"> <figure> <img src="https://i.ytimg.com/vi_webp/80RKCH2PmEU/sddefault.webp" width="256" height="192" alt="" loading="lazy"> <figcaption>Day 2 Lightning Talks</figcaption> </figure> </a> <a href="https://www.youtube.com/watch?v=XJLvovUVlhw&list=PLY_che_OEsX19RNHHqERLODTtYs1YAtMA&index=26&pp=iAQB"> <figure> <img src="https://i.ytimg.com/vi_webp/XJLvovUVlhw/sddefault.webp" width="256" height="192" alt="" loading="lazy"> <figcaption>KEYNOTE: Django for Data Science: Deploying Machine Learning Models with Django</figcaption> </figure> </a> <a href="https://www.youtube.com/watch?v=ZfSZWPcYWXw&list=PLY_che_OEsX19RNHHqERLODTtYs1YAtMA&index=27&pp=iAQB"> <figure> <img src="https://i.ytimg.com/vi_webp/ZfSZWPcYWXw/sddefault.webp" width="256" height="192" alt="" loading="lazy"> <figcaption>The incredible Djangonaut Space project</figcaption> </figure> </a> <a href="https://www.youtube.com/watch?v=4wfS5QFFT3s&list=PLY_che_OEsX19RNHHqERLODTtYs1YAtMA&index=28&pp=iAQB0gcJCcMJAYcqIYzv"> <figure> <img src="https://i.ytimg.com/vi_webp/4wfS5QFFT3s/sddefault.webp" width="256" height="192" alt="" loading="lazy"> <figcaption>Anatomy of a Database Operation</figcaption> </figure> </a> <a href="https://www.youtube.com/watch?v=lL2RtVlaafQ&list=PLY_che_OEsX19RNHHqERLODTtYs1YAtMA&index=29&pp=iAQB"> <figure> <img src="https://i.ytimg.com/vi_webp/lL2RtVlaafQ/sddefault.webp" width="256" height="192" alt="" loading="lazy"> <figcaption>One Thousand and One Django Sites</figcaption> </figure> </a> <a href="https://www.youtube.com/watch?v=rMyZycsYdKk&list=PLY_che_OEsX19RNHHqERLODTtYs1YAtMA&index=30&pp=iAQB"> <figure> <img src="https://i.ytimg.com/vi_webp/rMyZycsYdKk/sddefault.webp" width="256" height="192" alt="" loading="lazy"> <figcaption>Django Admin at Scale: From Milliseconds to Microseconds 🚀</figcaption> </figure> </a> <a href="https://www.youtube.com/watch?v=aG6m1n7zDbY&list=PLY_che_OEsX19RNHHqERLODTtYs1YAtMA&index=31&pp=iAQB"> <figure> <img src="https://i.ytimg.com/vi_webp/aG6m1n7zDbY/sddefault.webp" width="256" height="192" alt="" loading="lazy"> <figcaption>Just-in-Time Development with Django and HTMX: Faster, Leaner, and Smarter</figcaption> </figure> </a> <a href="https://www.youtube.com/watch?v=jrDCmuD9cBc&list=PLY_che_OEsX19RNHHqERLODTtYs1YAtMA&index=32&pp=iAQB0gcJCcMJAYcqIYzv"> <figure> <img src="https://i.ytimg.com/vi_webp/jrDCmuD9cBc/sddefault.webp" width="256" height="192" alt="" loading="lazy"> <figcaption>Europe, Django and two-factor authentication</figcaption> </figure> </a> <a href="https://www.youtube.com/watch?v=U-i2XWCQ14A&list=PLY_che_OEsX19RNHHqERLODTtYs1YAtMA&index=33&pp=iAQB"> <figure> <img src="https://i.ytimg.com/vi_webp/U-i2XWCQ14A/sddefault.webp" width="256" height="192" alt="" loading="lazy"> <figcaption>Closing session</figcaption> </figure> </a> <a href="https://www.youtube.com/watch?v=Nim1zHomTU0&list=PLY_che_OEsX19RNHHqERLODTtYs1YAtMA&index=34&pp=iAQB"> <figure> <img src="https://i.ytimg.com/vi_webp/Nim1zHomTU0/sddefault.webp" width="256" height="192" alt="" loading="lazy"> <figcaption>Day 3 Lightning Talks</figcaption> </figure> </a> </div>Thibaud ColasFri, 27 Jun 2025 01:51:21 -0500https://www.djangoproject.com/weblog/2025/jun/27/watch-the-djangocon-europe-2025-talks/DSF member of the month - Elena Williamshttps://www.djangoproject.com/weblog/2025/jun/17/dsf-member-of-the-month-elena-williams/<p>For June 2025, we welcome Elena Williams as our DSF member of the month! ⭐</p> <p><img alt="Elena in DjangoGirls Brisbane" src="https://media.djangoproject.com/blog/images/2025/06/elena.png"></p> <p>Elena is a dedicated member of the Django community. She is part of the Code of Conduct Working Group and she is a Django Girls organizer in Australia. She has been a DSF member since July 2014. <br> You can learn more about Elena by visiting <a href="https://elena.github.io/">Elena's website</a> and <a href="https://github.com/elena/">her GitHub Profile</a>.</p> <p>Let’s spend some time getting to know Elena better!</p> <h4 id="s-can-you-tell-us-a-little-about-yourself-hobbies-education-etc">Can you tell us a little about yourself (hobbies, education, etc)</h4> <p>My background is that I was always interested in computers, though my parents were more creative types, my Dad was an Architect (of built structures). When I was a kid we had computers for CAD around the house before it was common. I was always into STEM subjects, but unfortunately in that era for girls to do engineering it was a bit too hostile for me, so I trained in finance instead and worked in that industry (finance and banking, MNE orgs) for nearly a decade. I kept coming back to coding and was always building computers, and was obsessed with the internet as a technology from pretty early on. Just after I discovered Django I did a Masters in Computing at ANU. To this day my main hobbies are programming/webdev (very much a person who codes for fun) and the open source community. My persistent other hobbies are hackspace activities, I like CNC and laser stuff, but will pick up any and all tools/mediums and give them a go, lately been spending time with blender and cabinetry. When I can, I like to get away to snowboard or kitesurf, and this wild Australian long distance endurance navigation sport called rogaining. Really at the moment I’m mostly focussed on being a parent (which is an awesome experience), my friends (mostly python related), my job and working on the community here in Australia. With my family we go camping/hiking more than most. I’ve also continued to be a sessional academic at ANU teaching software engineering for many years. </p> <h4 id="s-how-did-you-start-using-django">How did you start using Django?</h4> <p>Golly, I’ve been using Django forever. I’d started doing web stuff in the early ‘00s and worked in a range of languages and paradigms. I was working in a physics research institute at a high profile university in Australia doing web stuff and made friends with a bunch of the doctoral students. In around 2007, one of these students, and my good mate, Dave, randomly recommended this new framework Django and Python (and emacs also actually but that’s a different story). Basically I got into it immediately and never looked back and went on to build a career around Django (actually Dave later gave up physics and did the same thing too). I’ve been engaged with the Python and Django communities to varying degrees since about 2011 as well. To be honest when I discovered the language and the framework I really didn’t expect to still be passionate about them all these years later but I really am! Hopefully I can continue to be well into the future also.</p> <h4 id="s-what-other-framework-do-you-know-and-if-there-is-anything-you-would-like-to-have-in-django-if-you-had-magical-powers">What other framework do you know and if there is anything you would like to have in Django if you had magical powers?</h4> <p>Over the years (being a curious person) I’ve worked with many many web frameworks and technologies, the vast majority of the big ones. In recent years I’ve been spending time with FastAPI and SQLAlchemy as well as non-python technologies. Django is better though.</p> <p>Not using Django as much at the moment makes me love it even more and realise how lucky we are with such a well designed and well supported framework. It’s not perfect but it’s outstanding. </p> <p>Having said that: at a technical level I’d love to have “cheaper” ways (in every sense) to deploy. Even though deployment methods have changed beyond recognition several times over the years, I always thought this would get easier over time and am kind of surprised that it hasn’t.</p> <p>Very specific to me is that I need Django to have stronger support for many database schemas in the same project, but honestly this is just a specific problem I have inherited in a project at the moment, but it’ll pass eventually.</p> <h4 id="s-what-projects-are-you-working-on-now">What projects are you working on now?</h4> <p>Over the last few years I’ve helped organise a number of events, including PyConAU, though realised I’d been taking on too many projects and trying to pull back actually! Still: Internationally I’m on DSF CoC with a great team. Nationally this year I’ve been serving on the committee of our main Australian open source foundation body, Linux Australia, as well as working in a small team trying to bring together all of the Australian python user groups under a banner we hope to call Python Australia and I’ve had a keen interest in python user groups around the world. In my home town I’ve been organising our local user groups for some time with an awesome team, as well as our fantastic local PyLadies.</p> <p>For work I’m flat-chat working in a senior role on a Platform team in a small data company that provides “critical digital infrastructure” for Australia. Though my most important project of all at the moment really is my family, and I do really prioritise my friends and being healthy nowadays. I’m an avid hackerspace person and do have a couple of purportedly active projects (I’m obsessed with maps among other things) but these are relatively neglected at the moment as I just don’t have the bandwidth.</p> <h4 id="s-which-django-libraries-are-your-favorite-core-or-3rd-party">Which Django libraries are your favorite (core or 3rd party)?</h4> <p>I just love the ORM. We’re so spoiled in the Django community we don’t realise how mature and feature-rich the ORM is. Maybe I’m biased because I’ve been using it for so long I just “think” in Django ORM and I’ve been working away from it lately. It’s such a (comparative) pleasure to use. You can nit-pick at it but compared to anything else it’s so beautifully thought through.</p> <p>The admin was the Django “killer app” in 2008 and I’d argue still is in 2025. To be some dozens of seconds away from a custom CMS backend at any time is still magical. Pony magical. It’s still as impressive as ever to show off to people. Also in the same way that Guido says python makes a great calculator: Django makes a great quick tool for really fast data munging, can’t describe how liberating it feels using it for this purpose.</p> <p>Writing tests in Django is under-rated too.</p> <p>There are so many amazing 3rd party libraries, too many to mention. For shout-outs I don’t think I have any projects without Debug Toolbar. The 3rd party caching libraries Memcache and Redis are both great. I’m also usually happy when I turn on Celery, and excited to see DEP-0014 on its way. Danny and Audrey’s Django Cookiecutter project is a great reference even if you don’t take the whole enchilada.</p> <h4 id="s-what-are-the-top-three-things-in-django-that-you-like">What are the top three things in Django that you like?</h4> <p>I’ve been lucky to generally have had a pretty great time with Django. Generally I’ve used it for projects where it was a really good fit and so it wasn’t painful. As such I like weird little quirky things about Django. Haters-can-hate but I actually really like a bunch of this controversial stuff, for example I like settings.py as a pattern for projects that aren’t out of control; I enjoy using and customising the management commands framework; I think Meta class as an approach to that type of awkward problem is neat; I’ve generally had a pretty nice time with the template language; I dig into utils and reuse them probably more often than most; ORM and the Tests obviously (it’s trivial to plugin pytest of course). Everything is a trade-off in software engineering and while I’m very biased: I just like the trade-offs that Django has chosen, they’re some of the best-in-class.</p> <p>The top 3 things though? This is tough. I just like it. To nail down actual answers though: </p> <ul> <li>the framework workflow overall;</li> <li>that the project has stayed so consistently high quality and battle-hardened for so many years; </li> <li>and the community and my friends (shout out sp-wg)</li> </ul> <h4 id="s-i-know-you-have-start-django-with-one-of-the-first-version-what-do-you-think-of-the-evolution-of-the-framework">I know you have start Django with one of the first version, what do you think of the evolution of the framework?</h4> <p>This is a great question! Thanks for being interested in this history, the Django history is a nice story of having good values and persisting and this actually being successful over the long run. </p> <p>For me there’s all the “back in my day” stuff that’s not obvious now, like Python not being taken seriously as a “real” programming language, let alone javascript, but now those tides have very much turned, and web development is considered extremely respectable and high profile, which was unimaginable when I started. Django started in Web1.0 (whatever that meant), and actually grew substantially during Web2.0 and now even in the modern Web3 era is kind of establishing itself into being part of the backbone of the large parts of the internet that aren’t obvious. Thibaud has a list he maintains of websites that he believes use Django, this is great if you haven’t seen it.</p> <p>One of the most impressive parts of the evolution has been how decisions have been made and implemented. In normal “work” you just have to make things as fast as possible and endlessly add features consequences-be-damned. Open source gets to be fundamentally the opposite. Traditionally one of the defining characteristics of Open Source is that “time is no object”. That is good design and implementation can be allowed the time to breathe and be excessively thought through. There is no rush or deadline. While there’s always conflict and drama I think there has been less so in Django than in most other projects as design decisions have been painstakingly threshed out and perfected in mailing lists, tickets, DEPs and forums over the months and years it takes to make them. The people inside see the drama but we’re in the news almost never compared to most projects in the same space. The point is that hypothetically it’s possible to try to make the best possible design decisions. In practice most projects don’t do this, but I think Django has demonstrated exemplary maturity in trying to pursue this ideal, and is regularly recognised for it. </p> <p>The original founding team deserve full credit for instilling this culture and each successive group of stewards deserve credit for preserving it. </p> <p>There have (and always will be) missteps. For example CBVs are such an obviously good idea on paper, but in practice people don’t think so. On the other hand Andrew Godwin’s implementation of migrations back in the day, that was completely re-writing South from scratch, was truly lovely, even though it was a battle to get to the point of having migrations at all. There’s the history around the db module, which pretty much everyone was too scared to touch after Malcolm died until there were some impressive breakthroughs in it during the “under the hood” sessions not long after DjangoGirls people started coming on board.</p> <p>Django consciously has decided to be extremely considered in its adoption of change and this has been a great thing. Other frameworks have generally been more cavalier, while Django has been steady, careful and reliable. The other full-feature frameworks are kind of in decline, or have hurt themselves by too-much-change-too-fast, while Django has steadily slowly grown and is the trusty go-to tool for a certain kind of job.</p> <p>Now moving forward I see focus on the very subtle things that make the framework nicer to use and understand, On just making the core capabilities better and more reliable and performant, and only very very carefully adding features. </p> <p>In an age where so much quality degradation is occurring, it inspires hope that projects like Django can persist as beacons of high quality, held together by a small group and big community of thoughtful, caring individuals. Hopefully this is something we can continue for a long time into the future also!</p> <h4 id="s-you-are-part-of-the-code-of-conduct-working-group-how-is-it-to-work-with-the-working-group-do-you-have-space-available-for-new-members-what-does-it-require-according-to-you">You are part of the Code of Conduct working group, how is it to work with the working group? Do you have space available for new members? What does it require according to you?</h4> <p>Code of Conduct WGs are slightly niche and exposed to a certain kind of work and responsibility. Not to mention that respecting many sensitives and view-points is necessary. It also means having the guts to tell people “that’s not how it’s done here” when it needs to be said. Personally it’s a kind of work I’ve grown to be passionate about. I truly believe having a great culture is at the core of community (and really anything good) and can be a complex balancing act of competing factors and emotions. It’s certainly not the kind of thing everyone is into, but if you are, the WG is looking for more diversity, if nothing else it’s tending slightly older at the moment.</p> <p>Having said that: Within all of the open source communities from local to international levels there’s always space for people who are willing to turn up and help!</p> <p>Join your local community! Find the parts of community that “speak” to you. Maybe it’s starting a meetup, helping your local conference, running a DjangoGirls. Maybe it’s something engineer-related like finally adding something to an open source library that you’re into, adding some beginner docs somewhere, or engaging with Djangonaut Space. Maybe it’s something online like helping out in <a href="https://forum.djangoproject.com/">forum.djangoproject.com</a>, <a href="https://www.reddit.com/r/django/">Reddit</a> or <a href="https://chat.djangoproject.com/">Discord</a>.</p> <p>As organisers we have this cheat code for finding new people to invite to help more, it’s called “looking for chair-stackers”, that is people who engage to help in the little ways, such as helping stack chairs at the end of an event or generally pack down, wipe up, carry boxes or put things away. Or online: people who go out of their way to try to understand and chip in to manage extra rules, or answer the unanswered thing that’s been sitting there for a while. Or people who just ask “can I help out with that?” when the organisers seem tired or stressed out. Having people around who help in these ways has huge value and has been the beginning of many people being involved in communities and making life-long friends and connections.</p> <p>Now more than ever though, it’s so important to connect to your community. We are stronger, better and healthier when we are connected to and relied on by other people and we have others we can share our experiences with. </p> <p>Particularly us computer people tend not to be as good with connecting with other people, but everyone should find their way to get out and connect! It’s sometimes hard but it’s always better.</p> <h4 id="s-you-have-organized-many-djangogirls-in-australia-how-did-you-start-do-you-have-any-advice-for-someone-who-would-like-to-organize-a-djangogirls-event">You have organized many DjangoGirls in Australia, how did you start? Do you have any advice for someone who would like to organize a DjangoGirls event?</h4> <p>In 2014 I was living in Perth, Australia, where Russell Keith Magee is based and we had a budding Python/Django User Group. At one of the meetings news emerged about how Ola and Ola were running this thing called “DjangoGirls” at EuroPython in a few weeks. PyConAU was scheduled a couple of weeks after this. I was like, that’s a great idea, I can absolutely have a go at doing that and emailed them immediately asking if I could copy their materials and plan. We pulled it together with an amazing bunch of people and I think this was technically the 2nd DjangoGirls event ever. In the following years I’ve been involved in many more, including the first North American DjangoGirls. From our Perth series of events a successful organisation was spun off called SheCodes.</p> <p>In the more-than-a-decade since then the world has changed so much! Particularly in the tech world. I would say specifically for DjangoGirls events, they are very region specific. My first advice for organising an event in your region is to see if there’s been one previously and reach out to the event organisers, or at least the nearest organisers – I think these days there are few places on earth that haven’t had a DjangoGirls event nearish-by. The resources on the website are actually great for getting going and the international DjangoGirls team are lovely, but also always looking for more help.</p> <p>Where I live now, back in the capital, Canberra, we are very well supported for education services. We held a DjangoGirls event a couple of years ago, but for the attendees what emerged was that what we really wanted was just to connect with other technical women.</p> <p>Now what has been very successful for us is an ongoing PyLadies/Women’s Software group who meet up regularly and talk about things that matter to our experience. We use the “lean-coffee” model and it’s been unexpectedly functional. This has been one of the best groups I’ve ever been in with a range of technical women regularly sharing our weird and statistically unusual experiences together, it feeds the soul, and is strongly recommended if you don’t participate in a group like this already.</p> <h4 id="s-is-there-anything-else-youd-like-to-say">Is there anything else you’d like to say?</h4> <p>A final shout out to the original leaders of the Django community, for me personally Russell, Jeff, Jacob, Andrew and Baptiste in particular, but everyone who has persisted over the years in just turning up over the long haul and keeping our part of the world as beautiful as can be. My friends Dave, Matt and Jonah. Thibaud is a great president right now. Rarely is there a dedicated Django person who is not absolutely delightful and I feel both proud and honoured to be part of this community. A big thank you to everyone (especially you Sarah! And all the Sarahs, Natalias, Lillys and Olas) who help to make Django what it is.</p> <hr> <p><strong>Thank you for doing the interview, Elena !</strong></p>Sarah AbderemaneTue, 17 Jun 2025 12:09:45 -0500https://www.djangoproject.com/weblog/2025/jun/17/dsf-member-of-the-month-elena-williams/Django bugfix releases issued: 5.2.3, 5.1.11, and 4.2.23https://www.djangoproject.com/weblog/2025/jun/10/bugfix-releases/<p>Following the June 4, 2025 security release, the Django team is issuing releases for <a class="reference external" href="https://docs.djangoproject.com/en/dev/releases/5.2.3/">Django 5.2.3</a>, <a class="reference external" href="https://docs.djangoproject.com/en/dev/releases/5.1.11/">Django 5.1.11</a>, and <a class="reference external" href="https://docs.djangoproject.com/en/dev/releases/4.2.23/">Django 4.2.23</a> to complete mitigation for CVE-2025-48432: Potential log injection via unescaped request path (<a class="reference external" href="https://www.djangoproject.com/weblog/2025/jun/04/security-releases/">full description</a>).</p> <p>These follow-up releases migrate remaining response logging paths to a safer logging implementation, ensuring that all untrusted input is properly escaped before being written to logs. This update does not introduce a new CVE but strengthens the original fix.</p> <p>We encourage all users of Django to upgrade as soon as possible.</p> <div class="section" id="s-affected-supported-versions"> <h3>Affected supported versions</h3> <ul class="simple"> <li>Django main</li> <li>Django 5.2</li> <li>Django 5.1</li> <li>Django 4.2</li> </ul> </div> <div class="section" id="s-resolution"> <h3>Resolution</h3> <p>Patches to resolve the issue have been applied to Django's main, 5.2, 5.1, and 4.2 branches. The patches may be obtained from the following changesets.</p> <div class="section" id="s-cve-2025-48432-potential-log-injection-via-unescaped-request-path"> <h4>CVE-2025-48432: Potential log injection via unescaped request path</h4> <ul class="simple"> <li>On the <a class="reference external" href="https://github.com/django/django/commit/957951755259b412d5113333b32bf85871d29814/">main branch</a></li> <li>On the <a class="reference external" href="https://github.com/django/django/commit/8fcc83953c350e158a484bf1da0aa1b79b69bb07/">5.2 branch</a></li> <li>On the <a class="reference external" href="https://github.com/django/django/commit/31f4bd31fa16f7f5302f65b9b8b7a49b69a7c4a6/">5.1 branch</a></li> <li>On the <a class="reference external" href="https://github.com/django/django/commit/b597d46bb19c8567615e62029210dab16c70db7d/">4.2 branch</a></li> </ul> </div> </div> <div class="section" id="s-the-following-releases-have-been-issued"> <h3>The following releases have been issued</h3> <ul class="simple"> <li>Django 5.2.3 (<a class="reference external" href="https://www.djangoproject.com/download/5.2.3/tarball/">download Django 5.2.3</a> | <a class="reference external" href="https://www.djangoproject.com/download/5.2.3/checksum/">5.2.3 checksums</a>)</li> <li>Django 5.1.11 (<a class="reference external" href="https://www.djangoproject.com/download/5.1.11/tarball/">download Django 5.1.11</a> | <a class="reference external" href="https://www.djangoproject.com/download/5.1.11/checksum/">5.1.11 checksums</a>)</li> <li>Django 4.2.23 (<a class="reference external" href="https://www.djangoproject.com/download/4.2.23/tarball/">download Django 4.2.23</a> | <a class="reference external" href="https://www.djangoproject.com/download/4.2.23/checksum/">4.2.23 checksums</a>)</li> </ul> <p>The PGP key ID used for this release is : <a class="reference external" href="https://github.com/sarahboyce.gpg">3955B19851EA96EF</a></p> </div> Sarah BoyceTue, 10 Jun 2025 03:06:32 -0500https://www.djangoproject.com/weblog/2025/jun/10/bugfix-releases/DSF calls for applicants for a Django Fellowhttps://www.djangoproject.com/weblog/2025/jun/09/django-fellow-applicants-2025/<p>The Django Software Foundation is announcing a call for Django Fellow applications. A Django Fellow is a contractor, paid by the Django Software Foundation, who dedicates time to maintain the Django framework.</p> <p>The Fellowship program was started in 2014 as a way to dedicate high-quality and consistent resources to the maintenance of Django. The Django Software Foundation currently supports two Fellows –Natalia Bidart and Sarah Boyce– and has approved funding for a new full-time Fellow. This position will be initially for a period of one year, but may be extended depending on fundraising levels.</p> <p>Beyond keeping Django running, a fellow is a representative of Django itself. They embody the welcoming culture of Django and aid the community to progress the framework. Fellows are often called upon to speak at Django conferences and events.</p> <p>They are also usually leading Django Sprints occurring in conferences or other setups. Hence a Django Fellow often engages in both informal and formal mentorship.</p> <h4 id="s-responsibilities">Responsibilities</h4> <p>Fellow duties include (but are not limited to):</p> <ul> <li>Monitoring security reports and ensuring security issues are acknowledged and responded to promptly</li> <li>Fixing release blockers and helping to backport fixes to these and security issues</li> <li>Ensure timely releases including being a release manager for a new version of Django</li> <li>Triaging tickets on Trac</li> <li>Reviewing and merging pull requests</li> <li>Answering contributor questions on the Forum</li> <li>Helping new Django contributors land patches and learn our philosophy</li> </ul> <h4 id="s-requirements">Requirements</h4> <p>A Django fellow reviews a very large amount of Django contributions. This requires knowledge in every aspect of web development that the Django framework touches. This turns out to be an intimidatingly-large list of technical topics, many of which are listed below. <strong>It’s not our expectation that you come into the job knowing everything on this list! </strong>We hope you’ll have solid experience in a few of these topics, particularly some of the “core” technologies important to Django (Python, relational databases, HTTP). But <strong>we fully expect that you’ll learn most of this on the job</strong>. <strong>A willingness to learn, and a demonstrated history of doing so, is more important than comprehensive knowledge.</strong> </p> <p>The technical topics you can expect to work on includes (and is not limited to):</p> <ul> <li>SQL and Databases: SQLite, MySQL, Postgres, Oracle</li> <li>Technical Documentation</li> <li>Javascript</li> <li>CSS</li> <li>Semantic HTML</li> <li>Accessibility</li> <li>UI/UX design (Web and CLI)</li> <li>Python async </li> <li>Python features (and versions), compatibility matrix, etc.</li> <li>Everything around HTTP</li> <li>Security best practices</li> </ul> <p>There are also:</p> <ul> <li>Complex processes which need adhering to</li> <li>Multiple discussions which need opinions and direction</li> <li>Requirements for both formal and informal mentorship</li> </ul> <p>And required professional skills such as:</p> <ul> <li>Conflict resolution</li> <li>Time management and prioritization expertise</li> <li>Ability to focus in short periods of time and do substantial context switches</li> <li>Self-awareness to recognize their own limits and reach out for help</li> <li>Relationship-building and coordination with Django teams, working groups, and potentially external parties.</li> <li>Tenacity, patience, compassion and empathy are essential</li> </ul> <p>Therefore a Django Fellow requires the skills and knowledge of a senior generalist engineer with extensive experience in Python and Django. Open source experience, especially in contributing to Django, is a big plus.</p> <p>Being a Django contributor isn't a prerequisite for this position — we can help get you up to speed. We'll consider applications from anyone with a proven history of working with either the Django community or another similar open-source community. While no geographical location is required, we have a slight preference for timezones between around -8 and +3 UTC to allow for better working hours to overlap the current fellows.</p> <p>If you're interested in applying for the position, please email us at <a href="mailto:fellowship-committee@djangoproject.com">fellowship-committee@djangoproject.com</a> describing why you would be a good fit along with details of your relevant experience and community involvement. Lastly, please include at least one recommendation.</p> <p>The current hourly rate for a fellow is $82.26 USD.</p> <p>Applicants will be evaluated based on the following criteria:</p> <ul> <li>Details of Django and/or other open-source contributions</li> <li>Details of community support in general</li> <li>Understanding of the position</li> <li>Clarity, formality, and precision of communications</li> <li>Strength of recommendation(s)</li> </ul> <p>Applications will be open until midnight AoE, 1 July, 2025, with the expectation that the successful candidate will start around August 1, 2025.</p>The Fellowship Working GroupMon, 09 Jun 2025 12:00:00 -0500https://www.djangoproject.com/weblog/2025/jun/09/django-fellow-applicants-2025/Django security releases issued: 5.2.2, 5.1.10, and 4.2.22https://www.djangoproject.com/weblog/2025/jun/04/security-releases/<p>In accordance with <a class="reference external" href="https://docs.djangoproject.com/en/dev/internals/security/">our security release policy</a>, the Django team is issuing releases for <a class="reference external" href="https://docs.djangoproject.com/en/dev/releases/5.2.2/">Django 5.2.2</a>, <a class="reference external" href="https://docs.djangoproject.com/en/dev/releases/5.1.10/">Django 5.1.10</a>, and <a class="reference external" href="https://docs.djangoproject.com/en/dev/releases/4.2.22/">Django 4.2.22</a>. These releases address the security issues detailed below. We encourage all users of Django to upgrade as soon as possible.</p> <div class="section" id="s-cve-2025-48432-potential-log-injection-via-unescaped-request-path"> <h3>CVE-2025-48432: Potential log injection via unescaped request path</h3> <p>Internal HTTP response logging used <tt class="docutils literal">request.path</tt> directly, allowing control characters (e.g. newlines or ANSI escape sequences) to be written unescaped into logs. This could enable log injection or forgery, letting attackers manipulate log appearance or structure, especially in logs processed by external systems or viewed in terminals.</p> <p>Although this does not directly impact Django's security model, it poses risks when logs are consumed or interpreted by other tools. To fix this, the internal <tt class="docutils literal">django.utils.log.log_response()</tt> function now escapes all positional formatting arguments using a safe encoding.</p> <p>Thanks to Seokchan Yoon (<a class="reference external" href="https://ch4n3.kr/">https://ch4n3.kr/</a>) for the report.</p> <p>This issue has severity &quot;low&quot; according to the Django security policy.</p> </div> <div class="section" id="s-affected-supported-versions"> <h3>Affected supported versions</h3> <ul class="simple"> <li>Django main</li> <li>Django 5.2</li> <li>Django 5.1</li> <li>Django 4.2</li> </ul> </div> <div class="section" id="s-resolution"> <h3>Resolution</h3> <p>Patches to resolve the issue have been applied to Django's main, 5.2, 5.1, and 4.2 branches. The patches may be obtained from the following changesets.</p> <div class="section" id="s-cve-2025-48432-potential-log-injection-via-unescaped-request-path-1"> <h4>CVE-2025-48432: Potential log injection via unescaped request path</h4> <ul class="simple"> <li>On the <a class="reference external" href="https://github.com/django/django/commit/a07ebec5591e233d8bbb38b7d63f35c5479eef0e">main branch</a></li> <li>On the <a class="reference external" href="https://github.com/django/django/commit/7456aa23dafa149e65e62f95a6550cdb241d55ad">5.2 branch</a></li> <li>On the <a class="reference external" href="https://github.com/django/django/commit/596542ddb46cdabe011322917e1655f0d24eece2">5.1 branch</a></li> <li>On the <a class="reference external" href="https://github.com/django/django/commit/ac03c5e7df8680c61cdb0d3bdb8be9095dba841e">4.2 branch</a></li> </ul> </div> </div> <div class="section" id="s-the-following-releases-have-been-issued"> <h3>The following releases have been issued</h3> <ul class="simple"> <li>Django 5.2.2 (<a class="reference external" href="https://www.djangoproject.com/download/5.2.2/tarball/">download Django 5.2.2</a> | <a class="reference external" href="https://www.djangoproject.com/download/5.2.2/checksum/">5.2.2 checksums</a>)</li> <li>Django 5.1.10 (<a class="reference external" href="https://www.djangoproject.com/download/5.1.10/tarball/">download Django 5.1.10</a> | <a class="reference external" href="https://www.djangoproject.com/download/5.1.10/checksum/">5.1.10 checksums</a>)</li> <li>Django 4.2.22 (<a class="reference external" href="https://www.djangoproject.com/download/4.2.22/tarball/">download Django 4.2.22</a> | <a class="reference external" href="https://www.djangoproject.com/download/4.2.22/checksum/">4.2.22 checksums</a>)</li> </ul> <p>The PGP key ID used for this release is Natalia Bidart: <a class="reference external" href="https://github.com/nessita.gpg">2EE82A8D9470983E</a></p> </div> <div class="section" id="s-general-notes-regarding-security-reporting"> <h3>General notes regarding security reporting</h3> <p>As always, we ask that potential security issues be reported via private email to <tt class="docutils literal">security&#64;djangoproject.com</tt>, and not via Django's Trac instance, nor via the Django Forum. Please see <a class="reference external" href="https://www.djangoproject.com/security/">our security policies</a> for further information.</p> </div> Natalia BidartWed, 04 Jun 2025 06:00:00 -0500https://www.djangoproject.com/weblog/2025/jun/04/security-releases/Why, in 2025, do we still need a 3rd party app to write a REST API with Django?https://www.djangoproject.com/weblog/2025/may/22/why-need-3rd-party-app-rest-api-with-django/<p><link rel="canonical" href="https://emma.has-a.blog/articles/why-do-we-need-an-external-app-for-rest.html"></p> <p>The question was asked to the president of the DSF this year at FOSDEM, after <a href="https://fosdem.org/2025/schedule/event/fosdem-2025-5824-shifting-dx-expectations-keeping-django-relevant-/">his talk</a>. And it is clearly a legitimate one!</p> <p>But… is it <code>True</code>? Do we actually need a 3rd party app to write an API with Django?</p> <p>In a lot of cases, when you require a complex and full-featured API, I would recommend you <strong>do</strong> use one. <a href="https://www.django-rest-framework.org">Django REST Framework</a> and <a href="https://django-ninja.dev">Django Ninja</a> being very sound choices with a bunch of nifty things you might need in a bigger project.</p> <p><strong>But…</strong> what if what you need is a simple REST API that does CRUD? Do you really need a 3rd party app to do that?</p> <h3 id="s-lets-try-not-to">Let's try not to!</h3> <p>Let's first ask what is a REST API in the context of this article. Let's limit ourselves to building this:</p> <ul> <li>a URL that answers to <code>GET</code> requests with a list of records of a single model type</li> <li><code>POST</code>-ing to that same URL should create a new record</li> <li>a second URL with the primary key of a record tagged to the end. When <code>GET</code>-ing that URL, one should receive only that single record, in a similar format as in the list</li> <li><code>PUT</code>-ing data to that URL should update the record and return that record with updated values</li> <li><code>DELETE</code>-ing to that same URL should delete the record</li> <li>everything should be achieved using JSON</li> </ul> <h3 id="s-listing-records-from-a-model">Listing records from a model</h3> <p>Chances are you have heard of generic class-based views (CBVs) in Django, the one that comes to mind when it comes to listing records is <a href="https://ccbv.co.uk/projects/Django/5.2/django.views.generic.list/ListView/">the built-in <code>django.views.generic.ListView</code></a>.</p> <p><code>ListView</code> extends 2 other classes, <code>BaseListView</code> and <code>MultipleObjectTemplateResponseMixin</code>. Since we want to build an API, we clearly don't need to extend anything template-related. Looking at what <a href="https://ccbv.co.uk/projects/Django/5.2/django.views.generic.list/BaseListView/"><code>BaseListView</code> provides</a>, one can notice the only thing really missing there is a <code>render_to_response</code> method. And this is going to be the case for most of the other base classes.</p> <p>This sets our starting point!</p> <p>The type of response we want to render is a <code>json</code> response and Django already provides one. So let's build a <code>JsonViewMixin</code> that looks like this for now:</p> <div class="highlight-python notranslate"><div class="highlight"> <pre><span></span><span class="k">class</span><span class="w"> </span><span class="nc">JsonViewMixin</span><span class="p">(</span><span class="n">View</span><span class="p">):</span> <span class="n">response_class</span> <span class="o">=</span> <span class="n">JsonResponse</span> <span class="n">content_type</span> <span class="o">=</span> <span class="s1">'application/json'</span> <span class="k">def</span><span class="w"> </span><span class="nf">render_to_response</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">context</span><span class="p">,</span> <span class="o">**</span><span class="n">response_kwargs</span><span class="p">):</span> <span class="n">response_kwargs</span><span class="o">.</span><span class="n">setdefault</span><span class="p">(</span><span class="s2">"content_type"</span><span class="p">,</span> <span class="bp">self</span><span class="o">.</span><span class="n">content_type</span><span class="p">)</span> <span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">response_class</span><span class="p">(</span><span class="n">context</span><span class="p">,</span> <span class="o">**</span><span class="n">response_kwargs</span><span class="p">)</span> </pre> </div></div> <p>The next thing we have to tackle is that the <code>context</code> returned by <code>BaseListView</code>'s <code>get_context_data</code> returns much more data than what we <em>strictly</em> need for this article. Also the list of records returned is not serialized to <code>json</code>.</p> <p>Serializers for mutliple formats already exist in Django core (see <code>django.core.serializers</code>) but I will go a different route here. There is another way to serialize data in Django that you are likely familiar with but is not labelled as clearly: forms.</p> <p>Forms are used in regular views to serialize models to simple types, understandable by HTML forms (mostly text) and vice-versa. This is very close to what we need, since <code>json</code> is also mostly text-based.</p> <p>To start with, using forms as serializers requires creating a new form instance for each record in the list we want to return.</p> <p>Let's add that to the mixin!</p> <div class="highlight-python notranslate"><div class="highlight"> <pre><span></span><span class="k">def</span><span class="w"> </span><span class="nf">serialize_many</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">obj_list</span><span class="p">):</span> <span class="k">return</span> <span class="p">[</span><span class="bp">self</span><span class="o">.</span><span class="n">serialize_one</span><span class="p">(</span><span class="n">obj</span><span class="p">)</span> <span class="k">for</span> <span class="n">obj</span> <span class="ow">in</span> <span class="n">obj_list</span><span class="p">]</span> <span class="k">def</span><span class="w"> </span><span class="nf">serialize_one</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">obj</span><span class="p">)</span> <span class="n">form</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">get_form_for_object</span><span class="p">(</span><span class="n">obj</span><span class="p">)</span> <span class="n">serialized</span> <span class="o">=</span> <span class="n">form</span><span class="o">.</span><span class="n">initial</span> <span class="n">serialized</span><span class="p">[</span><span class="s1">'pk'</span><span class="p">]</span> <span class="o">=</span> <span class="n">obj</span><span class="o">.</span><span class="n">pk</span> <span class="c1"># forms strip pk's from their data</span> <span class="k">return</span> <span class="n">serialized</span> <span class="k">def</span><span class="w"> </span><span class="nf">get_form_for_object</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">obj</span><span class="p">):</span> <span class="n">form_class</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">get_form_class</span><span class="p">()</span> <span class="n">kwargs</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">get_form_kwargs</span><span class="p">()</span> <span class="n">kwargs</span><span class="p">[</span><span class="s1">'instance'</span><span class="p">]</span> <span class="o">=</span> <span class="n">obj</span> <span class="k">return</span> <span class="n">form_class</span><span class="p">(</span><span class="o">**</span><span class="n">kwargs</span><span class="p">)</span> </pre> </div></div> <h4 id="s-why-use-forms">Why use forms?</h4> <p><a href="https://docs.djangoproject.com/en/stable/topics/forms/modelforms/#modelform">ModelForm</a>s are a built-in and robust Django tool that are built around the idea of handling the transition between <code>Model</code> fields and simple (and also JSON-serializable) types (mostly text and numbers). Which is exactly what we want from (de-)serializers in a lot of cases.</p> <p>If you need to (de-)serialize a custom field type, Django documents <a href="https://docs.djangoproject.com/en/stable/ref/forms/fields/#creating-custom-fields">creating a custom form field</a> and this covered in <a href="https://stackoverflow.com/questions/1526806/how-to-write-custom-form-fields-in-django">various places like StackOverflow</a>.</p> <h3 id="s-moving-on-to-our-first-view">Moving on to our first <code>View</code></h3> <p>Now that we have a tool to serialize the records list returned by <code>BaseListView</code> let's write the first version of <code>JsonListView</code>. As I alluded to earlier, we need to strip down what is returned from <code>get_context_data</code>.</p> <div class="highlight-python notranslate"><div class="highlight"> <pre><span></span><span class="k">class</span><span class="w"> </span><span class="nc">JsonListView</span><span class="p">(</span><span class="n">JsonViewMixin</span><span class="p">,</span> <span class="n">BaseListView</span><span class="p">):</span> <span class="k">def</span><span class="w"> </span><span class="nf">get_context_data</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">):</span> <span class="n">context</span> <span class="o">=</span> <span class="nb">super</span><span class="p">()</span><span class="o">.</span><span class="n">get_context_data</span><span class="p">(</span><span class="o">**</span><span class="n">kwargs</span><span class="p">)</span> <span class="c1"># wrapping this in a dict for security reasons</span> <span class="k">return</span> <span class="p">{</span> <span class="s1">'results'</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">serialize_many</span><span class="p">(</span><span class="n">context</span><span class="p">[</span><span class="s1">'object_list'</span><span class="p">])</span> <span class="p">}</span> </pre> </div></div> <p>This won't work yet because <code>get_form_class</code> that I used in the <code>JsonViewMixin</code> is only provided by classes that descend from <code>FormMixin</code>. Since we want this view to handle both listing and creating records, let's go and fix that in the next section!</p> <h3 id="s-1-down-3-to-go-adding-records">1 down, 3 to go: Adding records</h3> <p>First thing first, let's rebrand <code>JsonListView</code> and make it inherit from <code>BaseCreateView</code>.</p> <div class="highlight-python notranslate"><div class="highlight"> <pre><span></span><span class="k">class</span><span class="w"> </span><span class="nc">JsonListCreateView</span><span class="p">(</span><span class="n">JsonViewMixin</span><span class="p">,</span> <span class="n">BaseCreateView</span><span class="p">,</span> <span class="n">BaseListView</span><span class="p">):</span> </pre> </div></div> <p>Form creation and validation will be handled automatically by Django!</p> <p>Almost…</p> <p>The first concern will be with populating the form with <code>POST</code> data. While Django does this for you when dealing with URL encoded or multipart form data, it does not (<a href="https://github.com/django/deps/pull/88/files">yet</a>) handle json-encoded <code>POST</code> content.</p> <p>But this can be handled by taking advantage of the modularity of Django's generic class-based-views and overwritting <code>get_form_kwargs</code>.</p> <p>Let's address this (in a naïve way) within the mixin as it will be applicable to any JSON view:</p> <div class="highlight-python notranslate"><div class="highlight"> <pre><span></span><span class="k">def</span><span class="w"> </span><span class="nf">get_form_kwargs</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span> <span class="n">kwargs</span> <span class="o">=</span> <span class="nb">super</span><span class="p">()</span><span class="o">.</span><span class="n">get_form_kwargs</span><span class="p">()</span> <span class="k">if</span> <span class="p">(</span> <span class="nb">len</span><span class="p">(</span><span class="n">kwargs</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s2">"data"</span><span class="p">,</span> <span class="p">{}))</span> <span class="o">==</span> <span class="mi">0</span> <span class="ow">and</span> <span class="bp">self</span><span class="o">.</span><span class="n">request</span><span class="o">.</span><span class="n">content_type</span> <span class="o">==</span> <span class="s2">"application/json"</span> <span class="ow">and</span> <span class="nb">len</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">request</span><span class="o">.</span><span class="n">body</span><span class="p">)</span> <span class="p">):</span> <span class="c1"># The request has a JSON body that we did not decode</span> <span class="n">kwargs</span><span class="p">[</span><span class="s2">"data"</span><span class="p">]</span> <span class="o">=</span> <span class="n">json</span><span class="o">.</span><span class="n">loads</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">request</span><span class="o">.</span><span class="n">body</span><span class="p">)</span> <span class="k">return</span> <span class="n">kwargs</span> </pre> </div></div> <p>An issue that could arise here is that a <code>JSONDecoderError</code> could be triggered. <code>get_form_kwargs</code> does not return a response so I don't think it is the right place to handle the exception.</p> <p>The <code>post</code> method does return a response, let's wrap the original one with a <code>try</code>/<code>except</code> (still in the mixin):</p> <div class="highlight-python notranslate"><div class="highlight"> <pre><span></span><span class="k">def</span><span class="w"> </span><span class="nf">post</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">request</span><span class="p">,</span> <span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">):</span> <span class="k">try</span><span class="p">:</span> <span class="k">return</span> <span class="nb">super</span><span class="p">()</span><span class="o">.</span><span class="n">post</span><span class="p">(</span><span class="n">request</span><span class="p">,</span> <span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">)</span> <span class="k">except</span> <span class="n">json</span><span class="o">.</span><span class="n">decoder</span><span class="o">.</span><span class="n">JSONDecodeError</span> <span class="k">as</span> <span class="n">e</span><span class="p">:</span> <span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">response_class</span><span class="p">(</span> <span class="p">{</span><span class="s2">"error"</span><span class="p">:</span> <span class="sa">f</span><span class="s2">"json decode error: </span><span class="si">{</span><span class="n">e</span><span class="o">.</span><span class="n">msg</span><span class="si">}</span><span class="s2">"</span><span class="p">},</span> <span class="n">status</span><span class="o">=</span><span class="n">HTTPStatus</span><span class="o">.</span><span class="n">UNSUPPORTED_MEDIA_TYPE</span><span class="p">,</span> <span class="p">)</span> </pre> </div></div> <p>Speaking of returning responses, the <a href="https://docs.djangoproject.com/en/stable/ref/class-based-views/generic-editing/#django.views.generic.edit.BaseCreateView">BaseCreateView</a> class is built around HTML principles and its <code>form_valid</code> and <code>get</code> methods are both designed to render a form (via <code>get_context_data</code>).</p> <p>In the case of our REST API, the "create" part of things should not be involved with <code>GET</code> requests.</p> <p>Furthermore the reply to an invalid form submission should only comprise of an error (status + message) and should not require anything provided by <code>get_context_data</code>.</p> <p>Still, in regards to form validation, a valid form should not result in a redirect (behaviour of <code>BaseCreateView</code>) but rather in a <code>201</code> response optionally containing the representation of the created record.</p> <p>The form handling part is generic enough to put it in the mixin itself.</p> <p>The behaviour of <code>GET</code> is specific to the list/create view though.</p> <p>Let's write the code accordingly:</p> <div class="highlight-python notranslate"><div class="highlight"> <pre><span></span><span class="k">class</span><span class="w"> </span><span class="nc">JsonViewMixin</span><span class="p">(</span><span class="n">View</span><span class="p">):</span> <span class="o">...</span> <span class="k">def</span><span class="w"> </span><span class="nf">form_invalid</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">form</span><span class="p">):</span> <span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">response_class</span><span class="p">(</span> <span class="p">{</span><span class="s1">'errors'</span><span class="p">:</span> <span class="n">form</span><span class="o">.</span><span class="n">errors</span><span class="p">},</span> <span class="n">status</span><span class="o">=</span><span class="n">HTTPStatus</span><span class="o">.</span><span class="n">UNPROCESSABLE_CONTENT</span> <span class="p">)</span> <span class="k">def</span><span class="w"> </span><span class="nf">form_valid</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">form</span><span class="p">):</span> <span class="bp">self</span><span class="o">.</span><span class="n">object</span> <span class="o">=</span> <span class="n">form</span><span class="o">.</span><span class="n">save</span><span class="p">()</span> <span class="n">context</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">serialize_one</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">object</span><span class="p">)</span> <span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">response_class</span><span class="p">(</span> <span class="n">context</span><span class="p">,</span> <span class="n">status</span><span class="o">=</span><span class="n">HTTPStatus</span><span class="o">.</span><span class="n">CREATED</span> <span class="p">)</span> <span class="k">class</span><span class="w"> </span><span class="nc">JsonListCreateView</span><span class="p">(</span><span class="n">JsonViewMixin</span><span class="p">,</span> <span class="n">BaseCreateView</span><span class="p">,</span> <span class="n">BaseListView</span><span class="p">):</span> <span class="o">...</span> <span class="k">def</span><span class="w"> </span><span class="nf">get_context_data</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">):</span> <span class="c1"># Explicitly point to BaseListView instead of `super()`</span> <span class="c1"># to prevent actions taken by</span> <span class="c1"># BaseCreateView's implementation of `get_context_data`</span> <span class="n">context</span> <span class="o">=</span> <span class="n">BaseListView</span><span class="o">.</span><span class="n">get_context_data</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">)</span> <span class="k">return</span> <span class="p">{</span> <span class="s1">'results'</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">serialize_many</span><span class="p">(</span><span class="n">context</span><span class="p">[</span><span class="s1">'object_list'</span><span class="p">])</span> <span class="p">}</span> </pre> </div></div> <h3 id="s-halfway-there">Halfway there!</h3> <p>That was everything needed to handle the <code>create</code> and <code>list</code> portions of our CRUD REST application. Now we can move on to the <code>read</code>, <code>update</code>, <code>delete</code> part. We'll do that in a second <code>View</code> class as it requires a slightly different URL, one that contains the <code>pk</code> of the resource.</p> <p>Both read and update functionalities are provided by Django <code>BaseUpdateView</code> but, as with the create/list view, the major difference in this case will be that we need a much simpler context.</p> <div class="highlight-python notranslate"><div class="highlight"> <pre><span></span><span class="k">class</span><span class="w"> </span><span class="nc">JsonReadUpdateView</span><span class="p">(</span><span class="n">JsonViewMixin</span><span class="p">,</span> <span class="n">BaseUpdateView</span><span class="p">):</span> <span class="k">def</span><span class="w"> </span><span class="nf">get_context_data</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">):</span> <span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">serialize_one</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">object</span><span class="p">)</span> </pre> </div></div> <p>That's it!!!</p> <p>Well, almost…</p> <p><code>BaseUpdateView</code> is wired to answer to <code>POST</code> requests for updating a record while REST good practices want us to use <code>PUT</code> instead. The fix for this is to raise an error in reply to <code>POST</code> calls while directing <code>PUT</code>s to the parent's <code>post</code> implementation.</p> <div class="highlight-python notranslate"><div class="highlight"> <pre><span></span><span class="k">def</span><span class="w"> </span><span class="nf">post</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">request</span><span class="p">,</span> <span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">):</span> <span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">response_class</span><span class="p">(</span><span class="n">status</span><span class="o">=</span><span class="n">HTTPStatus</span><span class="o">.</span><span class="n">METHOD_NOT_ALLOWED</span><span class="p">)</span> <span class="k">def</span><span class="w"> </span><span class="nf">put</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">request</span><span class="p">,</span> <span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">):</span> <span class="k">return</span> <span class="nb">super</span><span class="p">()</span><span class="o">.</span><span class="n">post</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">request</span><span class="p">,</span> <span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">)</span> </pre> </div></div> <p>One more fix…</p> <p>Our mixin implementation returns a <code>201</code> on <code>form_valid</code>. In case of any view which is not creating a record, this should be <code>200</code>. Here are the necessary changes:</p> <div class="highlight-python notranslate"><div class="highlight"> <pre><span></span><span class="k">class</span><span class="w"> </span><span class="nc">JsonViewMixin</span><span class="p">(</span><span class="n">View</span><span class="p">):</span> <span class="n">form_valid_status</span> <span class="o">=</span> <span class="n">HTTPStatus</span><span class="o">.</span><span class="n">OK</span> <span class="c1"># new property</span> <span class="k">def</span><span class="w"> </span><span class="nf">form_valid</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">form</span><span class="p">):</span> <span class="bp">self</span><span class="o">.</span><span class="n">object</span> <span class="o">=</span> <span class="n">form</span><span class="o">.</span><span class="n">save</span><span class="p">()</span> <span class="n">context</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">serialize_one</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">object</span><span class="p">)</span> <span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">response_class</span><span class="p">(</span> <span class="n">context</span><span class="p">,</span> <span class="n">status</span><span class="o">=</span><span class="bp">self</span><span class="o">.</span><span class="n">form_valid_status</span> <span class="c1"># use the new property</span> <span class="p">)</span> <span class="o">...</span> <span class="k">class</span><span class="w"> </span><span class="nc">JsonListCreateView</span><span class="p">(</span><span class="n">JsonViewMixin</span><span class="p">,</span> <span class="n">BaseCreateView</span><span class="p">,</span> <span class="n">BaseListView</span><span class="p">):</span> <span class="n">form_valid_status</span> <span class="o">=</span> <span class="n">HTTPStatus</span><span class="o">.</span><span class="n">CREATED</span> <span class="c1"># override in case of creation</span> </pre> </div></div> <h4 id="s-why-put-and-not-patch">Why <code>PUT</code> and not <code>PATCH</code>?</h4> <p><code>BaseUpdateView</code> builds a form that expects all fields to be filled. Non-present fields would be reset to empty on the existing record for partial updates.</p> <p>I'll leave it as an exercise to the reader to override that behaviour in case of a <code>PATCH</code> request in order to "pre-fill the form" with existing values, maybe by using the form's <code>initial</code> property… 😉</p> <h3 id="s-finally">Finally…</h3> <p>The last bit of logic we have to implement is for deleting objects. Most of the code from Django's <a href="https://docs.djangoproject.com/en/stable/ref/class-based-views/generic-editing/#django.views.generic.edit.BaseDeleteView">BaseDeleteView</a> is related to creating and validating a form for confirming the user's intend on deleting the resource. This is usually not the expected behaviour for a REST API, this part being handled by whatever is calling the API.</p> <p>Furthermore, it doesn't implement a delete method. In the HTML world of Django's <code>BaseDeleteView</code>, everything is done using <code>GET</code> and <code>POST</code>. So we are (mostly) on our own for this last part.</p> <p>We can still leverage the <code>get_object</code> implementation provided by <a href="https://docs.djangoproject.com/en/stable/ref/class-based-views/generic-editing/#django.views.generic.edit.BaseUpdateView">BaseUpdateView</a> though.</p> <p>Here is what implementing the delete operation for our read/update/delete view looks like:</p> <div class="highlight-python notranslate"><div class="highlight"> <pre><span></span><span class="k">class</span><span class="w"> </span><span class="nc">JsonReadUpdateDeleteView</span><span class="p">(</span><span class="n">JsonViewMixin</span><span class="p">,</span> <span class="n">BaseUpdateView</span><span class="p">):</span> <span class="c1"># name changed</span> <span class="o">...</span> <span class="k">def</span><span class="w"> </span><span class="nf">delete</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">request</span><span class="p">,</span> <span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">):</span> <span class="bp">self</span><span class="o">.</span><span class="n">get_object</span><span class="p">()</span><span class="o">.</span><span class="n">delete</span><span class="p">()</span> <span class="c1"># data is required by JsonResponse</span> <span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">response_class</span><span class="p">(</span><span class="n">data</span><span class="o">=</span><span class="p">{},</span> <span class="n">status</span><span class="o">=</span><span class="n">HTTPStatus</span><span class="o">.</span><span class="n">NO_CONTENT</span><span class="p">)</span> </pre> </div></div> <h3 id="s-conclusion">Conclusion</h3> <p>This implementation is basic and clearly naïve. But it gets the job done!</p> <p>And this can all be done by leveraging Django-provided tools and mechanisms, mainly using Django's generic CBVs.</p> <p>Generic class-based views have been built in such a modular fashion that implementing one's own mini REST framework can be done in less than 100 lines of code.</p> <p>A non-negligible advantage of such an approach is that most libraries written to work with Django's generic CBVs are also likely to work with this implementation.</p> <p>This rather simple approach can certainly be improved (handling exceptions in <code>delete</code>… anyone?) and is clearly not going to cover everybody's use cases. And it most likely misses handling a bunch of edge cases!</p> <p>And if you are building a large REST API, I would say you are probably still better off using a 3rd party library but… to me, the answer to the question <em>“Why do you need a 3rd party application to write a simple REST application with Django?”</em> is: "<strong>You don’t</strong>"</p> <hr> <p>If you enjoyed this article, read more from Emma on <a href="https://emma.has-a.blog/">Emma has a blog</a>, which is where this piece was from. Or watch the FOSDEM talk that Emma reacts to:</p> <p><a href="https://fosdem.org/2025/schedule/event/fosdem-2025-5824-shifting-dx-expectations-keeping-django-relevant-/"> <figure> <img src="https://media.djangoproject.com/blog/images/2025/05/Django_challenges_-_opinions_on_the_internet-70.jpg" alt="Screenshot of conference slides, Django’s challenges according to users - Opinions on the internet. FOSDEM 25. With a picture of the speaker on the bottom right corner."> <figcaption>Thibaud Colas - Shifting DX expectations: keeping Django relevant 😬 | FOSDEM 2025</figcaption> </figure> </a></p>Emma DelescolleThu, 22 May 2025 04:12:02 -0500https://www.djangoproject.com/weblog/2025/may/22/why-need-3rd-party-app-rest-api-with-django/Our Google Summer of Code 2025 contributorshttps://www.djangoproject.com/weblog/2025/may/16/our-google-summer-of-code-2025-contributors/<p>We’re excited to introduce our <a href="https://summerofcode.withgoogle.com/">Google Summer of Code</a> 2025 contributors!</p> <p><img alt="Congratulations - Farhan Ali Raza (Django Templates: Bringing django-template-partials into core), A. Rafey Khan (Django Admin: Add Keyboard Shortcuts and Command Palette), Saurabh K (Automate processes within Django contributions workflow). Google Summer of Code, Django" src="https://media.djangoproject.com/blog/images/2025/05/gsoc-congratulations.jpg"></p> <p>These amazing folks will be working on impactful projects that will shape Django’s future. Meet the contributors 👇</p> <h3 id="s-a-rafey-khan"><a href="https://www.linkedin.com/in/ACoAADmjd6UBhQBSSK7Heb9OslQuj7aVSpyGpIc">A. Rafey Khan</a></h3> <p>Project: <a href="https://code.djangoproject.com/wiki/SummerOfCode2025#DjangoAdmin:AddCommandpalette">Django Admin – Add Keyboard Shortcuts &amp; Command Palette</a>. Mentors: <a href="https://www.linkedin.com/in/ACoAAD64FcIB_REeUesVHma7RDz16AOs1Y6l07s">Tom Carrick</a>, <a href="https://www.linkedin.com/in/apoorv-garg-/">Apoorv Garg</a></p> <p>Rafey will work on making Django Admin faster and more accessible through keyboard-driven workflows. Excited to see this land!</p> <h3 id="s-farhan-ali-raza"><a href="https://www.linkedin.com/in/farhanaliraza/">Farhan Ali Raza</a></h3> <p>Project: <a href="https://code.djangoproject.com/wiki/SummerOfCode2025#DjangoTemplates:Bringdjango-template-partialsintocore">Bring django-template-partials into core</a>. Mentor: <a href="https://www.linkedin.com/in/carlton-gibson-75b38b1b3/">Carlton Gibson</a></p> <p>Farhan will be enhancing Django’s template system by adding first-class support for partials—making componentized templates easier than ever.</p> <h3 id="s-saurabh-k"><a href="https://www.linkedin.com/in/0saurabh0/">Saurabh K</a></h3> <p>Project: <a href="https://code.djangoproject.com/wiki/SummerOfCode2025#AutomateprocesseswithinDjangocontributionworkflow">Automate processes within Django’s contribution workflow</a>. Mentor: <a href="https://www.linkedin.com/in/lillian-foote/">Lily Foote</a></p> <p>Saurabh will work on streamlining how contributors interact with Django repo—automating repetitive tasks and improving dev experience for all.</p> <p>A huge shoutout to our mentors and the broader Django community for supporting these contributors! 💚</p> <p>Let’s make this a summer of learning, building, and collaboration.</p>Bhuvnesh SharmaFri, 16 May 2025 06:54:21 -0500https://www.djangoproject.com/weblog/2025/may/16/our-google-summer-of-code-2025-contributors/Our new accessibility statementhttps://www.djangoproject.com/weblog/2025/may/15/our-new-accessibility-statement/<p>Happy Global Accessibility Awareness Day! We thought this would be a fitting occasion to announce our brand new <a href="https://www.djangoproject.com/accessibility/">Django accessibility statement</a> 🎉</p> <p>Did you know that according to the <a href="https://webaim.org/projects/million/">WebAIM Million</a> survey, 94.6% of sites have easily-detectable accessibility issues? We all need to work together to build a more inclusive web (also check out our <a href="https://www.djangoproject.com/diversity/">diversity statement</a> if you haven’t already!). There are accessibility gaps in Django itself too. This statement improves transparency, and clearly states our intentions. And we hope it encourages our community and the industry at large to more widely consider accessibility.</p> <h3 id="s-how-to-use-this-statement">How to use this statement</h3> <p>Read it, share it with your friends, or in a procurement context!</p> <ul> <li>Use it to understand where there are gaps in Django that need to be addressed on projects.</li> <li>And opportunities to contribute to Django and related projects ❤️</li> <li>Factor it into legal compliance. For example with the <a href="https://en.wikipedia.org/wiki/European_Accessibility_Act">European Accessibility Act</a>. Starting June 2025, accessibility becomes a legal requirement for large swaths of the private sector in the European Union.</li> <li>Share it with venues for Django events to demonstrate the importance of accessibility for their competitiveness.</li> </ul> <h3 id="s-how-you-can-help">How you can help</h3> <p>Take a moment to provide any feedback you might have about the statement <a href="https://forum.djangoproject.com/t/our-new-accessibility-statement/40947">on the Django Forum</a>. Let us know if you would prefer additional reporting like an ATAG audit, or VPAT, ACR, or any other acronym. Let us know if you’d like to contribute to the accessibility of the Django community! 🫶</p>Thibaud Colas & Accessibility teamThu, 15 May 2025 09:00:00 -0500https://www.djangoproject.com/weblog/2025/may/15/our-new-accessibility-statement/DjangoCon Europe and beyondhttps://www.djangoproject.com/weblog/2025/may/14/djangocon-europe-and-beyond/<p><img alt="Group photo with all in-person DjangoCon Europe 2025 attendees, in a large conference room" src="https://media.djangoproject.com/blog/images/2025/05/202504_DjangoConEurope2025_group_photo_at_DjangoCon_Europe_2025.jpg"></p> <p>Credit: DjangoCon Europe 2025 organizers</p> <hr> <p>We had a blast at <a href="https://2025.djangocon.eu/">DjangoCon Europe 2025</a>, and hope you did too! Events like this are essential for our community, delighting both first-timers and seasoned Djangonauts with insights, good vibes, and all-around inspiration. This year’s conference brought together brilliant minds from all corners of the globe. And featured early celebrations of Django’s 20th birthday! ⭐️🎂🎉</p> <p>After <a href="https://www.djangoproject.com/weblog/2005/jul/14/prelaunch/">launching in 2005</a>, Django turns 20 in 2025, and the conference was a great occasion for our community to celebrate this. And work on the sustainability of the project together.</p> <h3 id="s-we-need-more-code-reviews">We need more code reviews</h3> <p>Our <a href="https://www.djangoproject.com/fundraising/#fellowship-program">Django Fellow</a> Sarah Boyce kicked off the conference with a call for more contributions – of the reviewing kind. In her words,</p> <blockquote> <p>Django needs your help. Every day, contributors submit pull requests and update existing PRs, but there aren't enough reviewers to keep up. Learn why Django needs more reviewers and how you can help get changes merged into core.</p> </blockquote> <p><img alt="Django Fellow Sarah Boyce on stage at DjangoCon Europe 2025, with audience in the foreground" src="https://media.djangoproject.com/blog/images/2025/05/202504_DjangoConEurope2025_Django_Fellow_Sarah_Boyce_on_stage.jpg"></p> <h3 id="s-we-need-more-fundraising">We need more fundraising</h3> <p>Our Vice President Sarah Abderemane got on stage to encourage more financial support of Django from attendees, showcasing how simple it is to <a href="https://www.djangoproject.com/fundraising/">donate to the project</a> (get your boss to do it!). We have ambitious plans for 2025, which will require us to grow the Foundation’s budget accordingly.</p> <p><img alt="Sarah Abderemane on stage, with a slide titled 'Ways to support the community'" src="https://media.djangoproject.com/blog/images/2025/05/202504_DjangoConEurope2025_Sarah_Abderemane_ways_to_support_communit_WXBCVu6.jpg"></p> <h3 id="s-annual-meeting-of-dsf-members">Annual meeting of DSF Members</h3> <p>Our Board members Tom Carrick, Thibaud Colas, Sarah Abderemane, and Paolo Melchiorre were at the conference to organize a meeting of Members of the Django Software Foundation. This was a good occasion to discuss long-standing topics, and issues of the moment, such as:</p> <ul> <li>Diversity, equity and inclusion. Did you know we recently got awarded the <a href="https://www.djangoproject.com/weblog/2025/jan/19/django-earns-the-chaoss-dei-bronze-badge/">CHAOSS DEI bronze badge</a>? We need to keep the momentum in this area.</li> <li>Management of the Membership at the Foundation. With different visions on how much the membership is a recognition or a commitment (or both). There was interest in particular in sharing more calls to action with members.</li> <li>Content of the website. A long-standing area for improvement (which we’re working on!)</li> </ul> <p>All in all this was a good opportunity for further transparency, and to find people who might be interested in contributing to those areas of our work in the future.</p> <p><img alt="Sideways shot of a presentation at the annual meeting of DSF members, with a sankey diagram of Django finances on the right, the speakers in the middle, and audience on the left" src="https://media.djangoproject.com/blog/images/2025/05/202504_DjangoConEurope2025_annual_meeting_DSF_members.jpg"></p> <h3 id="s-birthday-celebrations">Birthday celebrations</h3> <p>There was a cake (well, three!). Candles to blow out. And all-around great vibes and smiles, with people taking pictures and enjoying specially-made Django stickers!</p> <p><img alt="Seven people standing in a tight group behind a golden picture frame, smiling at the camera. Two hold birthday cake props that say 'Django' and '20'" src="https://media.djangoproject.com/blog/images/2025/05/202504_DjangoConEurope2025_Django_accessibility_team_and_friends.jpg"></p> <p><img alt="Four people in a group photo, smiling, holding a golden picture frame" src="https://media.djangoproject.com/blog/images/2025/05/202504_DjangoConEurope2025_Django_contributors_Emma_David_Thibaud_Mariusz.jpg"></p> <p><img alt="Close-up of three birthday cakes covered in lit candles" src="https://media.djangoproject.com/blog/images/2025/05/202504_DjangoConEurope2025_birthday_cake_close-up.jpg"></p> <iframe width="560" height="315" src="https://www.youtube-nocookie.com/embed/QScxBJQFgPg?si=oYZgaD2sn_wVeZng" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen></iframe> <hr> <h3 id="s-up-next">Up next</h3> <p>We have a lot more events coming up this year where the Foundation will be present, and bringing celebrations of Django’s 20th birthday!</p> <h4 id="s-pycon-us-2025"><a href="https://us.pycon.org/2025/">PyCon US 2025</a></h4> <p>It’s on, now! And <a href="https://www.djangoproject.com/weblog/2025/apr/18/see-you-at-pycon-us-in-pittsburgh/">we’re present, with a booth</a>. Come say hi! There will be Django stickers available:</p> <p><img alt="Close-up of Django pony stickers on a table, with 8 different designs" src="https://media.djangoproject.com/blog/images/2025/05/202504_DjangoConEurope2025_pony_stickers_close-up.jpg"></p> <h4 id="s-pycon-italia-2025"><a href="https://2025.pycon.it/">PyCon Italia 2025</a></h4> <p>Some of the PyCon Italia team was there at DjangoCon Europe to hype up their event – and we’ll definitely be there in Bologna! They promised better coffee 👀, and this will have to be independently verified. Check out their <a href="https://2025.pycon.it/en/event/djangonauts">Djangonauts at PyCon Italia</a> event.</p> <p><img alt="Four people on stage, presenting PyCon Italia 2025 in a lightining talk, holding inflatable pony balloons" src="https://media.djangoproject.com/blog/images/2025/05/202504_DjangoConEurope2025_PyCon_Italia_team_on_stage.jpg"></p> <h4 id="s-europython-2025"><a href="https://ep2025.europython.eu/">EuroPython 2025</a></h4> <p>We got to meet up with some of the EuroPython crew at DjangoCon Europe too, and we’ll definitely be there at the conference too, as one of their <a href="https://ep2025.europython.eu/community-partners/">EuroPython community partners</a> 💚. There may well be birthday cake there too, get your tickets!</p> <p><img alt="Three people in a group photo, smiling, holding a golden picture frame. Two hold birthday cake props that say 'Django' and '20'" src="https://media.djangoproject.com/blog/images/2025/05/202504_DjangoConEurope2025_EuroPython_friends_Vicky_Mia_Shekhar.jpg"></p> <h4 id="s-django-events">Django events</h4> <p>And if you haven’t already, be sure to check out our next flagship Django events!</p> <ul class="list-events"> <li> <a href="https://2025.djangocon.africa" target="_blank">DjangoCon Africa 2025</a> <span class="meta"><span>Aug. 11, 2025</span> | Arusha, Tanzania 🇹🇿</span> <i class="icon icon-calendar"></i> </li> <li> <a href="https://2025.djangoday.in/" target="_blank">DjangoDay India</a> <span class="meta"><span>Nov. 2, 2025 (tentative)</span> | Bangalore, India 🇮🇳</span> <i class="icon icon-calendar"></i> </li> <li> <a href="https://2025.djangocon.us" target="_blank">DjangoCon US 2025</a> <span class="meta"><span>Sept. 8, 2025</span> | Chicago, Illinois, USA 🇺🇸</span> <i class="icon icon-calendar"></i> </li> </ul> <hr> <p>Thank you to everyone who joined us at DjangoCon Europe, and thank you to the <a href="https://2025.djangocon.eu/about/credits/">team behind the conference</a> in particular ❤️. DjangoCon Europe continues to show the strength and warmth of our community, proving that the best part of Django is truly the people. See you at the next one!</p> <p>PS: if you’re in Europe and like organizing big events, do <a href="https://www.djangoproject.com/contact/foundation/">reach out</a> to talk about organizing a DjangoCon Europe in your locale in the coming years.</p>Sarah Abderemane, Thibaud Colas, Tom CarrickWed, 14 May 2025 23:55:48 -0500https://www.djangoproject.com/weblog/2025/may/14/djangocon-europe-and-beyond/