User-controllable, in-database list_display for Django admin: enable, disable and reorder columns at runtime.
Project description
django-dynamic-admin-columns
User-controllable, in-database list_display for Django admin. End-users
enable, disable and reorder columns at runtime through the admin itself — no
code changes, no redeploy.
Why
django.contrib.admin.ModelAdmin.list_display is a developer-time setting.
If end-users want different columns visible, the developer has to ship a code
change. In bibliographic, archival and CRUD-heavy admin sites the column set
varies between users and projects; this library moves that choice into the
database so the admin UI itself is the configuration surface.
Extracted from the BPP academic bibliography system where it has run in production since 2022.
Features
- Drop-in
DynamicColumnsMixinfor anyModelAdmin. - In-changelist picker. A Columns button in the standard
object-toolsarea opens a modal where end-users toggle columns and reorder them via drag-and-drop — no admin training, no separate preferences page. - Per-user layouts. Each staff user keeps their own column configuration; users without a personal layout fall back to the global defaults. Resetting is one click away.
- Three column tiers: always (pinned, code-only), default (visible out of the box, user can toggle), allowed (hidden by default, user-discoverable).
- Per-admin and project-wide regex denylists (
list_display_forbidden,DYNAMIC_ADMIN_COLUMNS_FORBIDDEN_COLUMN_NAMES) to keep sensitive or noisy fields out of the picker. "__all__"shorthand: expose every model field and let the user pick.- Dictionary form of
list_select_relatedthat activates joins only for columns that are actually visible — no overhead for columns the user has hidden. - Vanilla-JS picker UI — no SortableJS, no jQuery, no Bootstrap.
- Settings-gated import allowlist
(
DYNAMIC_ADMIN_COLUMNS_ALLOWED_IMPORT_PATHS) to prevent arbitrary class loading from untrusted database content. - Polish translation included.
Installation
uv add django-dynamic-admin-columns
# or
pip install django-dynamic-admin-columns
Add the apps and the import allowlist to your settings:
INSTALLED_APPS = [
# ...
"adminsortable2",
"dynamic_admin_columns",
]
DYNAMIC_ADMIN_COLUMNS_ALLOWED_IMPORT_PATHS = [
"myapp.admin",
]
# Optional global regex denylist applied to every dynamic admin.
DYNAMIC_ADMIN_COLUMNS_FORBIDDEN_COLUMN_NAMES = [
r".*_cache$",
r"^cached_.*",
]
# django-admin-sortable2's reordering view triggers admin.E117 with a dict
# ``list_select_related`` — silence it.
SILENCED_SYSTEM_CHECKS = ["admin.E117"]
Then run migrations:
python manage.py migrate dynamic_admin_columns
Usage
# myapp/admin.py
from django.contrib import admin
from dynamic_admin_columns.mixins import DynamicColumnsMixin
from myapp.models import Book
@admin.register(Book)
class BookAdmin(DynamicColumnsMixin, admin.ModelAdmin):
# Pinned columns: always visible, always first, cannot be toggled.
list_display_always = ["title"]
# Visible out of the box, user can hide or reorder.
list_display_default = ["author", "isbn"]
# Hidden by default, user can enable via the admin.
list_display_allowed = ["pages", "notes"]
# Per-admin denylist (regex). Wins over ``__all__``.
list_display_forbidden = [r"^legacy_.*"]
First time a user opens the changelist, the matching ModelAdmin row
and its ModelAdminColumn rows are created automatically as the
global defaults (user IS NULL). Subsequent edits through the
in-changelist Columns picker write to that user's personal copy
(user=<request.user>); users without a personal copy fall back to
the global row.
Superuser: editing global defaults
A superuser sees an extra radio switch at the top of the modal:
- My personal layout — the default. Saves create or update a personal row that affects only the current user.
- Global defaults — saves rewrite the
user IS NULLrow, so every user without a personal layout sees the new column set on their next changelist load.
The accompanying Discard personal layout / Reset global defaults from code button operates on whichever scope is currently selected.
Dynamic select_related
Pay the JOIN cost only for columns that are actually visible:
class BookAdmin(DynamicColumnsMixin, admin.ModelAdmin):
list_display_default = ["author"]
list_display_allowed = ["publisher"]
list_select_related = {
"__always__": ["category"], # always joined
"author": ["author"], # joined only if the column is visible
"publisher": ["publisher"],
}
Example project
A minimal Django project demonstrating the library lives in
example/. Two ways to run it:
# Plain Django — SQLite, no extras:
uv pip install -e ".[dev]"
cd example
python manage.py migrate
python manage.py loaddata sample
python manage.py runserver
or, via run-site +
django-dev-helpers
(Postgres + Redis testcontainers, autologin, recommended for
exploration and for LLM coding agents):
uv pip install -e .
uv pip install -r example/requirements-dev.txt
cd example
uv run --no-sync python manage.py run_site
run_site spins up the containers, migrates, creates an
admin / admin superuser and opens your browser. The
example/runsite.toml file configures the
container names, the superuser credentials, and the welcome banner.
See example/README.md for the full walk-through
and the list of pre-loaded users.
Supported versions
This package targets actively supported Django releases. Older
Django versions (4.2 LTS, 5.0, 5.1) are end-of-life upstream and are
not covered by CI; pin django-dynamic-admin-columns < 0.2 if you
need to stay on them.
| Django | 3.11 | 3.12 | 3.13 | 3.14 | Status |
|---|---|---|---|---|---|
| 5.2 LTS | ✓ | ✓ | ✓ | — | Active LTS, extended support Apr 2028 |
| 6.0 | — | ✓ | ✓ | ✓ | Current mainstream |
CI exercises every ✓ cell on GitHub Actions.
License
MIT. See LICENSE.
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
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_dynamic_admin_columns-0.4.3.tar.gz.
File metadata
- Download URL: django_dynamic_admin_columns-0.4.3.tar.gz
- Upload date:
- Size: 49.5 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.9.2
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
91d6c619a9dd55ce829cd82825e41e2db01a472ae5a937d08929a9646ad7c7c2
|
|
| MD5 |
dbf860afe21a5e014be5d710f137c7d5
|
|
| BLAKE2b-256 |
091bc8211b7ec32ee8e0d22826953c35b1fd5b398bb87857331d2637ef5fd2cc
|
File details
Details for the file django_dynamic_admin_columns-0.4.3-py3-none-any.whl.
File metadata
- Download URL: django_dynamic_admin_columns-0.4.3-py3-none-any.whl
- Upload date:
- Size: 34.3 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.9.2
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
a3d01562536b0edb4cb2a89f6e09def4c39591eccb964274118d3e98f793f618
|
|
| MD5 |
cb125db4170065489d58fd07723bd607
|
|
| BLAKE2b-256 |
55b3d93e2cd2fc35481305e4821184ad9f1eb003a667e91c5c9e5bdd33cc0a28
|