Skip to content

chaufon/maintenance-app

Repository files navigation

Maintenance App

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.


Why it exists

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.


Architecture at a glance

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"]
Loading

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.


Tech stack

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.


Features

  • Generic MaintenanceAPIView — one class covers home, 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 with 403.
  • Soft-delete + reactivateBaseCatalogo flips is_active instead 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_list per action; export streams a tablib.Dataset as .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-Trigger web events (ObjectAdded, ObjectEdited, ObjectDeleted, ObjectsImported, ObjectReactivated, PasswordUpdated, plus *Related variants).
  • Bootstrap form renderer — floating labels, file inputs, radios, and is-invalid error styling applied centrally via BootstrapFormatMixin — no per-form boilerplate.
  • Session hygiene on loginMaintenanceLoginView clears other sessions on a successful login.
  • Ubigeo catalog — Departamento / Provincia / Distrito models and cascading-queryset mixins included as a reference implementation.

Demos

List + search

maintenance_search_demo.mp4

Edit modal (HTMX)

maintenance_edit_demo.mp4

Audit history

History accordion


Quick start

1. Install

pip install git+https://github.com/chaufon/maintenance-app.git@v1.1.0

2. 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.


Project layout

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

Roadmap

  • 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

Extended documentation

Index


License

See LICENSE.

2025

About

This repository serves as a template for future Django projects, providing a well-structured foundation to build upon. It includes essential features like user authentication, role management, and dynamic views using HTMX.

Topics

Resources

License

Stars

Watchers

Forks

Packages

 
 
 

Contributors