Lucus enhances Django Admin with custom templates, static theming, and a multi-column dashboard.
Project description
Lucus
Lucus is a Django package that restyles django.contrib.admin with its own base.html, layout CSS, and a single bundled admin stylesheet (lucus/css/lucus-admin.css) instead of linking the stock admin CSS. Staff can pick a color palette and appearance (light, dark, or system) from the header; choices are stored per user in LucusAdminUiPreference. The index page supports a configurable multi-column dashboard.
The default admin navigation sidebar is disabled. On changelist and change-form views, the primary action controls are fixed to the bottom of the viewport so they stay visible while scrolling.
Index page: multi-column dashboard cards (sample apps: Education, Logistics, Support, …), header toolbars for palette, appearance, site, language, and user.
Model changelist: breadcrumbs, search, bulk actions, result table, right-hand filters.
Change form: fieldset, fixed bottom bar (save variants and delete). Capture uses dark mode; palette and appearance are per-user settings.
Requirements
- Python 3.10+
- Django
>=5.2,<7(seepyproject.toml)
Install
pip install django-lucus
The Python package name on PyPI is django-lucus; import the app as lucus.
Setup
INSTALLED_APPS = [
# ...
"lucus",
"django.contrib.admin",
]
Put lucus before rosetta (and any other app that ships conflicting templates/rosetta/*.html) so Lucus’s Rosetta wrappers are found first. Same idea if a package exposes duplicate template names.
python manage.py migrate lucus_admin
Run collectstatic in production so admin assets are served like any other static files.
Settings
| Setting | Type | Default | Effect |
|---|---|---|---|
SITE_NAME |
str |
"Site" |
LUCUS_ADMIN_SITE_HEADER_TEMPLATE / site_title |
LUCUS_ADMIN_SITE_HEADER_TEMPLATE |
str |
"Administration — {site}" |
admin.site.site_header = .format(site=SITE_NAME); use only {site} or escape other {} |
LUCUS_ADMIN_SITE_TITLE_USE_SITE_NAME |
bool |
True |
admin.site.site_title = SITE_NAME if true |
LUCUS_ADMIN_BACKGROUND_IMAGE |
str / Path |
"" |
Full-page background: http(s)://, //, site path /…, or static path (no ..) |
LUCUS_ADMIN_BACKGROUND_SCRIM_OPACITY |
float |
0.88 |
Theme overlay on image, 0.0…1.0 |
LUCUS_ADMIN_LANGUAGE_SELECTOR |
bool |
(auto) | False → hide header language <select>. Shown only if USE_I18N, len(LANGUAGES) > 1, LocaleMiddleware, set_language URL exists |
LUCUS_ADMIN_SITE_SELECTOR |
bool |
(auto) | False → hide django.contrib.sites header switcher. Shown if sites app + ≥2 Site rows |
LUCUS_ADMIN_SITES |
AdminSite / list / tuple |
None |
AdminSite instance(s) Lucus patches (each_context, get_urls, headers, …). Default: only django.contrib.admin.site. Extra sites: LUCUS_ADMIN_SITES = (admin.site, my_site). Forms in base.html use {% url 'admin:lucus_save_ui' %} (default admin namespace); another namespace may need a template override. |
LUCUS_STAFF_THEME_PATH_PREFIXES |
tuple / list / str |
see below | Staff-only: merge AdminSite.each_context on these prefixes (pages that extend admin templates but are not admin_view). Default: /logs/ (django-log-viewer), /rosetta/, /explorer/. Add e.g. /admin/logs/ if mounted there. Set () to disable. |
LUCUS_UI |
dict |
{} |
See below |
LUCUS_EXTRA_STATIC_CSS |
str / list / tuple |
() |
Extra CSS paths after palette; no .., no leading / |
LUCUS_EMPTY_VALUE_DISPLAY_WRAP |
bool |
True |
Wrap empty changelist cells in lucus-admin-empty |
LUCUS_EMPTY_VALUE_PLACEHOLDER |
str |
"—" |
empty_value_display text |
LUCUS_ACTIONS_ON_BOTTOM |
bool |
True |
Default for ModelAdmin.actions_on_bottom on the class (Django’s normal inheritance: set actions_on_bottom = False on your ModelAdmin subclass to override) |
LUCUS_ACTIONS_ON_TOP |
bool |
True |
Same for actions_on_top |
LUCUS_DASHBOARD |
list / None |
None |
Dashboard; None → built-in grouped layout |
LUCUS_DASHBOARD_APPEND_UNCOVERED |
bool |
True |
Grouped mode: append uncovered apps to last column |
Palette / appearance: not in settings. Stored per user in LucusAdminUiPreference. Slugs: olivia, grey, slate, dune, midnight, nord, dracula, github, catppuccin, tokyo → lucus/static/lucus/css/schemes/<slug>.css. Context: lucus.theme.lucus_admin_extra_context → admin.site.each_context.
LUCUS_UI
| Key | Default | Effect |
|---|---|---|
help_as_icon |
True |
Field help_text behind a ? / <details> |
high_contrast_toggle |
False |
User menu “High contrast” (localStorage + data-lucus-contrast on <html>) |
LUCUS_UI = {
"help_as_icon": True,
"high_contrast_toggle": False,
}
LUCUS_DASHBOARD
Context: lucus_dashboard_columns from get_dashboard_for_request. Section link: {"label", "url"} or {"label", "admin_urlname"} (e.g. admin:app_model_changelist). Section omitted if no link resolves.
Layout mode: [{ "column"?: 1–4, "classes"?: str, "sections": [{ "title", "links": [...] }] }, …]. Same column → merge sections; classes from first dict.
Groups mode: [{ "column", "title", "links"?: [...], "app_labels"?: set|list|tuple|frozenset }, …]. links first, then models from app_labels; duplicate URLs dropped. list/tuple app_labels → order kept; set → get_app_list order.
LUCUS_DASHBOARD_APPEND_UNCOVERED: True → apps not referenced by groups / inferred from admin_urlname go to last column; False → only configured groups.
LUCUS_DASHBOARD = [
{
"column": 1,
"classes": "lucus-dashboard__col lucus-col lucus-col-3 lucus-col-md-6 lucus-col-sm-12",
"sections": [
{
"title": "Education",
"links": [
{"label": "Instructors", "admin_urlname": "admin:academy_instructor_changelist"},
{"label": "Courses", "url": "/admin/academy/course/"},
],
},
],
},
]
LUCUS_DASHBOARD = [
{"column": 1, "title": "Authorization", "app_labels": {"auth"}},
]
LUCUS_DASHBOARD = [
{
"column": 2,
"title": "Auth",
"links": [{"label": "Users", "admin_urlname": "admin:auth_user_changelist"}],
"app_labels": ("auth",),
},
]
Stylesheets
Stylesheets load in this order: lucus/css/style.css, lucus/css/lucus-admin.css, the selected palette lucus/css/schemes/<slug>.css, then paths from LUCUS_EXTRA_STATIC_CSS. JavaScript for admin widgets, inlines, and actions still comes from django.contrib.admin.
To add a custom palette, ship lucus/css/<slug>.css on your static path and append the slug and label to lucus.theme.BUNDLED_COLOR_SCHEMES in your project.
Lucus overrides admin/change_list_results.html: column label (.text) is rendered before .sortoptions, and both sit inside .lucus-th-heading-inner so sorted headers stay centered instead of using the stock float: right icon layout.
Tests
pip install -r requirements-dev.txt
pytest
Modules
| Module | Responsibility |
|---|---|
lucus.apps.LucusConfig |
ready(): site_header / site_title, each_context hook, dashboard context, routes lucus_save_ui and lucus_save_site, enable_nav_sidebar = False, empty_value_display, ModelAdmin actions placement |
lucus.dashboard |
Normalizes LUCUS_DASHBOARD into column structures |
lucus.models |
LucusAdminUiPreference (palette + appearance per user) |
lucus.theme |
Bundled schemes, lucus_admin_extra_context(request) |
lucus.views |
POST handlers for saving UI and site selection |
Package version: lucus.__version__.
If you use the optional sites switcher in the header and need request.site inside /admin/ to match the selection, register lucus.sites_panel.LucusAdminSiteMiddleware immediately after SessionMiddleware (see lucus.sites_panel).
Supported Django versions for the PyPI package are declared in pyproject.toml (currently 5.2 and compatible 6.x). The demo project at the repo root lists its dependencies in requirements.txt.
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_lucus-0.1.6.tar.gz.
File metadata
- Download URL: django_lucus-0.1.6.tar.gz
- Upload date:
- Size: 79.8 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.3
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
2cd06d54a8de7cb2ced14a9d9c9a4a663f6334f9b8eea41b3676377a9ac7d137
|
|
| MD5 |
47ba4fad9552b68be854514373afa3b8
|
|
| BLAKE2b-256 |
f187ffd7c7f70d87398c6cdbaea3b88ad5b8c696c793bba8042b2789adf6aa0b
|
File details
Details for the file django_lucus-0.1.6-py3-none-any.whl.
File metadata
- Download URL: django_lucus-0.1.6-py3-none-any.whl
- Upload date:
- Size: 98.5 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.3
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
a98f05b7b2cc6094b73ead7321a783817697ed1a72317cc56296e8378a26cd41
|
|
| MD5 |
16c7d7c00bd74a4094a1cb77adb9bc1c
|
|
| BLAKE2b-256 |
db1ed3cdd0d311cb537597a19e62b48361b12ca3583f0df88ab50e9d13179eaa
|