django-environ

Configure Django apps with twelve-factor environment variables and URL-based settings.

Library
PyPI
v0.14.0
3,165stars
MIT License

Repository Health

Pre-computed score based on development activity, maintenance, community, maturity, and trend momentum.How we score it →
83/100Excellent
Development Activity88
Maintenance64
Community80
Maturity60
Momentum40

Technical Analysis

AI-assessed by reading the actual repository — architecture, code quality, innovation, and documentation.How we score it →
84/100Excellent
Architecture74
Code Quality90
Innovation82
Learning Curve90

django-environ brings twelve-factor configuration to Django projects by letting you define settings as environment variables instead of hardcoded Python. It reads variables from the OS environment or a .env file, casts them to the right Python type (bool, int, float, list, tuple, dict, JSON), and exposes convenience accessors like env.db(), env.cache(), env.email(), env.search(), and env.channels() that parse a single connection URL into the nested dict Django’s DATABASES, CACHES, EMAIL_* and CHANNEL_LAYERS settings expect.

Beyond simple variable reading, the library supports Docker-style _FILE secret injection through FileAwareEnv, a Path helper for building filesystem paths relative to the project root, and URL parsers for a wide range of database engines (Postgres, MySQL, SQLite, Oracle, Redshift, and more), cache backends (Redis, Memcached, filesystem), email backends, and search engines (Elasticsearch, Solr, Whoosh, Xapian).

What You Get

  • Typed environment variable accessors — env.str(), env.bool(), env.int(), env.float(), env.list(), env.tuple(), env.dict(), and env.json() cast raw environment strings to native Python types with optional defaults.
  • URL-based settings parsers — db_url(), cache_url(), email_url(), search_url(), and channels_url() turn a single connection string into the nested dict Django expects for DATABASES, CACHES, EMAIL_* and CHANNEL_LAYERS.
  • .env file loading via Env.read_env(), including comment parsing, quoted-value handling, and an optional overwrite of existing OS environment values.
  • FileAwareEnv and FileAwareMapping for Docker/Kubernetes-style secrets, reading a value from a file path referenced by a <VAR>_FILE environment variable.
  • A Path helper class for building filesystem paths relative to BASE_DIR with operator overloading (path / ‘subdir’).

Common Use Cases

  • Configuring DATABASES and CACHES from a single DATABASE_URL / REDIS_URL environment variable across Heroku, Docker, and Kubernetes deployments.
  • Keeping secrets like SECRET_KEY and API tokens out of source control by loading them from a .env file in local development and real environment variables in production.
  • Injecting secrets from Docker/Kubernetes secret files using the _FILE convention instead of plaintext environment variables.
  • Casting comma-separated or JSON-formatted environment variables into Python lists, tuples, and dicts for settings like ALLOWED_HOSTS or CORS_ORIGIN_WHITELIST.

Under The Hood

Architecture The library is a flat, single-module design centered on environ/environ.py: a large Env class holds ecosystem-wide scheme dictionaries (DB_SCHEMES, CACHE_SCHEMES, EMAIL_SCHEMES, SEARCH_SCHEMES, CHANNELS_SCHEMES) that map URL prefixes to Django backend import paths, with every typed accessor (str, bool, int, float, json, list, tuple, dict, url) funneling through a shared get_value()/parse_value() pipeline so casting logic lives in one place. URL-to-settings conversion is handled by classmethods (db_url_config, cache_url_config, email_url_config, search_url_config, channels_url_config) that parse a connection string against the scheme tables and assemble the nested dict Django expects, delegating platform-specific quirks (such as choosing the Redis cache driver by Django version) to compat.py. Docker/Kubernetes secret support is layered in separately via fileaware_mapping.py’s FileAwareMapping, a MutableMapping wrapping os.environ that FileAwareEnv swaps in, and a standalone Path class at the bottom of the same file provides operator-overloaded filesystem path construction — three loosely coupled concerns living in one large module rather than separate packages, so a change to the core casting pipeline ripples through every accessor and every *_url_config classmethod that depends on it.

Tech Stack A pure-Python package targeting Python 3.10–3.14 with a single runtime dependency, django>=2.2, and compatibility maintained through Django 6.0 via compat.py’s version-gated driver selection (built-in redis cache backend on Django 4+, django_redis if installed, else the legacy redis_cache package). Build tooling is setuptools with setuptools.build_meta and a classic setup.py, and a tox.ini matrix runs unit tests, coverage, lint, mypy type-checking, and Sphinx documentation builds across every supported Python/Django combination plus PyPy. Docs are built with Sphinx and the furo theme and published via Read the Docs.

Code Quality An extensive test suite spans test_env.py, test_db.py, test_cache.py, test_email.py, test_channels.py, test_search.py, test_schema.py, test_path.py, test_fileaware.py, and test_utils.py, using pytest with heavy parametrized tables — for example dozens of .env-parsing edge cases combining comment styles, quoting, and escape sequences — plus custom fixtures and assertion helpers rather than ad hoc checks. Error handling is explicit: missing required variables and malformed casts raise Django’s ImproperlyConfigured instead of failing silently, and a DefaultValueWarning surfaces silent-default usage when enabled. Public method signatures carry type hints checked by mypy, and dedicated GitHub Actions workflows cover linting/formatting, CodeQL static analysis, and a CI matrix across Python and Django versions plus PyPy that runs on every push, pull request, and a daily cron.

API Design The public API optimizes for one-liner settings: env.db() replaces a multi-key DATABASES dict with a single URL string, and the same pattern repeats across cache(), email(), search(), and channels(), so adopting the library changes only a handful of settings.py lines. Declaring a scheme up front (Env(VAR=(type, default))) lets a project front-load its variable contract instead of scattering os.environ.get() calls with inline defaults, and the docs site walks through nearly every accessor with copy-pasteable examples. Method naming is consistent across typed accessors, and boilerplate is minimal to get started, though the underlying module packs typed-accessor, URL-parsing, and path-utility responsibilities into one very large class — more a maintainer-facing cost than an end-user one.

Join founders buildingwith open source

Opinionated takes, migration guides, cost-saving tips, and insights from the open source ecosystem.

Subscribe on Substack
Join 750+ subscribers

Search