django-admin-runner
Run Django management commands from the admin — with auto-generated forms, pluggable task runners, and a unified execution log.
Documentation | PyPI | Changelog
Features
@register_commanddecorator — register any management command with metadata- Auto-generated forms — argparse arguments become Django form fields automatically
- Widget & form customisation — override widgets per-argument (
widget=onadd_argument), supply a customFormclass, or use per-parameter field overrides in the decorator - Built-in file fields —
FileOrPathField(upload or server path),FileField,ImageField - Pluggable runners — Django Tasks (default), Celery, sync, or custom
- Execution log — every run is stored as a
CommandExecutionrecord - Stop running commands — a "Stop" button (graceful, via the output
heartbeat) that becomes "Force Stop" (hard kill) while the command keeps
running; Celery uses
revoke(terminate=True), django-q2 signals the task's worker PID. Shown only while the execution is running. - Terminal output — stdout/stderr render in an embedded xterm.js terminal
(vendored, no CDN): progress bars display as in a real terminal. Output is
stored as append-only 512 KB parts, so live updates cost only the new
characters and arrive every 250 ms regardless of output size; a cursor-based
delta endpoint transfers just the delta, sealed parts are served
browser-cacheable (immutable), and retention is capped per field at 500 MB
(
ADMIN_RUNNER_MAX_OUTPUT) by pruning the oldest parts. Terminal size is configurable (ADMIN_RUNNER_TERM_COLS/ADMIN_RUNNER_TERM_ROWS, default 120×40) and exported asCOLUMNS/LINESso output layout is deterministic. - Permission control — per-command permission requirements (superuser, Django perms, or a list)
- Model attachment — show a "Run" button on any model's admin change-list via
models=[...] - Scheduling — interactive "Add schedule" admin UI (cron / interval / one-off)
and declarative
@register_command(schedule=…)schedules, materialized into the backend's native periodic tasks (django-q2; see the scheduling guide) - Unfold support — auto-detected, uses Unfold templates and widgets when available
Installation
pip install django-admin-runner
Add to INSTALLED_APPS:
INSTALLED_APPS = [
...
"django_admin_runner",
]
Run migrations:
python manage.py migrate
Quick start
# myapp/management/commands/my_command.py
from django.core.management.base import BaseCommand
from django_admin_runner import register_command
@register_command(group="Maintenance", permission="myapp.change_mymodel")
class Command(BaseCommand):
help = "Does something useful"
def add_arguments(self, parser):
parser.add_argument("--count", type=int, default=10)
parser.add_argument("--dry-run", action="store_true")
def handle(self, *args, **options):
self.stdout.write(f"Running {options['count']} times")
Visit /admin/django_admin_runner/commandexecution/commands/ to run your commands.
Custom widgets
Override how individual parameters render — right inside add_arguments:
from django_admin_runner import FileOrPathField, register_command
@register_command(group="Import")
class Command(BaseCommand):
def add_arguments(self, parser):
# File upload OR server-side path text field
parser.add_argument("--source", widget=FileOrPathField(), default="data.csv")
# Swap to a textarea
parser.add_argument("--notes", widget=forms.Textarea(attrs={"rows": 3}))
# Image upload (requires Pillow)
parser.add_argument("--photo", widget=forms.ImageField(required=False))
Or provide a fully custom form class:
@register_command(form_class=MyImportForm)
class Command(BaseCommand):
...
See the Widget & form customisation docs for the full reference.
Scheduling
Registered commands can be scheduled — periodically (cron expression or fixed interval) or once at a specific time. Schedules are backend-abstracted:
| Runner | Schedule kinds |
|---|---|
django-q2 |
cron, interval, one-off |
celery, django (tasks), sync, rq |
not supported (action hidden) |
Interactive (admin UI)
When the active runner supports scheduling, an Add schedule action appears next to Run on the command pages. The creation form combines the command's argparse-generated parameter form with a schedule section (kind picker with dynamic fields, label, enabled toggle). Existing schedules are edited in a change view with Parameters and Schedule tabs; saving re-validates the kwargs against the command's current definition (stale options surface an error, never run with wrong args) and re-materializes the native backend schedule. Disabling keeps the row but stops runs; deleting removes both. A global Schedules overview lists every schedule with its next run.
Declarative (in code)
from django_admin_runner import CronSchedule, IntervalSchedule, register_command
@register_command(
group="Maintenance",
schedule=[
CronSchedule("0 3 * * *", name="nightly-full"),
IntervalSchedule(15, name="quick-incremental", kwargs={"limit": 10}),
],
)
class Command(BaseCommand):
...
The sync materializes declarations as source=code schedule rows
keyed by (command_name, name) (a single declaration defaults its name to
the command name; lists require explicit unique names so reordering never
rewires schedules). The registry wins for the schedule spec, the database
wins for enabled — admins can pause a declarative schedule without a
deploy. Admin-created schedules are never touched by the sync.
When the sync runs
RegisteredCommand and source=code schedule rows materialize when the
registry is synced — automatically after migrations (post_migrate) and on
demand via python manage.py admin_runner_sync (manual runs, worker boot,
or re-sync after code changes). AppConfig.ready() never touches the
database, so django.setup() works without one (image builds running
collectstatic) and is safe with gunicorn --preload and pooled
connections.
Consumers that cannot run migrations during deploy can restore the legacy
startup sync with ADMIN_RUNNER_SYNC_ON_READY = True — not recommended
with preloaded workers or pooled connections.
Development
# Install dependencies and pre-commit hooks
just install
# Run tests
just tests
# Run linters
just check
# Serve docs locally
just docs
Contributing
Contributions are welcome! Please open an issue or pull request on GitHub.
License
MIT
Metadata
Release files for django-admin-runner 0.3.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| django_admin_runner-0.3.0.tar.gz | 253.0 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| django_admin_runner-0.3.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 537.4 kB
Release files / django_admin_runner-0.3.0.tar.gz
| Download URL | django_admin_runner-0.3.0.tar.gz |
|---|---|
| Size | 253.0 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
fa413e6a3d8e3dc40f11e588be7ed76bedc7df5f90c34075aecaade656e02b90
|
|
BLAKE2b-256 checksum How to use checksums |
45c025acc2dc863394a7a5fefbd3378a37b6f824153aebfddda9eb7c3527c670
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 5, 2026.
Transparency logRelease files / django_admin_runner-0.3.0-py3-none-any.whl
| Download URL | django_admin_runner-0.3.0-py3-none-any.whl |
|---|---|
| Size | 284.4 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
f55f272070148b8b8448a614b8a7bcca4c41bb03b28f630fa79e2f7a2bcb0d6e
|
|
BLAKE2b-256 checksum How to use checksums |
4e5e9f546e213528b5ccf6551510ba930315e51af831323ec941174602e8a9ff
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 5, 2026.
Transparency log