Changelog¶
Changelog¶
All notable changes to django-adminlte4 are documented here. The format is
based on Keep a Changelog.
[Unreleased]¶
[0.3.1] - 2026-10-01¶
A bug-fix release: Django admin add/change pages work again with
AdminLTEFormRenderer, the Vite app is no longer loaded twice under
WhiteNoise, and the demo/starter is hardened (no superuser demo account, no
production start with the published SECRET_KEY).
Upgrading from 0.3.0¶
- Using
AdminLTEFormRenderer: nothing to do — upgrade and admin add/change pages render again. - Serving a Vite build through
ManifestStaticFilesStorageor WhiteNoise'sCompressedManifestStaticFilesStorage: switchSTORAGES["staticfiles"]todjango_adminlte4.storage.ViteManifestStaticFilesStorageorViteCompressedManifestStaticFilesStorage, thennpm run buildandcollectstatic --clear(see the assets docs). - Running the demo/starter with
DEBUG=False: make sureSECRET_KEYis set to a real key, or it will not start. Re-runseed_demoto convert an existingadminsuperuser into the limited staff demo account.
Changed — demo and starter (breaking for deployments)¶
- The starter refuses to run in production without a real
SECRET_KEY.demo/config/settings.pyused to fall back to the publisheddjango-insecure-dev-only-change-mekey wheneverSECRET_KEYwas unset, including withDEBUG=False. It now raisesImproperlyConfiguredwhenDEBUG=Falseand the key is missing, blank or that development key; withDEBUG=Truethe convenient fallback stays. SetSECRET_KEYin the environment (or.env) before deploying — see the deployment docs. - The public demo account is no longer a superuser.
seed_democreatesadmin/adminpass(configurable viaDEMO_ACCOUNT) as a staff, non-superuser account in a Demo visitors group: view permission on every model in the admin, add/change/delete on the demo data (crud) only, and nothing that grants rights (users, groups, permissions, e-mail addresses are view-only). A newDemoAccountGuardMiddlewarelets it open, but not submit, the password-change and e-mail pages (Django, admin and allauth), so a visitor can't lock the next one out; a themed403.htmlexplains why. Runningseed_demoon an existing database demotes the oldadminsuperuser and resets its password, group and permissions — idempotently, without re-hashing an unchanged password.--no-superuseris now--no-demo-user(the old flag still works). The login page shows the same credentials and says the account is a shared staff account that resets nightly.
Fixed¶
- Sidebar toggle (and every other handler) firing twice on the Components
page in production. WhiteNoise's manifest storage re-hashed Vite's
already-hashed output: the page loaded the entry as
app-XXXX.<hash>.js, while the lazily imported Quill chunk — which imports code it shares with the entry — fetched./app-XXXX.js, a second URL and so a second copy of the app. Newdjango_adminlte4.storage.ViteManifestStaticFilesStorageandViteCompressedManifestStaticFilesStorage(WhiteNoise) keep every file a Vite manifest lists under its own name, so the page and the chunks agree on one URL per module whatever the chunk graph; everything else is hashed as before.immutable_file_testgives those files WhiteNoise's far-future cache headers. The demo uses them in production, andadminlte_installpoints to them. - Django admin add/change pages no longer return 500 with
AdminLTEFormRenderer. 0.3.0 only wrapped the renderer's top-levelget_template()in a fallback to Django's form templates, but admin widget templates (admin/widgets/date.html,url.html, the related-field wrapper,auth/widgets/read_only_password_hash.html, …){% include %}built-in widget templates, and an include resolves through the engine that loaded the outer template — so every add/change form raisedTemplateDoesNotExist: django/forms/widgets/input.htmlunlessdjango.formshappened to be inINSTALLED_APPS. The renderer now renders through a private copy of the project'sDjangoTemplatesengine (sameDIRS, loaders, builtins and libraries) with Django's form templates added as the last loader, so nested includes resolve, project overrides ofdjango/forms/*still win, and it works with or withoutdjango.forms, withAPP_DIRS=Trueor explicit (cached or uncached) loaders.
[0.3.0] - 2026-10-01¶
Changed¶
- Charts are now Chart.js (MIT) instead of
ApexCharts. From 5.2 ApexCharts ships under its own commercial licence, so
it can no longer be part of a free, MIT-licensed package.
adminlte_chartkeeps its tag name and props and renders the same charts with Chart.js 4.5; all 15 demo charts (Dashboard v1, v2 and v3, and the Components page) were rebuilt with the same data and colours. The demo's chart code shrinks from 266 kB to 72 kB gzipped. - New
assets/adminlte-charts.jsstub (copied byadminlte_install): the AdminLTE theme preset for Chart.js. It reads the Bootstrap colours and font from the page intoChart.defaults, and re-themes every chart in place when the Light/Dark/Auto toggle, the text direction or the Bootstrap colour variables change. It also exportschartColor(),verticalGradient()andwithAlpha()for page scripts that draw their own charts. adminlte_chartgainscolors(palette names such asprimary/teal, CSS variables or CSS colours; palette names follow the colour mode) andlabel(the canvas's accessible name).typetakes every Chart.js type plusarea;donutandcolumnstill work as aliases.seriesaccepts the old{"name", "data"}dicts, a flat list for pie charts, or Chart.js datasets. The container fires anadminlte:chartevent with the chart instance, for callbacks that JSON cannot carry.- The demo's lazy plugin loader is now
adminlteUse("chartjs")(wasadminlteUse("apexcharts")); it resolves withChartand puts the theme helpers onwindow.adminlteCharts.
Migrating from 0.2.x¶
Nothing changes for projects that do not use adminlte_chart or the demo's
chart code. If you do:
- Copy the new front-end stubs into
assets/:adminlte-charts.js(new) andadminlte-plugins.js(it now looks fordata-chartjscontainers instead ofdata-apexchart).python manage.py adminlte_installadds the new file and skips existing ones;--forceoverwrites all stubs, including anapp.js,package.jsonorvite.config.jsyou may have edited. - Swap the npm package:
npm uninstall apexcharts && npm i chart.js. optionspassed toadminlte_chartare now Chart.js options, merged over the defaults. ApexCharts keys (chart,xaxis,yaxis,stroke,dataLabels,colors,legend,tooltip, …) are dropped with aDeprecationWarning; move them to their Chart.js equivalents (for examplecolors→ the newcolorsprop,legend.show→plugins.legend.display,plotOptions.bar.horizontal→indexAxis: "y").- Page scripts that called
new ApexCharts(el, options).render()need a<canvas>inside a sized,position: relativebox and a Chart.js config;docs/assets.mdhas the pattern, and the Dashboard v1–v3 templates are worked examples. - CSS that targeted
.apexcharts-*or[data-apexchart]can go.
Fixed¶
npm cifailed in the demo.demo/package.jsonhas asked foradmin-lte ^4.8.4since 0.2.0, but the lockfile still pinned 4.8.1. The lockfile now pins 4.8.4, the version the package bundles.- The Dashboard v1 activity chart labelled only five of its six months.
- The Dashboard v3 visitors chart drew light-grey bands across the plot in dark mode; the bands now use the theme's tertiary background.
[0.2.1] - 2026-09-30¶
Changed¶
adminlte_scaffoldnow generates views with access checks. The list view is@login_required, and the create view also requires the model'saddpermission (<app>.add_<model>), answering 403 without it. Previously the generated views were open to anonymous visitors until you added checks yourself. Apps already scaffolded are not touched; re-run with--forceor add the two decorators by hand to pick this up.- Demo: changing contacts uses Django's model permissions. Browsing the
Contacts list still only needs a sign-in, but adding, editing and deleting
now require
crud.add_contact,crud.change_contactandcrud.delete_contact— checked in the views, with the matching buttons hidden for users who lack them. The seededadminsuperuser keeps full access; accounts made through the sign-up pages get a read-only list. - The source distribution leaves out repository tooling (
CLAUDE.md,.github/, and any local.envor SQLite file). The wheel is unchanged. django_adminlte4.__version__(shown byadminlte_status) now matches the released version; it had stayed at 0.1.1 through the 0.2.0 release.
[0.2.0] - 2026-08-19¶
Changed¶
- Bundled AdminLTE updated to 4.8.4, and
ADMINLTE_VERSIONwith it. Adds the fix for every page scrolling sideways at phone widths (4.8.3) on top of the 4.8.1 work already recorded below.
Fixed¶
- Tooltips never initialised on the UI → General page. The page's inline
<script>ran before the app bundle, which is a deferred module, sobootstrapwas undefined and the call threw — leaving all four tooltip targets dead. Both that page and the Components page (whosewindow.bootstrapguard swallowed the same race, so its toast simply never appeared) now use<script type="module">, which runs after the bundle. - Mailbox rows forced 638px of horizontal scroll on a phone. The row link
and its elastic middle column were missing the
min-width: 0core carries, sotext-truncatecould not shrink below its content width. - The jsVectorMap zoom buttons rendered outside the card. Its stylesheet was
never imported, so the
+/−controls laid out as ordinary content instead of being positioned inside the map box.
Changed¶
- Dependency refresh across the demo and the packaging metadata. Everything
the demo installs from npm and everything the package declares in Python is on
its current release, with one deliberate hold-back (below). The headline is
ApexCharts 3.54 → 6.10 — three majors — covered in its own entry further
down. Also on npm: Vite 6 → 8 (a major; Vite 8 builds with Rolldown, which
cut the demo's production build from ~2.2 s to ~0.5 s and shaved ~3 % off the
emitted JS), Sass 1.80 → 1.102, Tabulator 6.3 → 6.5.2, jsVectorMap 1.6 → 1.7,
OverlayScrollbars 2.11 → 2.16, and FullCalendar 6.1.20 → 6.1.21 across
core,daygrid,interaction,listandtimegrid.admin-ltestays at 4.8.1. - Four high-severity advisories cleared in the demo's transitive tree — Vite
(
server.fs.denybypass on Windows alternate paths, plus alaunch-editorNTLMv2 hash disclosure), PostCSS (path traversal viasourceMappingURL), nanoid and immutable. These moved with the lockfile; no direct dependency changed for them. One low-severity advisory remains open against Quill 2.0.3, which is the latest published release — there is nothing to upgrade to yet. - Python dependencies verified against their current releases — Django 6.1,
django-components 0.151.1, django-filter 26.1, django-crispy-forms 2.7,
django-allauth 65.19.1, django-environ 0.14, pytest 9.1.1, pytest-django 4.14
and mkdocs-material 9.7.7 — with the full suite (74 tests) and every demo URL
green. The declared floors in
pyproject.tomlare unchanged, because they are minimums and the package still supports them; the one exception isdjango-components, whose cap moves from<0.151to<0.152now that 0.151 is tested.Framework :: Django :: 6.1joins the classifiers. - The Vite scaffold written by
adminlte_install(package.json.stub) picks up the same Sass, Vite and OverlayScrollbars versions as the demo, so a fresh project starts on the toolchain the demo is actually tested with.
Changed — ApexCharts 3 → 6¶
The demo's charting library jumps three majors. Every chart on every dashboard
was rendered in a headless browser before and after and compared, both by DOM
inspection and by screenshot; the curves, bars, slices and colours are
unchanged. Three call sites needed adjusting, all in demo templates — the
adminlte_chart component and the adminlte-plugins.js initializer that ship
in the package were already using only the stable API (new ApexCharts(el,
config).render()) and are untouched.
- Datetime axes name their own tick format now. ApexCharts 4 reworked
datetime tick generation: the monthly series on Dashboard v1 (
#revenue-chart) and Dashboard v2 (#sales-chart) started drawing a fortnightly day grid ("09 Jan", "23 Jan", …) that no longer lined up with the data points, instead of one tick per month. Both charts now setxaxis.labels.formatandtickAmountexplicitly, which restores the previous "Jan '23 … Jun '23" axis. - The donut's pinned height is sized to what it actually occupies. The
height: 350on Dashboard v2's#pie-chartexists to break an ApexCharts ResizeObserver feedback loop on browser zoom (#6019) and is still needed, but ApexCharts 3 quietly ignored it and shrink-wrapped the container to 221 px while 4+ honours it — leaving ~130 px of dead space under the chart. Pinned to 220 px instead: the workaround stands and the card is its original height. plotOptions.bar.endingShaperemoved from Dashboard v3's#sales-chart. It was deprecated in ApexCharts 3.24 and deleted in 4, and it was already a no-op — the bars render square before and after.- Note on payload: the ApexCharts chunk grows from ~142 kB to ~266 kB
gzipped. It is behind a dynamic
import(), so only pages with charts pay for it. ApexCharts 6 also ships per-chart-type entry points (apexcharts/area,apexcharts/bar, …) which would cut this substantially; adopting them means reworking thewindow.ApexChartsglobal the page scripts share, so it is left as a follow-up.
Held back¶
-
@fullcalendar/core7.0.2. Onlycorehas a stable 7 —daygrid,interaction,listandtimegridare still on 6.1.21 — so the family stays together on 6.1.x rather than mixing a v7 core with v6 plugins. -
Target AdminLTE 4.8.1 (was 4.0.0). Bumped in all three places the version is pinned — the demo's
package.json, thepackage.jsonstub written byadminlte_install, and theADMINLTE_VERSIONmarker reported byadminlte_status. The pre-built assets shipped for the Node-optional path (adminlte.min.css,adminlte.rtl.min.cssandadminlte.min.jsunderdjango_adminlte4/static/adminlte/dist/) were refreshed from the 4.8.1 dist. Upstream additions projects can now opt into: the extended palette and the AdminLTE 3 palette sheets,data-lte-primary="…"to promote a palette colour to Bootstrap'sprimary,data-lte-print="plain"for document printing,data-lte-contrast="aa"for WCAG AA text on the v3 palette, plus sidebar search and ribbons.
Fixed¶
- The Light/Dark/Auto switcher no longer runs two implementations at once.
AdminLTE has bundled its own
ColorModemodule since 4.1; the copies indemo/assets/app.jsandstatic/adminlte/dist/js/adminlte.init.js— written when 4.0 shipped none — bound a second set of handlers to the same[data-bs-theme-value]toggles. They also resolved the theme as stored → OS, ignoring the theme declared in the markup, so withADMINLTE["dark_mode"] = Trueand nothing stored they overrode the dark default and left the dropdown highlighting the wrong entry. Both copies are removed: the bundled module owns the toggles, thelte-themestorage key and the[data-lte-theme-icon]icons, and it honours the markup theme.
[0.1.1] — 2026-06-11¶
Fixed¶
- README images and documentation links are now absolute URLs, so they render on the PyPI project page (relative paths only resolve on GitHub).
adminlte_statusprints the new distribution nameadminlte-django.
[0.1.0] — 2026-06-11¶
First PyPI release, published as adminlte-django (the import name remains
django_adminlte4).
Added — performance & Django-native rendering¶
AdminLTEFormRenderer— setFORM_RENDERER = "django_adminlte4.forms.AdminLTEFormRenderer"and plain{{ form }}renders AdminLTE/Bootstrap 5 markup project-wide: per-widget-type classes (form-control/form-select/form-check-input/form-range),is-invalid+invalid-feedbackvalidation states,form-texthelp text and non-field errors as an alert. Widget templates fall back to Django's built-in form engine, sodjango.formsis not required inINSTALLED_APPS.- Django system checks replacing the startup warning:
adminlte.W001(unknown config key),adminlte.E002(django-components loader missing — theAPP_DIRS=Truefootgun, now with an actionable hint),adminlte.W003(malformed or misspelled menu items),adminlte.W004(context processor not configured). ADMINLTE["language_switcher"]— topbar dropdown posting to Django'sset_languageview.py.typedmarker — the package's type annotations are now visible to mypy/pyright.GateFilterrecurses into submenus: unauthorized children are pruned, and a parent left with no children (and no link of its own) is dropped.ActiveFilterderives auto-active patterns from the resolvedhref, soroute:-based items get active detection too.
Changed — performance¶
- The merged
ADMINLTEconfig is computed once per process (invalidated viasetting_changed); the default footer copyright year is evaluated lazily at render time instead of import time. - Menus are built lazily (
SimpleLazyObject) — pages that never render a sidebar/navbar skip the build entirely — and the filter pipeline is split: request-independent filters (HrefFilter,SearchFilter) run once per process (per active language), soreverse()no longer re-runs per request. Custom filters keep per-request semantics viaper_request = True(default). - Wildcard active-state patterns are compiled once (
lru_cache) instead of per item per request. - The demo front-end is code-split: ApexCharts, jsVectorMap, Tabulator, Quill,
SortableJS and FullCalendar load on demand via
adminlteUse()dynamic imports; the always-loaded core drops to ~46 kB gzipped. The chart-refitsetTimeouthack was replaced by a width-guardedResizeObserver. - Demo-only sample images (~2.9 MB) moved out of the pip package into
demo/static/.
Changed — demo¶
- Dashboard v1 is data-driven: ORM-fed small boxes linking to the CRUD views
and themed admin, plus an activity chart fetching six months of aggregates
from
/api/dashboard/activity.json. - The sidebar menu uses named routes (
route:) for all internal items, and a STAFF ONLY section showcasesGateFilter(callable + permission-string gating) — log in with the pre-filled credentials to see it appear. - New
/native/formpage demonstrating the form renderer; the language switcher is enabled with an English/EspañolLANGUAGESlist. seed_demospreads project start dates across ~5 months so the dashboard chart has a curve.
Added — v1 (core)¶
- AdminLTE 4 base layout (
adminlte/master.html,adminlte/page.html) with navbar, sidebar, footer, color-mode toggle and user menu partials. - Config-driven sidebar/topnav menu via
settings.ADMINLTE["menu"]with a per-request filter pipeline (GateFilter,HrefFilter,ActiveFilter,SearchFilter) — a faithful port of the Laravel package's menu system. - Settings surface (
settings.ADMINLTE) mirroringconfig/adminlte.php, merged over defaults bydjango_adminlte4.conf. - Context processor exposing config + per-request menus;
adminlte_body_classesandadminlte_titletemplate tags; anadd_classform-field filter. - Form components:
adminlte_input,adminlte_textarea,adminlte_select,adminlte_input_switch,adminlte_input_color,adminlte_input_file,adminlte_button— with bound-field validation feedback + value repopulation. - Widget components:
adminlte_card,adminlte_small_box,adminlte_info_box,adminlte_alert,adminlte_callout,adminlte_progress,adminlte_progress_group,adminlte_timeline,adminlte_description_block,adminlte_profile_card,adminlte_ratings,adminlte_breadcrumb. - Auth templates (login/register/lockscreen) wired to Django's auth views.
- Rich, data-driven topbar: Messages and Notifications dropdowns plus a full
user card, configured via
ADMINLTE["navbar_messages"],["navbar_notifications"]and["usermenu"](each optional). Falls back to a Django-user-driven menu whenusermenuis omitted. Addedlogo_alt_textandnavbar_searchconfig keys. - Vite front-end pipeline (django-vite) with
npm-installedadmin-lte. - Management commands:
adminlte_install,adminlte_status,adminlte_make_auth,adminlte_scaffold. - Demo project showcasing the layout, menu, components and auth flow.
Added — v2 (interactive + plugin-backed components)¶
- Bootstrap components:
adminlte_modal,adminlte_toast,adminlte_tabs,adminlte_accordion,adminlte_direct_chat,adminlte_nav_messages,adminlte_nav_notifications. - Plugin-backed Tool components emitting
data-*+ JSON-config containers:adminlte_chart(ApexCharts),adminlte_vector_map(jsVectorMap),adminlte_datatable(Tabulator),adminlte_editor(Quill),adminlte_sortable(SortableJS). - Plugin initializer (
frontend/adminlte-plugins.js.stub, installed toassets/adminlte-plugins.js) that lazily imports each library only when a matching element is present. Wired intoapp.js.stuband the demo bundle. - Demo "Components (v2)" showcase page exercising all of the above.
Added — Django-native integration¶
- Themed Django admin:
django.contrib.adminskinned with the AdminLTE shell; the sidebar is auto-built from the registered apps/models through the same menu builder + filter pipeline (honours per-user view permissions + active state). Configurable viaADMINLTE["admin_brand"]/["admin_menu"]. - Node-optional assets: a pre-built bundle ships in
static/adminlte/dist/;ADMINLTE["assets_mode"]="static"serves it with no Vite/npm (django-vite is only imported in"vite"mode). - Messages → alerts:
partials/messages.htmlrenders the messages framework as dismissible AdminLTE alerts (level → class + icon, error → danger). - Pagination: reusable
partials/pagination.htmlfrom aPaginatorpage_obj, preserving the current query string. - Built-in auth: AdminLTE-themed
registration/templates (login, logout, password change + the full password-reset flow) on the auth shell. - django-tables2 / django-filter (
[tables]extra): adjango_tables2/adminlte.htmltheme (card wrapper + footer pagination) plus a democrudapp proving list/filter/create/update/delete end to end. - crispy-forms (
[crispy]extra): one-line{% crispy form %}whole-form rendering via the crispy-bootstrap5 pack. - django-allauth (
[allauth]extra): AdminLTE-themed allauth layouts (base/entrance/manage) and elements (fields, field, form, button, alert, h1/h2, p, hr, panel). - Auto-breadcrumbs:
{% adminlte_breadcrumb %}derives crumbs fromrequest.path; the default content ofpage.html's breadcrumb block. - i18n: a package message catalog with a fully-translated Spanish (
es) locale (compiled.mo), shipped viaMANIFEST.in. - Self-hosted demo: every front-end plugin (ApexCharts, jsVectorMap, Tabulator, SortableJS, FullCalendar) loads from the Vite bundle — no CDN.
Added — production starter (demo)¶
- Twelve-factor settings via
django-environ:SECRET_KEY,DEBUG,ALLOWED_HOSTS,DATABASE_URL,EMAIL_URL,CSRF_TRUSTED_ORIGINSfrom the environment, with a git-ignored.env(see.env.example). - SQLite by default, PostgreSQL-ready via
DATABASE_URL; console email by default, SMTP viaEMAIL_URL. - WhiteNoise compressed + manifest static storage in production (plain
storage in dev); production security hardening (HSTS, SSL redirect, secure
cookies, nosniff) auto-enabled when
DEBUG=False. demo/requirements.txt(package + extras +django-environ/whitenoise/gunicorn) and a deployment section in the README.- Stripped
sourceMappingURLcomments from the shippedstatic/adminlte/distbundle socollectstaticwith manifest storage succeeds without.mapfiles.
Added — relational demo data¶
- A small relational schema in the demo
crudapp:Company,Contact(now linked to a Company),Tag,Project(FK company + lead, M2M team + tags) andTask(FK project + assignee) — exercising FK, reverse-FK and M2M. - Themed admin registrations with an inline (Tasks on Project), autocomplete fields and list filters, so the relational model is fully manageable in the AdminLTE-skinned admin.
seed_demomanagement command — deterministic, idempotent sample data (6 companies, 24 contacts, 6 tags, 10 projects, 40 tasks) plus an optional demo superuser (admin/adminpass).- Front-end Projects list (django-tables2 + filter) and a detail page rendering the related company, lead, team, tags and tasks.
Still deferred¶
- Additional locales beyond English + Spanish (the extraction structure ships;
run
makemessagesto add more). - The form Wizard as a dedicated component (the 1:1 demo page covers it).