11 May 2026

feedDjango community aggregator: Community blog posts

Improving First Byte and Contentful Paint on a Django Website

Recently I have been experimenting with http streaming and realized how it can improve page performance. If you come from the PHP world, you might know the command flush(). It immediately sends to the visitor what has been echoed to the buffer, and doesn't wait for the full page to be rendered on the server side. That allows the browser to start rendering the website before the whole document is rendered on the server and transferred. On the other hand, the usual Django HttpResponse renders the whole HTML document on the server first, and only then sends it to the visitor. So the initial HTML document rendering is always the bottleneck for the full page load. Here comes StreamingHttpResponse, which can be used to mimic what flush() does in PHP.

HttpResponse vs. StreamingHttpResponse in Action

When using a normal HttpResponse, the HTML document is first rendered on the server side, then sent to the browser, then static files are downloaded in parallel if possible, and lastly rendering in the browser happens.

Django Streaming Waterfall Comparison

When you use StreamingHttpResponse, you can send the <head> and the content above the fold as the first part of the document, so that static files can be located and start downloading while the rest of the HTML document is being sent in parts. The first paint of the document would happen just after the CSS file is downloaded, and the rest of the HTML document would be drawn at a later point.

Generic HTML Streaming View

Here is a generic HTMLStreamingView that expects a list of template files, get_document_context_data() for the global context, and get_template_context_data() for the template-specific context:

from django.http.response import StreamingHttpResponse
from django.conf import settings
from django.template.loader import render_to_string
from django.views.generic.base import View


class HTMLStreamingView(View):
    # templates for different parts of the document
    template_names = []  
    extra_context = None

    def get(self, request, *args, **kwargs):
        # Capture the nonce before StreamingHttpResponse is returned. 
        # CSP middleware writes the nonce into the response header during
        # process_response, then replaces request.csp_nonce with 
        # an error-raising lazy object. generate() restores the plain value
        # so templates can access it during streaming.
        self._csp_nonce = (
            str(request.csp_nonce)
            if hasattr(request, "csp_nonce") 
            else None
        )
        context = self.get_document_context_data(**kwargs)
        return StreamingHttpResponse(
            self.generate(context), 
            content_type="text/html"
        )

    def generate(self, context):
        if self._csp_nonce is not None:
            self.request.csp_nonce = self._csp_nonce
        for template_name in self.template_names:
            template_context = {
                **context, 
                **self.get_template_context_data(template_name)
            }
            yield render_to_string(
                template_name, 
                template_context, 
                request=self.request
            )

    def get_document_context_data(self, **kwargs):
        kwargs.setdefault("view", self)
        if self.extra_context is not None:
            kwargs.update(self.extra_context)
        return kwargs

    def get_template_context_data(self, template_name, **kwargs):
        return {}

Use Case with the Strategic Prioritizer "1st things 1st"

The start page of the decision support system and strategic prioritizer 1st things 1st has been implemented as a multi-section landing page. The cookie consent widget only showed up after the whole page had rendered, resulting in a delay of a few seconds.

This is how I used HTMLStreamingView to reorganize the page into parts:

class StartPageView(HTMLStreamingView):
    template_names = [
        "startpage_index_top.html",
        "startpage/includes/description.html",
        "startpage/includes/tutorial.html",
        "startpage/includes/benefits.html",
        "startpage/includes/social_proof.html",
        "startpage/includes/testimonials.html",
        "startpage/includes/about_us.html",
        "startpage/includes/questions_and_answers.html",
        "startpage/includes/pricing.html",
        "startpage/includes/cause.html",
        "startpage/includes/call_to_action.html",
        "startpage/includes/footer.html",
        "startpage_index_bottom.html",
    ]

    def get_template_context_data(self, template_name, *args, **kwargs):
        if template_name == "startpage_index_top.html":
            return {
                "structured_data": settings.JSON_LD_STRUCTURED_DATA,
            }
        if template_name == "startpage/includes/social_proof.html":
            from django.contrib.auth import get_user_model

            User = get_user_model()
            return {
                "active_user_count": User.objects.filter(is_active=True).count(),
            }
        ...

        return super().get_template_context_data(template_name, **kwargs)

To transform a normal Django view into an HTTP streaming view, I cut the base.html template into two pieces:

  1. everything before {% block content %} as base_top.html - the head and content above the fold.
  2. everything after {% endblock content %} as base_bottom.html - the closing HTML tags and the footer.

For example, here's base_top.html:

<!DOCTYPE html>
{% load static %}
<html lang="en">
<head>
    <meta charset="utf-8" />
    <title>{% block title %}1st things 1st{% endblock %}</title>
    <meta name="viewport" content="width=device-width, initial-scale=1" />
    <link rel="stylesheet" href="{% static 'css/styles.css' %}" />
    {% block extra_head %}{% endblock %}
</head>
<body>
    {% block top_navigation %}
        <nav>
            <a href="/">Logo</a>
        </nav>
    {% endblock %}
    <main id="main_content">
    {% block content %}{% endblock %}
    {% include "startpage/includes/extra_js.html" %}

And here is base_bottom.html:

    {% block content %}{% endblock %}
    </main>
    <footer>
        ...
    </footer>
</body>
</html>    

I moved the JS from base_bottom.html to the body section of base_top.html, where it will start downloading immediately after the content above the fold is shown. I did that to reduce the delay for the cookie consent widget.

Then I prepared the templates for all parts of the start page:

  1. startpage_index_top.html extends base_top.html
  2. content templates provide the HTML directly without extending anything.
  3. startpage_index_bottom.html extends base_bottom.html.

The Optimization Results

I used the Lighthouse plugin to measure performance for the start page on an emulated slow mobile network, before and after applying StreamingHttpResponse.

PageSpeed performance with HttpResponse

In the updated version, the content above the fold and the static files needed to render it are retrieved earlier. These include the static file requirements for the cookie consent widget, which can now be loaded from the initial part of the stream, so the widget appears sooner.

PageSpeed performance with StreamingHttpResponse

Final Words

HTTP streaming is a relatively simple technique that can make a noticeable difference in perceived page performance, particularly when it comes to metrics like First Byte and Contentful Paint. By sending the top of the document early, the browser can begin fetching static assets and rendering above-the-fold content while the server is still working on the rest of the page.

A faster Time To First Byte (TTFB) is also worth considering for LLM crawlers such as GPTBot or ClaudeBot. These bots often work with short timeouts, and if your server doesn't respond quickly enough, they may abandon the request before reading your content. HTTP streaming helps here too, since it gets the most important parts of your HTML out early - right at the top of the document where crawlers are most likely to see them.

That said, it does require splitting your templates into parts and thinking more carefully about which context data is needed where. If your page is lightweight and fast to render, the added complexity probably isn't worth it. The technique really shines on heavier pages that involve bigger database queries or external API calls - those are exactly the cases where server-side delay is most significant, and where streaming can therefore have the greatest impact.

It is also worth noting that HTTP streaming works with both WSGI and ASGI, so it fits into most standard Django deployment setups without requiring any major infrastructure changes.


Thanks to Famitsay Tamayo for the cover photo!

11 May 2026 5:00pm GMT

08 May 2026

feedDjango community aggregator: Community blog posts

Issue 336: Google Summer of Code 2026 Contributors Announced

News

Announcing the Google Summer of Code 2026 contributors for Django

After receiving over 200 proposals from contributors across the world, four were selected for this year's GSOC batch.

Django security releases issued: 6.0.5 and 5.2.14

Three CVE-level security issues fixed. As ever, updating to the latest version of Django is a highly-recommended security practice.


Releases

Python 3.14.5 release candidate

Python 3.14.5 has a release candidate available, inviting testing before the final cut. Watch for any regressions or packaging issues while you validate your apps and dependencies against the RC.


Updates to Django

Today, "Updates to Django" is presented by Pradhvan from Djangonaut Space! 🚀

Last week we had 16 pull requests merged into Django by 15 different contributors - including 4 first-time contributors! Congratulations to Raoni Timo de Castro Cambiaghi, Anna Makarudze 🚀 , Fashad Ahmed, and Tilda Udufo for having their first commits merged into Django - welcome on board! 🥳

This week's Django highlights: 🦄

The Django Software Foundation announced 4 accepted projects for this year's Google Summer of Code , congratulations to Praful Gulani, p-r-a-v-i-n, Keha Chandrakar, and Varun Kasyap Pentamaraju for having their proposals selected! 🎉

That's all for this week in Django development! 🐍🦄


Sponsored Link

Middleware, but for AI agents

Django middleware composes request handlers. Harnesses do the same for AI agents - Claude Code, Codex, Gemini in one coordinated system. Learn what a harness actually is, why it's a new primitive, and how to engineer one that holds in production. Apache 2.0, open source.


Wagtail CMS News

A customizable page explorer and other quality improvements in Wagtail 7.4

Wagtail 7.4 adds a customizable page explorer plus a set of quality improvements. If you rely on the admin's navigation and editor workflows, this release is about making those experiences more adaptable and smoother.

Independent security audit: findings and next steps

An independent security audit lays out its findings and the specific next steps to address them. If you maintain a Django app, use the recommendations to prioritize fixes, validate risk areas, and plan follow-up checks.

The carbon footprint of Wagtail AI

An interesting overview of Wagtail's AI footprint and more broadly links to carbon emissions by task during model inference (hint, hint image generation is expensive).


Articles

Using Django Tasks in production

The Djangonaut Space website has been using the Django Tasks framework and django-tasks-db in production successfully for about six months now. Some lessons learned from the integration in this article by Tim Schilling.

Thoughts from DjangoCon Europe

Carlton Gibson was a keynote speaker and shares highlights from the talks, events, and hallway chats that make the conference special.

Easily Stream LLM Responses with Django-Bolt and PydanticAI

Quickly setup an async streaming endpoint using django-bolt and PydanticAI in this tutorial from Caktus.

Python Unplugged on PyTV: Key Takeaways From Our Community Conference

A recap from March's PyTV digital conference, featuring talks from several Django figures including Mark Smith, PyLadies panel, Carlton Gibson on typing, Sarah Boyce on debunking Django myths, Sheena on Django development with Claude Code, and more.

Start Django Project

Get a clean start with a focused checklist for bootstrapping a Django project. Once the project skeleton is in place, you can build your apps, configure settings, and begin wiring URLs and models without rework later.

Core Dispatch #3

Core Dispatch #3 highlights what is landing in the Django core stream and what developers should pay attention to next. A quick roundup to keep your plans aligned with current core movement.

django-prodserver design updates

A quick rundown of recent design updates for the Django production server. Includes the key decisions behind the changes so you can track what's different in your deploy setup.

Me and Mentorship

A personal look at mentorship, focused on the practical habits and mindset shifts that shape both mentors and mentees. Useful reading if you want to think about how to support others in a way that actually sticks.

Png - I resolved my issue with virtual env and Docker.

A quick look at how to sort out Python virtual environment problems by leaning on Docker instead. The workflow focuses on getting dependencies and runtime isolation aligned so your Django setup stops fighting you.


Events

Asking the Key Questions: Q&A with the PyCon US 2026 keynote speakers: Rachell Calhoun and Tim Schilling

Rachell and Tim tease their upcoming talks on Djangonaut Space at PyCon US and highlight open source projects worth knowing about.

PyTexas 2026 Recap

A very deep-dive into the conference, with pictures, discussion of talks, new ideas, and more.


Podcasts

Django Chat #202: EuroPython 2026 - Mia Bajić

Mia is Vice Chair of the EuroPython society, a regular conference speaker, podcast host, and software engineer. We discuss what to expect at this year's event in Krakow, Poland in July this summer.


Projects

jsheffie/django-schematic

An interactive graph of your Django model structure. With related blog post.

archmonger/django-dbbackup

This Django application provides management commands to help backup and restore your project database and media files with various storages such as Amazon S3, Dropbox, local file storage, or any Django-supported storage.

08 May 2026 3:00pm GMT

PyGrunn: list-man, pragmatic system integration - Doeke Zanstra

(One of my summaries of the 2026 one-day PyGrunn conference in Groningen, NL).

When automating in a big company with many systems, you often end up with spaghetti: many systems connecting to a lot of the others... A common solution is to have a "bus architecture". Generic existing "enterprise service bus" solutions were clearly overkill, so he proposed an alternative solution.

He made a couple of assumptions/choices. All data is tabular data. He wanted to store a copy of data in a database. SQL views to access the data. So: multiple sources that he wanted to import in a central database (which would function as a sort of "read-only enterprise service bus"). And a generic sql/view-based way of accessing the data.

He initially focused on read-only data. And he started real simple. Just a bash script that ran regularly that scraped data from other systems and injected it in the database.

In the second version of the system, for every system he wrote a target/command in a Makefile. Every thing that needed to be scraped got its own table (called a "list" in his system"). Lists could be compared. The first killer app was a comparison between a telephone list and the list of employees so that differences could be consolidated.

For the third version, he started using more and more python. CSV file imports. Downloaders from REST APIs. All configurable so that he could use the same python script for many different sources.

He now had a simple sytem for which he could write views and exports.

  • Publishing data on the intranet via the "jekyll" static site generator. For instance a "mug book" of all employees.
  • And regularly exporting a list of names+emailaddresses in a format suitable for the multifunctional printer: to make it easy to select your email address when scanning on the printer.
  • An export to a google spreadsheet that combined the holiday spreadsheet with the data on part-time days.

Security was handled with a role-based system.

https://reinout.vanrees.org/images/2026/lac-de-kruth6.jpg

Unrelated photo: the "lac de Kruth-Wildenstein" reservoir during a family holiday in France in 2006.

08 May 2026 4:00am GMT