Registry / database / django-pghistory

django-pghistory

JSON →
library3.9.2pypypiunverified

django-pghistory is a Django library that provides simple, powerful, and performant history tracking for Django models using PostgreSQL's event triggers. It leverages database-level features for efficiency, making it suitable for auditing and versioning. The current stable version is 3.9.2, and it maintains a regular release cadence with several minor versions and patches throughout the year.

pip install django-pghistory 'psycopg[binary]' django
INSTALL
IMPORT
SIG · DJANGO-PGHISTORY
D
django-pghistory
databasepythonv3.9.2
Install
4.1s avg
Import
918ms
Disk
84MB
Pass rate
10/ 10
Env Coverage10 / 10
glibc
3.93.13
musl
3.93.13
Install & Compatibility
Where this runs
tested against v3.9.2 · pip install
no network on importno background threads
Install × environment matrix
Each cell = how many times install + import succeeded across repeated harness runs. Partial = flaky.
glibc = Debian/Ubuntu slim · musl = Alpine Linux
musl
py 3.103.920 runs
installs and imports cleanly · install 0.0s · import 0.950s · 84.2MB
glibc
py 3.103.920 runs
installs and imports cleanly · install 4.1s · import 0.885s · 87MB
84MB installed
● package 84MB
Code
Verified usage

Verified import paths — ran on the pinned version, not inferred.

track
import pghistory
context
import pghistory
get_event_model
import pghistory
ProxyField
import pghistory.models
admin
import pghistory.admin
from django.contrib import admin; admin.site.register(MyHistoryModel)
pghistory provides its own admin integration for history models, which simplifies registration and adds extra features.

This quickstart demonstrates how to define a Django model and enable history tracking using the `@pghistory.track` decorator. It also shows how to retrieve the historical events through the automatically generated history model. Remember to add `pghistory` and `pghistory.admin` to `INSTALLED_APPS` and run `makemigrations` and `migrate` after defining your models.

import os import django from django.conf import settings from django.db import models settings.configure( DEBUG=True, INSTALLED_APPS=[ 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.sites', 'django.contrib.messages', 'django.contrib.staticfiles', 'pghistory', 'pghistory.admin', 'your_app_name' # Replace with your actual app name ], DATABASES={ 'default': { 'ENGINE': 'django.db.backends.postgresql', 'NAME': os.environ.get('PG_DB_NAME', 'test_db'), 'USER': os.environ.get('PG_DB_USER', 'test_user'), 'HOST': os.environ.get('PG_DB_HOST', 'localhost'), 'PORT': os.environ.get('PG_DB_PORT', '5432'), 'PASSWORD': os.environ.get('PG_DB_PASSWORD', 'password'), } }, MIDDLEWARE_CLASSES=( 'django.contrib.sessions.middleware.SessionMiddleware', 'django.middleware.common.CommonMiddleware', 'django.middleware.csrf.CsrfViewMiddleware', 'django.contrib.auth.middleware.AuthenticationMiddleware', 'django.contrib.messages.middleware.MessageMiddleware', ), TEMPLATES=[ { 'BACKEND': 'django.template.backends.django.DjangoTemplates', 'DIRS': [], 'APP_DIRS': True, 'OPTIONS': { 'context_processors': [ 'django.template.context_processors.debug', 'django.template.context_processors.request', 'django.contrib.auth.context_processors.auth', 'django.contrib.messages.context_processors.messages', ], }, }, ], STATIC_URL = '/static/', SECRET_KEY = 'a-very-secret-key', ) django.setup() import pghistory # Define your model @pghistory.track(fields=['name', 'value']) class MyModel(models.Model): name = models.CharField(max_length=255) value = models.IntegerField(default=0) created_at = models.DateTimeField(auto_now_add=True) updated_at = models.DateTimeField(auto_now=True) def __str__(self): return f"{self.name} - {self.value}" class Meta: app_label = 'your_app_name' # After running makemigrations and migrate: # from your_app_name.models import MyModel # obj = MyModel.objects.create(name='Test', value=10) # obj.value = 20 # obj.save() # # HistoryModel = pghistory.get_event_model(MyModel) # for event in HistoryModel.objects.all(): # print(f"ID: {event.pgh_obj_id}, Name: {event.name}, Value: {event.value}, Changed at: {event.pgh_created_at}")
Debug
Known issues
breakingPython 3.9 support was dropped in version 3.9.0. Ensure your project is running Python 3.10 or newer.
fix
Upgrade your Python environment to 3.10 or later. Consider Django 6.0 compatibility, which was also introduced in 3.9.0.
affects: >=3.9.0
gotchaInstalling `pghistory.admin` in `INSTALLED_APPS` registers the admin automatically for all tracked models. If you need custom admin registration for history models, you should not include `pghistory.admin` and register them manually using `pghistory.admin.site.register`.
fix
To avoid automatic registration, remove 'pghistory.admin' from `INSTALLED_APPS` and register history models explicitly with `pghistory.admin.site.register(pghistory.get_event_model(MyModel))`.
affects: >=3.9.2 (documentation warning added), all versions.
gotchaContext tracking in `django-pghistory` can be ignored for certain SQL statements (e.g., `VACUUM`, `SELECT` without `FOR UPDATE`). This is by design to prevent issues or track irrelevant changes, but it means certain database operations will not generate context events.
fix
Be aware that context events are primarily for DML (INSERT, UPDATE, DELETE) operations. Do not expect context for all SQL statement types. Review the documentation for specific ignored statements.
affects: >=3.7.0, >=3.8.3
gotchaUpgrading existing tracking models to denormalized context requires specific migration steps. Simply changing tracking configuration won't automatically update existing history tables.
fix
Refer to the 'Migrating existing tracking models to denormalized context' section in the official documentation for detailed steps on how to migrate your historical data and schema.
affects: >=3.8.0
gotchaStatement-level history tracking (introduced in 3.6.0) significantly changes how triggers work, especially for bulk operations. While it offers performance improvements, it may alter the granularity or content of recorded history events compared to row-level tracking.
fix
If adopting statement-level tracking (`@pghistory.track(level=pghistory.Statement)` or `PGHISTORY_LEVEL = pghistory.Statement`), thoroughly test how your history is recorded for bulk actions. Understand the differences in how `new`, `old`, and context data are captured.
affects: >=3.6.0
Upgrade
Version history
3.9.2latest on PyPI · released Feb 17, 2026
Audit
Dependencies
DjangorequiredCore framework dependency for the library.
psycopgrequiredPostgreSQL adapter for Django, required for interaction with the database triggers.
Agent activity
25 hits · last 30 days
node
20
OpenAI (training)
2
Resources
django-pghistory — pip install django-pghistory · libregistry