Everything to latest: Django 6.1, ApexCharts 6, Alpine 3.16, HTMX 2.0.10,
Tailwind 4.3, Node 24, Postgres 18. Verified with the full unit suite
(1,066 tests incl. new admin-page coverage), the Playwright E2E suite
(67/78 — the 11 failures are identical on 0.19.1, i.e. stale tests, not
regressions), a crawl of every nav page A/B'd before/after (same 45
charts / 75 series render, JS errors 70 → 6, all six pre-existing),
pixel-diffed themed-admin screenshots on Django 6.0.4 vs 6.1, and a
fresh-clone Docker build + compose boot on the new base images.
**Everything to latest: Django 6.1, ApexCharts 6, Alpine 3.16, HTMX 2.0.10,
Tailwind 4.3, Node 24, Postgres 18.** Verified with the full unit suite
(1,066 tests incl. new admin-page coverage), the Playwright E2E suite
(67/78 — the 11 failures are identical on 0.19.1, i.e. stale tests, not
regressions), a crawl of every nav page A/B'd before/after (same 45
charts / 75 series render, JS errors 70 → 6, all six pre-existing),
pixel-diffed themed-admin screenshots on Django 6.0.4 vs 6.1, and a
fresh-clone Docker build + compose boot on the new base images.
Changed — Python (59 packages in the lock; direct deps below)
| Package | 0.19.1 | 0.20.0 |
|---|---|---|
| django | 6.0.4 | 6.1 |
| django-ninja | 1.6.2 | 1.6.3 |
| daphne | 4.2.1 | 4.2.3 |
| weasyprint | 68.1 | 69.0 |
| pillow | 12.2.0 | 12.3.0 |
| qrcode | 7.4.2 (was capped ~=7.4) | 8.2 |
| pyotp | 2.9.0 (was capped ~=2.9) | 2.10.0 |
| gunicorn | 25.3.0 | 26.0.0 |
| requests | 2.33.1 | 2.34.2 |
| python-dotenv | 1.2.2 | 1.2.3 |
| sentry-sdk (sentry extra) | 2.58.0 | 2.68.0 |
| mssql-django (mssql extra) | 1.7.1 | 1.8.0 |
| django-prometheus (metrics extra) | 2.4.0 | 2.6.0.dev4 — see note |
| pytest / pytest-django / pytest-asyncio | 9.0.3 / 4.12 / 1.3 | 9.1.1 / 4.14 / 1.4 |
| pytest-playwright / playwright | 0.7.2 / 1.59 | 0.9.0 / 1.62 |
| ruff | 0.15.12 | 0.16.3 |
Already at latest, unchanged: whitenoise 6.12, psycopg 3.3.4, channels
4.3.2, channels-redis 4.3, authlib 1.7.2, openpyxl 3.1.5, factory-boy
3.3.3, pyodbc 5.3. pyproject.toml floors were raised to the versions
above (django>=6.1,<6.2, qrcode>=8.2, pyotp>=2.10, …).
Django 6.1 notes. The themed admin (25 template overrides) needed no
changes: upstream reformatted its admin JS and added CSP-nonce plumbing,
but the DOM the theme styles is unchanged — confirmed by pixel-diffing
index / changelist / change-form / delete pages against 6.0.4. The one
visible upstream change is the empty-choice label in selects, now
"- Select an option -" instead of "---------". Django 6.1 also introduces
RemovedInDjango70Warnings that touch three spots in this codebase —
select_related() with no arguments (apps/customers/views.py), the
fail_silently mail argument (apps/notifications/dispatch.py), and the
EMAIL_BACKEND setting (superseded by MAILERS). All still work through
6.x; they're follow-ups before Django 7.0 (Dec 2027).
django-prometheus caveat. Its latest stable (2.5.0) caps
Django<6.1. Rather than hold Django back for an opt-in extra, the
metrics extra pins the maintainers' 2.6.0.dev4 pre-release (which
allows <6.2); it passes the metrics tests. Switch to >=2.6 when 2.6.0
final ships.
Changed — front-end
- ApexCharts 3.54.1 → 6.10.0 (CDN pin + SRI). Every chart on
/, /charts/, the four dashboards and /pages/widgets/ renders identically (screenshot-compared), theme toggling re-renders correctly, tooltips work. No changes to static/js/charts.js were needed.
- Alpine.js 3.14.1 → 3.16.2, htmx 2.0.3 → 2.0.10 (CDN pins + SRI, in both
base.html and the admin base.html).
- Tailwind CSS /
@tailwindcss/cli 4.2.4 → 4.3.3; rebuilt app.css.
- EasyMDE 2.20.0 → 2.21.0, re-vendored via
npm run vendor:easymde.
Changed — toolchain / images
- GitHub Actions:
checkout v4→v7, setup-node v4→v7, upload-artifact v4→v7, astral-sh/setup-uv v3→v10.0.1 (exact tag — setup-uv stopped publishing floating majors after v7); CI Node 20→24 (Node 20 has been end-of-life since April 2026 — the runners were warning about it).
- Dockerfile CSS stage
node:20-slim → node:24-slim. Runtime stays on python:3.13-slim (3.13 remains the documented recommendation; 3.14 is supported and a one-line change).
- docker-compose
postgres:16-alpine → postgres:18-alpine. Postgres 18 moved PGDATA and its VOLUME to /var/lib/postgresql, so the named volume mounts there now. **A pg_data volume created by the old 16 stack is incompatible** — run docker compose down -v once (demo data reseeds on boot).
- Docs:
getting-started.md no longer claims Python 3.14 is unsupported (stale since 0.19.x); Node 22+ noted where Node 20+ was.
Fixed
pwa.js loaded after Alpine, so x-data="apexInstallPrompt()" on the install banner threw apexInstallPrompt is not defined (plus a follow-on visible is not defined) on every dashboard page — 64 JS errors across the 39 nav pages, and a PWA install banner that could never show. Moved before Alpine alongside shell.js, which already carried the comment explaining why. Errors on the crawl: 70 → 6, the remaining six being pre-existing and unrelated (see below).
- E2E suite throttled itself into 30-second timeouts.
LoginThrottleMiddleware counts every POST to /accounts/login/ (10 per username per 5 minutes) in the process-local cache, nearly every E2E test signs in as demo, and nothing reset the cache between tests — so from the 11th login onward the server answered 429 and each later test timed out waiting for the post-login redirect. That is exactly the 29-timeout failure CI's Playwright job showed. tests/e2e/conftest.py now clears the cache around every test.
Added
apps/core/tests/test_admin_pages.py — walks every registered ModelAdmin (changelist, search, add, change, history, delete-confirm) plus index / login / password-change / jsi18n on a seeded DB. Cheap insurance for the themed admin against future Django bumps.
Known, not addressed here (pre-existing)
Surfaced by the crawl, unrelated to the upgrade: a `SyntaxError:
Unexpected token ';' in an inline script on /pages/forms/`;
data.forEach is not a function on /pages/maps/; one CSP
inline-script violation on admin change forms; the scatter chart's
y-axis printing 10.000000000000002.