Skip to main content

django-dev-helpers

PyPI CI License: MIT Python Django

Dev-time conveniences for Django projects: autologin endpoint, dotfiles for LLM coding agents, agent help prompt, and gitignore self-check.

Features

  • Autologin endpoint — one URL logs in a user via token, no interactive login needed
  • Auth-state query toggles?__autologin__=tmp_off|logout|log_in on any URL to flip auth state in the browser without leaving the page
  • Dotfiles.dev_helpers_token, .dev_helpers_port, .dev_helpers_pg_*, .dev_helpers_redis_* written to project root for easy cat by LLM agents
  • Agent help prompt — copy-pasteable curl/psql/redis-cli commands printed at startup
  • Gitignore self-check — warns if dotfiles are not in .gitignore
  • Browser auto-open — opens autologin URL in browser after server starts
  • ALLOWED_HOSTS auto-injection — when started by run-site --bind 0.0.0.0, unions the discovered LAN hostnames/IPs into settings.ALLOWED_HOSTS so other devices on the network can reach the dev server without per-project edits
  • Live reload / tab reuse — open tabs refresh themselves on restart instead of piling up new ones
  • Production-safe — default-off, requires explicit enabled=True, raises on DEBUG=False

Installation

pip install django-dev-helpers
# or
uv add django-dev-helpers --group dev

Quick Start

  1. Add to INSTALLED_APPS:
INSTALLED_APPS = [
    # ...
    "django_dev_helpers",
]
  1. Enable in settings:
DJANGO_DEV_HELPERS = {"enabled": True}

Or via environment variable:

DJANGO_DEV_HELPERS_ENABLED=1 python manage.py runserver

That's it — autologin is wired automatically. On startup, the package will:

  • Generate an autologin token
  • Auto-install AutologinMiddleware into MIDDLEWARE so the autologin URL works without urls.py changes (refuses to load if DEBUG=False)
  • Write dotfiles to your project root
  • Check .gitignore
  • Print agent help after first request
  • Open browser at the autologin URL (falls back to / with a banner if the URL is somehow not wired)

If you'd rather wire the URL by hand (e.g. to mount it under a prefix), set {"autologin": {"middleware_autoinstall": False}} and add to urls.py:

from django_dev_helpers.urls import autologin_urlpatterns

urlpatterns = [
    *autologin_urlpatterns(),
    # ... your other URLs
]

Usage

Autologin URL (token-based)

T=$(cat .dev_helpers_token)
curl -L "http://localhost:8000/__autologin__/?token=$T"

Auth-state toggles (browser-friendly)

Once AutologinMiddleware is wired (the default), every request is scanned for a toggle query parameter. Drop it onto any URL — the middleware handles it before URL resolution.

URL on any view Effect
https://localhost:8000/some/page/?__autologin__=tmp_off Render this one request with request.user = AnonymousUser. Session unchanged — the next plain request is logged in again. Toggle param stripped from request.GET before the view sees it.
https://localhost:8000/some/page/?__autologin__=logout django.contrib.auth.logout(request) — ends the session. 302 to the same path with the toggle stripped; other query parameters preserved.
https://localhost:8000/some/page/?__autologin__=log_in (or login) Log in the configured user (autologin.user_lookup_field / user_lookup_value). 302 to the cleaned URL. No URL token required — the localhost host allowlist is the trust signal.

Unknown values pass through silently (likely typos). Off-host requests pass through identically — the toggles do not announce their existence to unauthorized hosts.

Rename the parameter via autologin.query_param, or set it to "" / None to disable the toggle layer while keeping the path-based /__autologin__/ URL working. Full details and threat model: docs/autologin.md.

Middleware ordering

AutologinMiddleware is auto-appended at the end of settings.MIDDLEWARE during AppConfig.ready(). That works because the toggles need SessionMiddleware, AuthenticationMiddleware, and MessageMiddleware to have already run by the time we look at the request — sessions for logout/log_in, request.user set up so tmp_off can override it, and request._messages for the path-based view's flash_message.

If you install the middleware manually (with autologin.middleware_autoinstall=False), place it after those three:

MIDDLEWARE = [
    "django.contrib.sessions.middleware.SessionMiddleware",
    "django.contrib.auth.middleware.AuthenticationMiddleware",
    "django.contrib.messages.middleware.MessageMiddleware",
    # ... your other middleware ...
    "django_dev_helpers.middleware.AutologinMiddleware",
]

Putting it before SessionMiddleware/AuthenticationMiddleware/MessageMiddleware will break logout, log_in, and flash_message respectively.

Auto-injected ALLOWED_HOSTS (with run-site)

When the dev server is started via run-site >= 0.13.0 with a non-loopback bind (e.g. run-site run --bind 0.0.0.0), run-site discovers the machine's mDNS hostname and primary LAN IP and exports them under DEV_HELPERS_ALLOWED_HOSTS. At AppConfig.ready(), django-dev-helpers reads that env var and unions the entries into settings.ALLOWED_HOSTS.

This means a phone on the same Wi‑Fi can hit http://your-mac.local:8000/ without you touching settings.py — even when the project hard-codes ALLOWED_HOSTS = ['localhost'].

Behaviour:

  • Idempotent — safe under runserver autoreload, no duplicate entries.
  • Gated by is_active() (i.e. DJANGO_DEV_HELPERS_ENABLED=1) — the env var is ignored unless the helper is fully activated, so a leftover var in production cannot mutate ALLOWED_HOSTS.
  • No-op when settings.ALLOWED_HOSTS already contains * (your setting takes precedence — adding more entries accomplishes nothing).
  • No-op when the env var is unset (default --bind 127.0.0.1 runs).

You can also set DEV_HELPERS_ALLOWED_HOSTS=host1,host2,... manually when running outside run-site if you want the same convenience.

Live reload / tab reuse

With live_reload.enabled (default), a tiny SSE client is injected into every text/html page. When the server comes back after a restart (or Django autoreload), the already-open tab reloads itself instead of you getting a fresh duplicate tab. On restart, dev-helpers (and run-site, if you use it) skip opening a new browser tab when a live tab is already connected. See docs/configuration.md#live_reload for the knobs (enabled, reuse_tabs, grace_seconds) and the CSP / closed-tab caveats.

Management Commands

python manage.py dev_helpers_doctor          # Full diagnostic
python manage.py dev_helpers_print_help      # Print agent prompt
python manage.py dev_helpers_check_gitignore # Check .gitignore entries (read-only)
python manage.py dev_helpers_fix_gitignore   # Add missing .gitignore entries (idempotent)

dev_helpers_fix_gitignore is the one you want when you see the "missing entries from .gitignore" warning on startup — it appends the missing dotfile names (and only those) without reordering existing rules. Pass --dry-run to preview. See docs/configuration.md#gitignore for the full contract.

Documentation

Configuration

All configuration via settings.DJANGO_DEV_HELPERS dict. See configuration docs for full reference.

DJANGO_DEV_HELPERS = {
    "enabled": True,
    "autologin": {
        "user_lookup_field": "username",
        "user_lookup_value": "admin",
        "url_path": "__autologin__/",
        "redirect_to": "/",
        # Middleware that handles the autologin URL + auth-state toggles.
        # Auto-appended to settings.MIDDLEWARE; refuses to load when DEBUG=False.
        "middleware_autoinstall": True,
        # Name of the query toggle (?__autologin__=tmp_off|logout|log_in).
        # Set to "" or None to disable the toggle layer.
        "query_param": "__autologin__",
    },
    "dotfiles": {
        "enabled": True,
    },
    "agent_help": {
        "auto_print": True,
    },
    "browser_open": {
        "enabled": True,
    },
    "live_reload": {
        "enabled": True,     # inject SSE client; open tabs reload on restart
        "reuse_tabs": True,  # don't open a new tab when one is already live
        "grace_seconds": 2.0,
    },
    "gitignore": {
        "mode": "warn",  # warn | auto-add | error | off
    },
}

Security

This package exposes an autologin backdoor for development. It is always off by default:

  • enabled must be explicitly set to True (via settings or env var)
  • Raises ImproperlyConfigured if DEBUG=False and serving HTTP
  • Autologin view verifies token via hmac.compare_digest (timing-safe)
  • Only accepts requests from localhost/127.0.0.1 by default
  • Returns 404 (not 403/401) on any failure — endpoint appears non-existent

Never install this package in production. Add it only to dev dependencies.

Requirements

  • Python >= 3.11
  • Django >= 5.2

Supported versions

Combinations exercised on every push by the CI matrix (.github/workflows/ci.yml). Each cell installs the latest patch release of that Django series (Django~=5.2.0, not Django==5.2), so CI tests what users actually run:

Python 3.11 Python 3.12 Python 3.13
Django 5.2
Django 6.0
Django 6.1

Django 6.0 and 6.1 require Python ≥ 3.12, so the (3.11, 6.0) and (3.11, 6.1) cells are excluded from CI.

Django 4.2, 5.0 and 5.1 are no longer supported — they are past upstream end of life, and the live-reload SSE stream needs ASGIRequest.listen_for_disconnect(), which Django only added in 5.0. If you are still on one of those series, pin django-dev-helpers<=0.1.14.

License

MIT

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

django_dev_helpers-0.2.0.tar.gz (119.5 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

django_dev_helpers-0.2.0-py3-none-any.whl (47.3 kB view details)

Uploaded Python 3

File details

Details for the file django_dev_helpers-0.2.0.tar.gz.

File metadata

  • Download URL: django_dev_helpers-0.2.0.tar.gz
  • Upload date:
  • Size: 119.5 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.11.14 {"installer":{"name":"uv","version":"0.11.14","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

File hashes

Hashes for django_dev_helpers-0.2.0.tar.gz
Algorithm Hash digest
SHA256 98756fdcaac9a41b4eda96507ffb1a64790b82d213a1b42a342139e845911200
MD5 6e9021f27f614fd0b1edc1f68bc41ee8
BLAKE2b-256 4be198d637a5f0de8e2b53ffd30b71894dc57f6edf4ef6f0c977ec735158d9d6

See more details on using hashes here.

File details

Details for the file django_dev_helpers-0.2.0-py3-none-any.whl.

File metadata

  • Download URL: django_dev_helpers-0.2.0-py3-none-any.whl
  • Upload date:
  • Size: 47.3 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.11.14 {"installer":{"name":"uv","version":"0.11.14","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

File hashes

Hashes for django_dev_helpers-0.2.0-py3-none-any.whl
Algorithm Hash digest
SHA256 c234d36b50ac5f3af68eacf4d04160cd1ed0fa28100d55bead015cd1b52e8f96
MD5 9e77503f57c6461f17d8eca3c88b06e5
BLAKE2b-256 0c024a34f37c815fc908275fbcd499c6f801484f73166f62aa896d58cdcecbb3

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page