maintenance-app is a reusable Django package that handles the slow, repetitive parts of building a
back-office: list pages, search, edit modals, soft-delete with an audit trail, XLSX import and
export, and permission checks. It packages all of that into one base view that any Django project
can subclass.
The package ties together the five Django concerns that usually drift apart over time — Models, Forms, Routes, Templates, and Static assets — so a team can ship a new admin screen by writing a small view subclass and a handful of templates, instead of rebuilding the same plumbing for every model.
Most Django projects rebuild the same back-office for every model: a list page with search and pagination, an add modal, an edit modal, a soft-delete with an audit history, an Excel export, and an Excel import. Each new version drifts a little from the last — modal sizes diverge, soft-delete is bolted on inconsistently, HTMX wiring is copy-pasted between templates, and permission checks slip out of sync with the URL routes.
maintenance-app is the result of doing that work enough times to extract a single base view.
Adding a new resource is one subclass: name the model, name the form, list the columns you want on
the list page. The URL conventions, the templates, the audit accordion, the HTMX-driven
interactions, and the permission gate are already in place.
flowchart TB
URL["Incoming request<br/>/app/model/action/pk?"] --> Gate
subgraph Pipeline["MaintenanceAPIView"]
direction TB
Gate["Permission gate<br/>user.eval_perm(action, model, object)"] --> Dispatch{"action segment"}
Dispatch --> A1["list / home<br/>search + paginate"]
Dispatch --> A2["add / edit<br/>ModelForm save"]
Dispatch --> A3["delete / reactivate<br/>flip is_active"]
Dispatch --> A4["history<br/>pghistory accordion"]
Dispatch --> A5["export<br/>tablib → XLSX"]
Dispatch --> A6["import<br/>parse XLSX → bulk save"]
end
A1 --> R1["HTML page or partial"]
A4 --> R1
A2 --> R2["204 No Content<br/>+ HX-Trigger event"]
A3 --> R2
A6 --> R2
A5 --> R3["XLSX file download"]
R1 --> B1["Browser: HTMX swap"]
R2 --> B2["Browser: HTMX swap<br/>+ SweetAlert2 toast"]
R3 --> B3["Browser: file download"]
Every request goes through one permission check before anything else runs. The action segment in the
URL chooses one of the built-in flows, and that flow ends in one of three responses: an HTML page or
partial, an empty 204 carrying a small event header that the browser uses to refresh the list and
pop a toast, or an XLSX download.
| Layer | Choice |
|---|---|
| Runtime | Python 3.13 |
| Framework | Django 5.2 |
| Database | PostgreSQL (psycopg 3) |
| Audit | django-pghistory (Postgres triggers) |
| Frontend | HTMX + Bootstrap 5 + SweetAlert2 (vendored, no build step) |
| Templating | django-template-partials |
| Data I/O | tablib (XLSX), python-magic (MIME validation) |
| Observability | sentry-sdk |
| Packaging | setuptools, installable via pip install git+… |
No Node toolchain, no SPA. All assets ship as Django static files.
- Generic
MaintenanceAPIView— one class covershome,list,add,edit,read,delete,reactivate,export,import,history,reset, and HTMX partial flows. RelatedMaintenanceAPIView— same ergonomics for nested resources (parent/child).- Permission-checked actions — every action runs
user.eval_perm(action, model_name, object)before its handler; unauthorized requests short-circuit with403. - Soft-delete + reactivate —
BaseCatalogoflipsis_activeinstead of dropping rows; reactivation is a first-class action with its own permission. - Audit trail out of the box —
@pghistory.track()captures inserts/updates/deletes at the database layer; the view renders a Bootstrap accordion diff (field name, before, after) with foreign-key, choice, datetime, and boolean resolution. - XLSX import/export — declarative
field_listper action; export streams atablib.Datasetas.xlsx; import validates the MIME type, walks rows, reports per-row errors, and tolerates blank lines. - HTMX-driven UX with no per-page JavaScript — modals, inline search with debounce, paginated
lists, and toast notifications driven by
HX-Triggerweb events (ObjectAdded,ObjectEdited,ObjectDeleted,ObjectsImported,ObjectReactivated,PasswordUpdated, plus*Relatedvariants). - Bootstrap form renderer — floating labels, file inputs, radios, and
is-invaliderror styling applied centrally viaBootstrapFormatMixin— no per-form boilerplate. - Session hygiene on login —
MaintenanceLoginViewclears other sessions on a successful login. - Ubigeo catalog — Departamento / Provincia / Distrito models and cascading-queryset mixins included as a reference implementation.
maintenance_search_demo.mp4
maintenance_edit_demo.mp4
1. Install
pip install git+https://github.com/chaufon/maintenance-app.git@v1.1.02. Register
# settings.py
INSTALLED_APPS = [
...
"maintenance",
]3. (Optional) Mount the bundled ubigeo views
# urls.py
urlpatterns = [
path("maintenance/", include("maintenance.urls")),
]4. Declare your own resource
from maintenance.views import MaintenanceAPIView
from maintenance.constants import API_ACTION_EXPORT, API_ACTION_LIST
from myapp.forms import ProductEditForm
from myapp.models import Product
class ProductAPIView(MaintenanceAPIView):
model = Product
edit_formclass = ProductEditForm
search_placeholder = "Search products"
field_list = {
API_ACTION_LIST: ["sku", "name", "price", "create_date", "is_active"],
API_ACTION_EXPORT: ["sku", "name", "price"],
}Add the standard action URLs (home, list, add, edit/<pk>, delete/<pk>, …) and the matching
templates under myapp/product/. The view resolves them by convention:
<app>/<model_name>/<action>.html.
maintenance/
├── views.py # MaintenanceAPIView, RelatedMaintenanceAPIView, login/logout
├── models.py # BaseModel, BaseCatalogo, MaintenanceMixin, ubigeo models
├── forms.py # MaintenanceBaseModelForm, SearchForm, ImportForm, mixins
├── history.py # pghistory → Bootstrap accordion renderer
├── webevents.py # HX-Trigger event catalog (success + failure variants)
├── constants.py # Action names, modal titles, MIME types
├── validators.py # is_xlsx, is_mp3, is_zip (python-magic)
├── templates/ # base layout + per-action HTMX templates
└── static/ # Bootstrap 5, HTMX, SweetAlert2, datepicker, inputmask
- Management command to scaffold actions for a new model
- Per-variable modal size customization
- SCSS pipeline for theme overrides
- Sticky table headers and autofocus on single-field forms
- Captcha hook on login
See LICENSE.
2025
