Skip to main content

Incremental Static Revalidation for Django (SWR + tag-based invalidation + warmup + CDN connectors).

Project description

django-edge-isr 🚀

GitHub stars

If this project is useful to you, please star the repo ⭐️. Your support helps us prioritize features and keep improving!

Incremental Static Revalidation for Django — get static-like speed with dynamic freshness. Serve fast cached pages at the edge (CDN or proxy) while revalidating in the background and rebuilding only what changed.

⚠️ Alpha — APIs may evolve. Feedback & contributions welcome!

PyPI Python versions License Docs


📚 Documentation


💡 Why

Keeping Django pages fresh without over-purging is hard:

  • TTL vs correctness — wait for TTL (stale) or purge everything (origin stampede)
  • 🧩 Ad-hoc invalidation — brittle, duplicated per view/model
  • 🌐 CDN gap — Django doesn’t natively speak modern edge semantics like SWR or URL-level purges
  • 🧠 No first-class dependency mapping — “this page depends on these objects” is missing

django-edge-isr brings a modern ISR + SWR developer experience inside Django, without a static site generator.


✨ Current status (alpha)

  • @isr(...) for views (full pages)
  • SWR headers via middleware (public, s-maxage, stale-while-revalidate)
  • Redis tag graph mapping url ↔ tags
  • Revalidation & warmup (inline queue by default)
  • Admin/status endpoints
  • 🧪 Cloudflare connectorexperimental
  • 🛠️ Planned: CloudFront connector, Celery/RQ adapters, fragment decorator, metrics, improved Admin UX

No optional install extras (Celery/RQ/CloudFront) yet — we’ll announce them once stabilized.


⚙️ Install

pip install django-edge-isr

Requirements: Python 3.10+, Django 4.2/5.x, Redis (for the tag graph & job state)


🚀 Quickstart

1) Settings

# settings.py
INSTALLED_APPS += ["edge_isr"]

MIDDLEWARE += [
    "edge_isr.middleware.EdgeISRMiddleware",  # injects default SWR headers
]

EDGE_ISR = {
    "REDIS_URL": "redis://localhost:6379/0",
    "DEFAULTS": {"s_maxage": 300, "stale_while_revalidate": 3600},

    # Optional (experimental): Cloudflare connector
    # "CDN": {"provider": "cloudflare", "zone_id": "...", "api_token": "..."},

    # Queue: inline for dev; Celery/RQ planned
    # "QUEUE": {"backend": "celery", "queue_name": "edge_isr"},
}

2) Tag your pages

# urls.py
from edge_isr import isr, tag

@isr(tags=lambda req, post_id: [tag("post", post_id)], s_maxage=300, swr=3600)
def post_detail(request, post_id):
    post = Post.objects.select_related("category").get(pk=post_id)
    request.edge_isr.add_tags([tag("category", post.category_id)])  # add dynamic tags
    return render(request, "post_detail.html", {"post": post})

3) Revalidate on data changes

# models.py
from django.db.models.signals import post_save, post_delete
from django.dispatch import receiver
from edge_isr import revalidate_by_tags, tag

@receiver([post_save, post_delete], sender=Post)
def _post_changed(sender, instance, **kw):
    revalidate_by_tags([tag("post", instance.pk), tag("category", instance.category_id)])

Admin / status (optional)

# urls.py
from django.urls import include, path
urlpatterns = [
    # ...
    path("edge-isr/", include("edge_isr.admin.urls")),
]

🧠 Concepts

  • Tags — strings like post:42, category:7 declared by views (and fragments later)
  • Tag Graph — Redis keeps tag → {urls} and url → {tags}
  • Revalidation — on changes, resolve URLs by tag, purge (optional CDN), then warm
  • SWR — responses include Cache-Control: public, s-maxage=N, stale-while-revalidate=M (+ ETag when appropriate)

✅ Compatibility

  • Python: 3.10+
  • Django: 4.2, 5.x
  • Redis: required
  • CDN: optional (Cloudflare experimental), CloudFront planned
  • Queues: inline today; Celery/RQ planned

🚫 When not to use

  • Heavily personalized or private pages (varies by user/cookie)
  • Non-idempotent endpoints

🗺️ Roadmap

  • v0.1: SWR headers, manual tags, Redis tag graph, Cloudflare purge (experimental), warmup, basic Admin
  • v0.2: CloudFront connector, auto-tagging helpers, fragment decorator
  • v0.3: Admin UX, metrics, locale/device cache keys, smarter warmup (rate-limit, batching)

❓ FAQ

Do I need a CDN? No. You can start locally or behind Nginx/Varnish. A CDN gives global edge caching and instant purges.

How do you avoid origin stampede? SWR serves a stale version while a single background warmup refreshes the cache.

How do I tag template fragments? Planned for v0.2. In v0.1 use @isr on full views. A possible future API (subject to change):

Python — fragment decorator:

from edge_isr import isr_fragment, tag

@isr_fragment(tags=lambda post: [tag("post", post.id)], s_maxage=300, swr=3600)
def render_post_card(post):
    ...

Django template — fragment cache tag:

{% raw %}

{% isrcache "post_card" tags=["post:{{ post.id }}"] %}
  {% include "components/post_card.html" %}
{% endisrcache %}

{% endraw %}


🤝 Contributing

Issues & PRs welcome! See the guide: https://hamabarhamou.github.io/django-edge-isr/contributing/


📄 License

MIT

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

django_edge_isr-0.0.8.tar.gz (12.6 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

django_edge_isr-0.0.8-py3-none-any.whl (15.6 kB view details)

Uploaded Python 3

File details

Details for the file django_edge_isr-0.0.8.tar.gz.

File metadata

  • Download URL: django_edge_isr-0.0.8.tar.gz
  • Upload date:
  • Size: 12.6 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.12.11

File hashes

Hashes for django_edge_isr-0.0.8.tar.gz
Algorithm Hash digest
SHA256 28283d340a8b6ef982e83a638f4f86555818e5782a204bc08708cf118a9887ca
MD5 69e5070bbe5a4c7f3b3dd3da62b887d5
BLAKE2b-256 23931d0eae97e16adb6d285839db38922d5cf813426591cb6a64729b9752829f

See more details on using hashes here.

File details

Details for the file django_edge_isr-0.0.8-py3-none-any.whl.

File metadata

File hashes

Hashes for django_edge_isr-0.0.8-py3-none-any.whl
Algorithm Hash digest
SHA256 68c10409a3b5ad7ead71ca6d155874f4e8ce0fd52e29e5cb9689b34cece20caf
MD5 984e116aa8976403592ab6c70cc449bb
BLAKE2b-256 55858c9a7d2ca53680e6e14b2f63c4c4975b1708e0651f1e39271816b29e0b90

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page