django-postgres-extra
Brings PostgreSQL's advanced features (atomic upserts, table partitioning, materialized views, HStore) to the Django ORM with fully automatic migrations.
Repository Health
Technical Analysis
django-postgres-extra extends the Django ORM with PostgreSQL-specific capabilities that Django’s database-agnostic core deliberately leaves out. Instead of bolting on a handful of raw-SQL helpers, it replaces the database backend and schema editor so that atomic upserts, declarative table partitioning, materialized/regular views, schema management, explicit locking, and HStore fields all behave like native Django features — including full support in makemigrations and migrate, with no hand-written migration operations required.
The project has been maintained by Sector Labs since 2017, tracks current Django and Python releases closely (Django 2.0-6.0, Python 3.7-3.13), and supports both psycopg2 and psycopg3. It ships as a small, focused dependency surface (just Django and python-dateutil) precisely because its value comes from deep integration with Django’s own internals rather than wrapping a separate client library.
What You Get
- A custom database backend and schema editor (
psqlextra.backend) that emits PostgreSQL-native DDL for partitioning, views, and constraints PostgresModel/PostgresManager/PostgresQuerySetdrop-in replacements that add.on_conflict()atomic upsert support to the standard Django ORM- Declarative table partitioning (range and time-based) fully integrated into
makemigrations, including a management command to auto-create future partitions - Support for regular and materialized views defined as ordinary Django models, kept in sync through migrations
- A patched migration autodetector so partitioning, view, and HStore-constraint changes are captured automatically instead of requiring manual migration authoring
- HStore field support with unique and required-key constraints enforced at the database level
Common Use Cases
- Replacing manual
INSERT ... ON CONFLICTraw SQL with a typed, queryset-level atomic upsert API - Partitioning large, time-series or high-write tables (events, logs, metrics) by range or time without leaving Django’s migration system
- Backing reporting or read-heavy views with PostgreSQL materialized views that are still modeled and migrated like regular Django models
- Enforcing uniqueness or required-ness on individual HStore keys at the database layer instead of in application code
- Taking explicit table-level locks or dropping/creating PostgreSQL schemas from within Django code and migrations
Under The Hood
Architecture
The package layers around a custom Django database backend (psqlextra/backend/base.py, base_impl.py) that subclasses Django’s PostgreSQL backend and wires in a custom schema editor (backend/schema.py) hooking DDL emission for partitioning, views, locking, and truncation, plus side-effect modules (backend/side_effects/hstore_required.py, hstore_unique.py) that inject HStore constraint validation into the migration-execution pipeline. Migration awareness comes from patches to Django’s own machinery (backend/migrations/patched_autodetector.py, patched_migrations.py, patched_project_state.py), extending the autodetector so partitioning, view, and HStore-specific state changes are captured automatically. On the query side, PostgresManager (manager/manager.py) requires the psqlextra backend to be configured and returns a PostgresQuerySet (query.py) that overrides insert/annotate/upsert behavior, building PostgresQuery/PostgresInsertQuery objects (sql.py) that compile into ON CONFLICT-aware SQL. Partitioning is its own subsystem (partitioning/) with a manager, pluggable strategies, and a reconciliation “plan” that diffs desired schema against introspected state (introspect/). The schema editor is the single load-bearing abstraction — every side effect and migration operation hooks through it.
Tech Stack Pure Python targeting Django 2.0-6.0 across Python 3.7-3.13, with a deliberately minimal dependency surface (Django and python-dateutil only) since the value comes from extending Django’s own backend rather than wrapping a separate driver — both psycopg2 and psycopg3 are supported through Django’s standard backend configuration. Development tooling is uv-managed, with black for formatting, flake8 for linting, mypy plus django-stubs for type-checking, isort for import order, and poethepoet as the task runner tying these together; documentation is built with Sphinx and published to Read the Docs. CI runs the test suite against a live PostgreSQL service across the full Python/Django/psycopg matrix via tox, with a separate tag-triggered workflow building and publishing releases to PyPI.
Code Quality
Tests are extensive — dozens of test modules covering conflict handling, partitioning, views, HStore constraints, migrations, and locking, run with pytest and pytest-django, snapshot-tested via syrupy, and benchmarked with pytest-benchmark, with coverage reported to Coveralls in CI. Error handling is explicit and typed rather than swallowed: ImproperlyConfigured and SuspiciousOperation are raised deliberately at integration boundaries, such as PostgresManager refusing to initialize if the psqlextra backend isn’t configured. Naming mirrors Django’s own conventions (PostgresQuerySet, PostgresManager, PostgresSchemaEditor), the codebase ships a py.typed marker and is checked with mypy plus django-stubs, and formatting/linting/import order are all enforced through a single poe verify task.
API Design
The public API deliberately mirrors Django’s own conventions so adopting it requires no new mental model: subclass PostgresModel, swap in PostgresManager, and atomic upserts, partitioning, and views become ordinary queryset methods and model base classes rather than a separate DSL. Migrations stay fully automatic — the patched autodetector understands partitioning config, view models, and HStore constraints, so makemigrations produces correct operations without hand-authored migration code, a level of integration most single-feature Postgres-for-Django add-ons don’t attempt. Documentation is thorough, with a dedicated Sphinx site covering each feature area plus a precise version-compatibility matrix in the README, lowering the risk of adopting a database-specific extension.