Display a maintenance countdown banner and block access to a Django site when the countdown expires.
Project description
django-countdown
Display a maintenance countdown banner across a Django site, then block public access (returning HTTP 503) when the countdown expires. Superusers retain access during maintenance so they can finish the work and clear the countdown.
Why?
Planned downtime is the worst kind of downtime to communicate badly. Users
land on a half-broken page mid-deploy, hit error logs, file support tickets,
and trust erodes. django-countdown lets you announce a maintenance window
before it starts (a countdown banner with a real timer), then during the
window swap public traffic for an explicit "we're in maintenance" page —
while leaving operators unblocked so they can actually finish the work.
Features
- Pre-maintenance banner — an ultra-visible countdown banner inserted into templates via context processor, with a JS timer that ticks live.
- Hard cutoff at expiry — middleware returns HTTP 503 and renders a branded blocked page once the countdown lapses.
- Superuser bypass — admins keep working through the cutoff so they can fix the underlying issue and clear the countdown.
- Maintenance window — optional
maintenance_untillets you set a target end-time; a second banner appears for superusers and the blocked page shows a live countdown to recovery. - Per-Site configuration — uses Django's
sitesframework, so each domain in a multi-tenant setup has its own independent countdown. - Admin integration — full Django admin support with status colors and a validator preventing past-dated countdowns.
Supported versions
Authoritative upstream: https://docs.djangoproject.com/en/dev/faq/install/#what-python-version-can-i-use-with-django
| Django | 3.10 | 3.11 | 3.12 | 3.13 | 3.14 | Status |
|---|---|---|---|---|---|---|
| 5.2 LTS | ✓ | ✓ | ✓ | ✓ | ✓ | Active LTS (extended support Apr 2028) |
| 6.0 | — | — | ✓ | ✓ | ✓ | Mainstream Aug 2026, extended Apr 2027 |
8 cells in total are exercised by the CI matrix on every push.
Installation
Using uv (recommended)
uv add django-countdown
Using pip
pip install django-countdown
Project configuration
Add to INSTALLED_APPS — the django.contrib.sites framework must also be
installed:
INSTALLED_APPS = [
# ...
"django.contrib.sites",
"django_countdown",
]
SITE_ID = 1
Add the blocking middleware after Django's auth middleware (it needs
request.user):
MIDDLEWARE = [
# ...
"django.contrib.auth.middleware.AuthenticationMiddleware",
"django_countdown.middleware.CountdownBlockingMiddleware",
]
Add the context processor so the banner partials see the active countdown:
TEMPLATES = [
{
# ...
"OPTIONS": {
"context_processors": [
# ...
"django_countdown.context_processors.countdown_context",
],
},
},
]
Run migrations:
./manage.py migrate
Quick start
Create a SiteCountdown row (one per django.contrib.sites.Site) via the
Django admin, providing:
countdown_time— when access starts being blocked.maintenance_until(optional) — when public access resumes.message— short banner headline (max 200 chars).long_description(optional) — extended copy shown on the blocked page.
The package exposes two template partials you can {% include %} from your
own base layout:
{% include "django_countdown/countdown_banner.html" %}
Renders a banner before the countdown expires; switches to a subdued
"maintenance in progress" banner for superusers after expiry until
maintenance_until passes. Two context variables are populated by the
context processor: active_countdown (pre-maintenance) and
maintenance_countdown (during maintenance, superusers only).
The middleware automatically serves django_countdown/blocked.html for
non-superuser requests once the countdown has expired and maintenance is
ongoing.
A working end-to-end example lives under example/ — start
there if you want to see the package wired up in a minimal Django project.
Optional / template-level requirements
countdown_banner.html uses {% load compress %} (from
django-compressor) to bundle its
CSS. If you include the banner partial in your templates, install
django-compressor and add it to INSTALLED_APPS, or fork the template and
drop the {% compress %} wrapping.
blocked.html references Foundation CSS and Foundation-Icons via
{% static %}. If you serve those files from your project's static pipeline
they will resolve; otherwise the blocked page degrades visually but still
renders the maintenance message and timer.
Configuration
The package does not require any project-level Django settings. The middleware
exempts /admin/, /static/, and /media/ URL prefixes from blocking by
default.
Development
git clone https://github.com/iplweb/django-countdown.git
cd django-countdown
uv sync --all-extras
uv run pytest
License
MIT — see LICENSE for details.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file django_countdown-0.2.0.tar.gz.
File metadata
- Download URL: django_countdown-0.2.0.tar.gz
- Upload date:
- Size: 30.5 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.9.2
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
d87ed080bf91ab388c3a28acb046df8e56c392c483a369126166e641d625145c
|
|
| MD5 |
ec5baaa0b078d1b447645670c0959198
|
|
| BLAKE2b-256 |
f8cf24ef6118a39789aab8a4cf79d4e2f7e5673496bcf7961c0ce1aa465bd1b5
|
File details
Details for the file django_countdown-0.2.0-py3-none-any.whl.
File metadata
- Download URL: django_countdown-0.2.0-py3-none-any.whl
- Upload date:
- Size: 34.9 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.9.2
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
2e5f1472348329d7970e262ad30dbc82ce3d830e0b3248ecd37bba46f7292ffe
|
|
| MD5 |
f9e783acb410aa8c4628ce0335edbfb0
|
|
| BLAKE2b-256 |
fcdecfdaab369f4d73cb4941be1612cae5e6f9905b1fff0ac9394ffe6f898549
|