About The StreamingHttpResponse and Caching
StreamingHttpResponse doesn't support view caching by design. However, you can cache the individual chunks that you send to the response.
Moreover, to avoid those views to be cached by Nginx, Varnish, or Cloudflare, set the Cache-Control: no-store explicitly.
import asyncio
import json
from asgiref.sync import sync_to_async
from django.core.cache import cache
from django.http import StreamingHttpResponse
from .models import Trick
async def trick_list_json_stream(request):
def serialize(trick):
return {
"title": trick.title,
"url": request.build_absolute_uri(trick.get_url_path()),
"categories": [cat.slug for cat in trick.categories.all()],
"technologies": [tech.slug for tech in trick.technologies.all()],
}
async def generate():
yield '{"results": ['
first = True
chunk_size = 50
offset = 0
def fetch_chunk(offset_val):
cache_key = f'tricks_chunk_{offset_val}_{chunk_size}'
cached = cache.get(cache_key)
if cached is not None:
return cached
chunk = 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]
)
cache.set(cache_key, chunk, timeout=300)
return chunk
get_chunk = sync_to_async(fetch_chunk)
while True:
chunk = await get_chunk(offset)
if not chunk:
break
for trick in chunk:
if not first:
yield ','
yield json.dumps(serialize(trick))
first = False
await asyncio.sleep(0) # yield control between items
del chunk
offset += chunk_size
yield ']}'
response = StreamingHttpResponse(
generate(), content_type="application/json"
)
response["Cache-Control"] = "no-store"
return response
Tips and Tricks Programming Optimization Performance Django 6.x Django 5.2 Django 4.2 Memcached Redis ASGI 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.