About Streaming HTML Documents
StreamingHttpResponse can be used not only for large audio, video, or other binary files, but also for text-based content, for example, a large HTML document of a few megabytes with data from the database.
import asyncio
import html
from asgiref.sync import sync_to_async
from django.http import StreamingHttpResponse
from .models import Trick
async def streaming_demo_html(request):
async def generate():
yield '''
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Streaming Tricks Demo</title>
</head>
<body>
<h1>Streaming Tricks</h1>
<p>Each trick is streamed from the database in chunks of 10.</p>
<hr>
'''
await asyncio.sleep(0) # yield control to other requests
chunk_size = 10
offset = 0
count = 0
def fetch_chunk(offset_val):
return list(
Trick.objects.filter(
publishing_status__in=(
Trick.PUBLISHING_STATUS_PUBLISHED,
Trick.PUBLISHING_STATUS_PUBLISHED_OUTDATED,
)
)
.prefetch_related("categories", "technologies")
[offset_val:offset_val + chunk_size]
)
get_chunk = sync_to_async(fetch_chunk)
while True:
chunk = await get_chunk(offset)
if not chunk:
break
for trick in chunk:
count += 1
safe_title = html.escape(trick.title)
safe_url = html.escape(request.build_absolute_uri(trick.get_url_path()))
yield f'''
<div>
<small>Trick #{count}</small>
<h2><a href="{safe_url}">{safe_title}</a></h2>
</div>
'''
yield " " * 1024 # padding to force browser to flush and render
await asyncio.sleep(0.5) # yield control between chunks
del chunk
offset += chunk_size
yield f'''
<hr>
<p>Done! All {count} tricks streamed.</p>
</body>
</html>
'''
return StreamingHttpResponse(
generate(),
content_type="text/html; charset=utf-8"
)
Tips and Tricks Programming Optimization Django 6.x Django 5.2 ASGI WSGI Uvicorn Daphne HTTP streaming
Also by me
Django Messaging
For Django-based social platforms.
Django Paddle Subscriptions
For Django-based SaaS projects.
Django GDPR Cookie Consent
For Django websites that use cookies.