django-postgres-objects
PostgreSQL objects (i.e. non-tables) have no representation in Django’s migration state, so the usual way to manage these in Django migrations is through hand-written RunSQL. That works, but nothing notices when the declaration and the database drift apart, and makemigrations --check will never tell you.
This package lets you declare such an object as a class, the same way a model is a class, have makemigrations write the operations for you, and have migrate perform the operations for you.
Full documentation is at https://django-postgres-objects.readthedocs.io/.
# example/db_functions.py
from postgres_objects import Function
class AllUppercase(Function):
arguments = 'input TEXT'
returns = 'TEXT'
volatility = 'IMMUTABLE'
strict = True
parallel = 'SAFE'
body = """
BEGIN
RETURN UPPER(input);
END;
"""
Views are declared the same way, in a module of their own:
# example/db_views.py
from postgres_objects import View
class UppercasedCakes(View):
sql = 'SELECT id, name_uppercased FROM example_cake'
A view masking an existing model (e.g. a subset of its columns, a row condition or an aggregate) can declare the queryset instead of the SQL, and then its columns are declared exactly once: the compiled SELECT goes into the migration, and the declaration exposes .objects, a generated unmanaged model for reading the view back:
from django.db.models import Count
from example.models import Cake
from postgres_objects import MaterializedView
class CakeCounts(MaterializedView):
unique_index = ('name',)
def queryset():
return Cake.objects.values('name').annotate(cakes=Count('id'))
CakeCounts.objects.filter(cakes__gt=3)
A materialized view stores its rows, and no declaration can say when they have gone stale, so repopulating one is a call you make from wherever you know the data has moved:
CakeCounts.refresh()
Point a setting at the module each kind lives in, relative to each app:
POSTGRES_OBJECTS = {
'FUNCTIONS_MODULE_PATH': 'db_functions',
'VIEWS_MODULE_PATH': 'db_views',
}
manage.py makemigrations now writes the operations for whatever you added, changed or removed. Function migrations are written before that app’s model migrations and removals after them, so a model migration adding a generated column can rely on the function its expression calls, and a function is only dropped once nothing refers to it any more. Views are placed the other way round, since a view reads from tables rather than being read by them.
The declaration is callable, so the same class serves the migration that creates the function and the queries that call it:
from django.db.models import F
from example.db_functions import AllUppercase
Cake.objects.annotate(uppercased=AllUppercase(F('name')))
# or as a generated column
from postgres_objects import GeneratedField
class Cake(models.Model):
name = models.CharField(max_length=128)
name_uppercased = GeneratedField(
expression=AllUppercase(F('name')),
output_field=models.TextField(),
db_persist=True,
)
postgres_objects.GeneratedField is Django’s GeneratedField plus one promise: when a declared function the column depends on changes what it computes, the migration that alters the function is followed by one that recalculates the stored values, which PostgreSQL does not do on its own. Django’s plain GeneratedField works too, but its stored values are then left as the old body computed them.
Installation
pip install django-postgres-objects
Add it to INSTALLED_APPS:
INSTALLED_APPS = (
...
'postgres_objects',
...
)
Then name the module each kind of declaration lives in, relative to each app:
POSTGRES_OBJECTS = {
'FUNCTIONS_MODULE_PATH': 'db_functions',
'VIEWS_MODULE_PATH': 'db_views',
}
Leave one path undefined to use only the other kind. A path may be dotted, so 'db.functions' works too. See the settings reference for the full set of options, and other migration libraries for how to coexist with a library that ships its own makemigrations.
Relationship to DjanQuiltDB
While this library was written in conjunction with DjanQuiltDB and the libraries were designed to work together seamlessly in projects where both are installed, both libraries are written to be used as standalone libraries as well. For using django-postgres-objects, it is not necessary to use or be familiar with DjanQuiltDB.
If you wish to use DjanQuiltDB features for objects managed through django-postgres-objects, you can install the djanquiltdb[postgres-objects] extra. For more information, please refer to the DjanQuiltDB documentation.
Scope limitations
A major type of PostgreSQL object that this library is intentionally not covering is triggers, since there is already a very mature library available for this in django-pgtrigger.
Requirements
Python 3.14
Django 6.0 or 6.1
PostgreSQL 17 or 18
Metadata
Release files for django-postgres-objects 1.0.1
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_postgres_objects-1.0.1.tar.gz | 192.2 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| django_postgres_objects-1.0.1-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 240.9 kB
Release files / django_postgres_objects-1.0.1.tar.gz
| Download URL | django_postgres_objects-1.0.1.tar.gz |
|---|---|
| Size | 192.2 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
c46058e5cd0e9bade3c1de09f6a15dd05dcf5174d9866bd9846f305be8725fb9
|
|
BLAKE2b-256 checksum How to use checksums |
9e9221e15a13ed92026b795fd4dded1f7a475100efa8df22beb9679eb69cd7c1
|
| 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 12, 2026.
Transparency logRelease files / django_postgres_objects-1.0.1-py3-none-any.whl
| Download URL | django_postgres_objects-1.0.1-py3-none-any.whl |
|---|---|
| Size | 48.7 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
030072d3596a0a4513e452430bbf3de8a939bc8661378475bc13946be92f8937
|
|
BLAKE2b-256 checksum How to use checksums |
b4000ed70197e34c907f27c6b556ec66e0bc3fe924ff92f05f3c4118e8fae7e4
|
| 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 12, 2026.
Transparency log