How do you replace the Django admin's 'Django administration' header and page titles with a charity's own branding?
answer
- attributes on the site object
- header, title, index title
- a subclass plus an app config
- base_site.html for the rest
basics
~10 sSet site_header, site_title and index_title on the Django admin site, either on admin.site directly or on an AdminSite subclass made the default through an AdminConfig subclass's default_site. Logos and colours come from overriding admin/base_site.html.
solid answer
~40 sThe texts live on `AdminSite`: `site_header` (the banner, default "Django administration"), `site_title` (the end of each `<title>`, default "Django site admin") and `index_title` (the index heading, default "Site administration"); `site_url` controls the "View site" link and `None` removes it. The quick way is to assign them on `admin.site` in an `admin.py`. The cleaner way is a subclass, `class CharityAdminSite(admin.AdminSite)` with those attributes, made the project default by an `AdminConfig` subclass whose `default_site` points at it, listed in `INSTALLED_APPS` in place of `"django.contrib.admin"`. Every existing `admin.site.register()` call and `@admin.register` then lands on the branded site. For a logo, fonts or colours, override `admin/base_site.html` and fill its `branding` or `extrastyle` block; the admin's colours are CSS variables, so a short style block retheme it.
code
python · 29 lines# charity/admin_site.py
from django.contrib import admin
class CharityAdminSite(admin.AdminSite):
site_header = "Harbour Food Bank"
site_title = "Food Bank admin"
index_title = "Operations"
site_url = None # no "View site" link
# charity/apps.py
from django.contrib.admin.apps import AdminConfig
class CharityAdminConfig(AdminConfig):
default_site = "charity.admin_site.CharityAdminSite"
# settings.py
INSTALLED_APPS = [
"charity.apps.CharityAdminConfig", # replaces "django.contrib.admin"
"django.contrib.auth",
"django.contrib.contenttypes",
"django.contrib.sessions",
"django.contrib.messages",
"django.contrib.staticfiles",
"charity",
]go deeper
Recall the three text attributes on the admin site and that they can be set on admin.site in an admin.py.
Explain the AdminSite subclass plus AdminConfig.default_site route, and why existing registrations follow it without edits.
Keep branding and site-wide rules in one subclass, override template blocks rather than copying templates, and avoid forks that break on upgrades.
Decide how far an internal admin should be branded at all, versus the cost of maintaining template overrides across Django releases.
## Where the default texts come from `django.contrib.admin` renders every page through `admin/base_site.html`, which reads a few values that `AdminSite.each_context()` puts into every template context. Those values are class attributes of **`AdminSite`**: | Attribute | Default | Where it shows | |---|---|---| | `site_header` | "Django administration" | The banner at the top of every page and the login page | | `site_title` | "Django site admin" | The end of each page's `<title>` | | `index_title` | "Site administration" | The heading of the admin index | | `site_url` | `"/"` | The "View site" link; `None` removes it | Changing these is the whole of "rebranding" for most projects. ## Option 1: set them on `admin.site` The default site is the object `django.contrib.admin.site`. Assigning attributes in any module that loads at startup — typically one app's `admin.py` — works immediately: - `admin.site.site_header = "Harbour Food Bank"` - `admin.site.site_title = "Food Bank admin"` - `admin.site.index_title = "Operations"` It is quick, but the configuration is scattered across whichever `admin.py` happens to do it. ## Option 2: an `AdminSite` subclass as the default site 1. Write `class CharityAdminSite(admin.AdminSite)` with the attributes as class attributes; any other site-wide behaviour, such as a stricter `has_permission()`, can live there too. 2. Write an app config: `class CharityAdminConfig(AdminConfig)` with `default_site = "charity.admin_site.CharityAdminSite"`. `AdminConfig` is the admin's default config and performs autodiscovery of every app's `admin.py`. 3. In `INSTALLED_APPS`, replace `"django.contrib.admin"` with `"charity.apps.CharityAdminConfig"`. `django.contrib.admin.site` is a lazy object that builds its site from the admin app config's `default_site` on first use, so every `admin.site.register(...)`, every `@admin.register(...)` and the `admin.site.urls` in your URLconf now use the branded class with no further edits. This is the documented way to "override the default admin site". ## Beyond text: logo and colours - Create `templates/admin/base_site.html` in a directory that is searched before the admin's own templates, `{% extends "admin/base.html" %}`, and override the `branding` block to add a logo image next to `{{ site_header }}`. - The admin defines its colours as **CSS variables** such as `--primary`; overriding them in the `extrastyle` block (keeping `{{ block.super }}`) retheme the whole admin, dark mode included, without touching individual rules. - `base_site.html` can only be overridden project-wide, not per app or model. ## Common mistakes 1. Setting `site_header` on a **new** `AdminSite()` instance that nothing is registered on — the header changes on an empty site while the real one stays unbranded. 2. Keeping both `"django.contrib.admin"` and the custom config in `INSTALLED_APPS`, which registers the same app label twice and fails at startup. 3. Copying all of `base.html` into the project to change one line; future Django releases then render with a stale copy. 4. Hard-coding the header text in templates instead of reading `site_header`, so the login page and the password pages disagree. ## Where the branding shows up Because the texts come from the site object through `each_context()`, one change reaches every admin page that uses it: - the **login** and **logout** pages served by the site; - the **password change** pages linked from the header; - every change list, change form, delete confirmation and history page; - custom admin views that build their context from `admin_site.each_context(request)`. Pages outside the admin do not get it automatically. The documented recipe for adding a password-reset flow to the admin, for example, passes `extra_context={"site_header": admin.site.site_header}` to the auth views so they show the same banner. ## A worked setup for the charity 1. Create `CharityAdminSite` with the header "Harbour Food Bank", the title "Food Bank admin", the index title "Operations" and `site_url = None`, because staff never need to jump to the public site from the admin. 2. Point `CharityAdminConfig.default_site` at it and swap it into `INSTALLED_APPS`. 3. Add `templates/admin/base_site.html` that extends `admin/base.html`, puts the charity's logo in the `branding` block and sets the brand colour on `--primary` in `extrastyle`. 4. Load the admin login page and one change form to confirm the header, the tab title and the colours all changed.
- Why does a Django project's @admin.register(...) end up on the custom site after setting default_site, without changing any imports?`django.contrib.admin.site` is a lazy object, `DefaultAdminSite`, that instantiates the class named by the admin app config's `default_site` when first used. `@admin.register` without a `site` argument and `admin.site.register()` both go through that object, so they register on the subclass automatically.
- How do you change the Django admin's colours without rewriting its stylesheets?Override `admin/base.html` or `admin/base_site.html`, extend the original, and in the `extrastyle` block, after `{{ block.super }}`, redefine the admin's CSS variables such as `--primary` for the light and dark themes. Every rule that uses the variables picks up the new values.
saying these in an interview costs you the question
- The admin header can only be changed by editing Django's templates
- A new AdminSite() instance automatically replaces admin.site
- site_title sets the banner text at the top of every page
- default_site is a Django setting in settings.py
- Keep django.contrib.admin in INSTALLED_APPS alongside the custom AdminConfig