Module

pghistory

class pghistory.AfterInsert(label=None, *, condition=None)[source]

For trackers that fire after a database insert

class pghistory.AfterInsertOrUpdate(label=None, *, condition=None)[source]

A database tracker that happens after insert/update

class pghistory.AfterUpdate(label=None, *, condition=None)[source]

For trackers that fire after a database update. The NEW values of the row will be snapshot to the event model

class pghistory.BeforeDelete(label=None, *, condition=None)[source]

For trackers that fire before a database deletion.

class pghistory.BeforeUpdate(label=None, *, condition=None)[source]

For trackers that fire before a database update. The OLD values of the row will be snapshot to the event model

class pghistory.BeforeUpdateOrDelete(label=None, *, condition=None)[source]

A database tracker that snapshots the old row during an update or delete

class pghistory.Changed(event_model, exclude=None)[source]

A utilty to create conditions based on changes in the tracked model.

Given the event model, we create a condition as follows:

  • If the event model trackes every field from the main model, we can use a standard OLD.* IS DISTINCT FROM NEW.* condition to snapshot every change on the main model.

  • If the event model tracks a subset of the fields of the main model, only changes to event fields will trigger a snapshot. In other words, if the main model has an int and char field, but the event model only tracks the char field, the condition will be OLD.char_field IS DISTINCT FROM NEW.char_field.

  • If one has fields on the event model and wishes to ignore them from triggering snapshots, pass them to the exclude argument to this utility.

class pghistory.ContextForeignKey(*, null=True, related_query_name=<object object>, **kwargs)[source]

Configuration for the pgh_context field when a foreign key is used.

Overrides null to True.

class pghistory.ContextJSONField(*, null=True, **kwargs)[source]

Configuration for the pgh_context field when denormalized context is used.

class pghistory.ContextUUIDField(*, null=True, **kwargs)[source]

Configuration for the pgh_context_id field when denormalized context is used.

class pghistory.DatabaseEvent(label=None, *, when=None, condition=None, operation=None, snapshot=None)[source]

The deprecated base class for all trigger-based trackers. Use DatabaseTracker instead.

class pghistory.DatabaseTracker(label=None, *, when=None, condition=None, operation=None, snapshot=None)[source]

For tracking an event automatically based on database changes.

setup(event_model)[source]

Set up the tracker for the event model

class pghistory.Event(label=None)[source]

The deprecated base class for event trackers. Use Tracker instead

class pghistory.Field(*, primary_key=<object object>, unique=<object object>, blank=<object object>, null=<object object>, db_index=<object object>, editable=<object object>, unique_for_date=<object object>, unique_for_month=<object object>, unique_for_year=<object object>)[source]

Configuration for fields.

Provides these default parameters:

  • primary_key (default=False)

  • unique (default=False)

  • blank

  • null

  • db_index (default=False)

  • editable

  • unique_for_date (default=None)

  • unique_for_month (default=None)

  • unique_for_year (default=None)

The default values for the above parameters ensure that event models don’t have unnecessary uniqueness constraints carried over from the tracked model.

class pghistory.ForeignKey(*, on_delete=<object object>, db_constraint=<object object>, **kwargs)[source]

Configuration for foreign keys.

The following parameters can be used:

  • on_delete (default=models.DO_NOTHING)

  • db_constraint (default=False)

Arguments for RelatedField and Field can also be supplied. Note that db_index is overridden to True for all foreign keys

class pghistory.ManualTracker(label=None)[source]

For manually tracking an event.

class pghistory.ObjForeignKey(*, related_name=<object object>, related_query_name=<object object>, **kwargs)[source]

Configuration for the pgh_obj field

pghistory.ProxyField(proxy, field)[source]

Proxies a JSON field from a model and adds it as a field in the queryset.

Parameters
  • proxy (str) – The value to proxy, e.g. “user__email”

  • field (Type[django.models.Field]) – The field that will be used to cast the resulting value

class pghistory.RelatedField(*, related_name=<object object>, related_query_name=<object object>, **kwargs)[source]

Configuration for related fields.

The following parameters can be used to configure defaults for all related fields:

  • related_name (default=”+”)

  • related_query_name (default=”+”)

By default, related names are stripped to avoid unnecessary clashes.

Note that all arguments from Field can also be supplied.

class pghistory.Snapshot(label=None, delayed=False)[source]

Tracks changes to fields. A snapshot tracker tracks inserts and updates. It ensures that no duplicate rows are created with a pre-configured condition.

NOTE: Two triggers are created since Insert triggers do not allow comparison against the OLD values. We could also place this in one trigger and do the condition in the plpgsql code.

setup(event_model)[source]

Set up the tracker for the event model

class pghistory.Tracker(label=None)[source]

For tracking an event when a condition happens on a model.

pghistory_setup(event_model)[source]

Registers the tracker for the event model and calls user-defined setup

setup(event_model)[source]

Set up the tracker for the event model

class pghistory.context(**metadata)[source]

A context manager that groups changes under the same context and adds additional metadata about the event.

Context is added as variables at the beginning of every SQL statement. By default, all variables are localized to the transaction (i.e SET LOCAL), meaning they will only persist for the statement/transaction and not across the session.

Once any code has entered pghistory.context, all subsequent entrances of pghistory.context will be grouped under the same context until the top-most parent exits.

To add context only if a parent has already entered pghistory.context, one can call pghistory.context as a function without entering it. The metadata set in the function call will be part of the context if pghistory.context has previously been entered. Otherwise it will be ignored.

Parameters

**metadata – Metadata that should be attached to the tracking context

Example

Here we track a “key” with a value of “value”:

with pghistory.context(key='value'):
    # Do things..
    # All tracked events will have the same ``pgh_context``
    # foreign key, and the context object will include
    # {'key': 'value'} in its metadata.
    # Nesting the tracker adds additional metadata to the current
    # context

# Add metadata if a parent piece of code has already entered
# pghistory.context
pghistory.context(key='value')

Notes

Context tracking is compatible for most scenarios, but it currently does not work for named cursors. Django uses named cursors for the .iterator() operator, which has no effect on history tracking. However, there may be other usages of named cursors in Django where history context is ignored.

pghistory.create_event(obj, *, label, using='default')[source]

Manually create a event for an object.

Events are automatically linked with any context being tracked via pghistory.context.

Parameters
  • obj (models.Model) – An instance of a model.

  • label (str) – The event label.

  • using (str) – The database

Raises

ValueError – If the event label has not been registered for the model.

Returns

The created event model.

Return type

models.Model

pghistory.create_event_model(tracked_model, *trackers, fields=None, exclude=None, obj_fk=<object object>, context_fk=<object object>, obj_field=<object object>, context_field=<object object>, context_id_field=<object object>, related_name=None, name=None, model_name=None, app_label=None, base_model=None, attrs=None, meta=None, abstract=True)[source]

Obtain a base event model.

Instead of using pghistory.track, which dynamically generates an event model, one can instead construct a event model themselves, which will also set up event tracking for the tracked model.

Parameters
  • tracked_model (models.Model) – The model that is being tracked.

  • *trackers (List[Tracker]) – The event trackers. When using any tracker that inherits pghistory.DatabaseTracker, such as pghistory.AfterInsert, a Postgres trigger will be installed that automatically tracks the event with a generated event model. Trackers that do not inherit pghistory.DatabaseTracker are assumed to have manual events created by the user.

  • fields (List[str], default=None) – The list of fields to snapshot when the event takes place. When no fields are provided, the entire model is snapshot when the event happens. Note that snapshotting of the OLD or NEW row is configured by the snapshot attribute of the DatabaseTracker object. Manual events must specify these fields during manual creation.

  • exclude (List[str], default=None) – Instead of providing a list of fields to snapshot, a user can instead provide a list of fields to not snapshot.

  • obj_field (pghistory.ObjForeignKey, default=unset) – The foreign key field configuration that references the tracked object. Defaults to an unconstrained non-nullable foreign key. Use None to create a event model with no reference to the tracked object.

  • context_field (Union[pghistory.ContextForeignKey, pghistory.ContextJSONField], default=unset) – The context field configuration. Defaults to a nullable unconstrained foreign key. Use None to avoid attaching historical context altogether.

  • context_id_field (pghistory.ContextUUIDField, default=unset) – The context ID field configuration when using a ContextJSONField for the context_field. When using a denormalized context field, the ID field is used to track the UUID of the context. Use None to avoid using this field for denormalized context.

  • model_name (str, default=None) – Use a custom model name when the event model is generated. Otherwise a default name based on the tracked model and fields will be created.

  • app_label (str, default=None) – The app_label for the generated event model. Defaults to the app_label of the tracked model. Note, when tracking a Django model (User) or a model of a third-party app, one must manually specify the app_label of an internal app to use so that migrations work properly.

  • base_model (models.Model, default=pghistory.models.Event) – The base model for the event model. Must inherit pghistory.models.Event.

  • attrs (dict, default=None) – Additional attributes to add to the event model

  • meta (dict, default=None) – Additional attributes to add to the Meta class of the event model.

  • abstract (bool, default=True) – True if the generated model should be an abstract model.

Example

Create a manual event model:

class MyEventModel(create_event_model(
    TrackedModel,
    pghistory.AfterInsert('model_create'),
)):
    # Add custom indices or change default field declarations...
pghistory.track(*trackers, fields=None, exclude=None, obj_fk=<object object>, context_fk=<object object>, obj_field=<object object>, context_field=<object object>, context_id_field=<object object>, related_name=None, model_name=None, app_label=None, base_model=None, attrs=None, meta=None)[source]

A decorator for tracking events for a model.

When using this decorator, an event model is dynamically generated that snapshots the entire model or supplied fields of the model based on the events supplied. The snapshot is accompanied with the label that identifies the event.

Parameters
  • *trackers (List[Tracker]) – The event trackers. When using any tracker that inherits pghistory.DatabaseTracker, such as pghistory.AfterInsert, a Postgres trigger will be installed that automatically tracks the event with a generated event model. Trackers that do not inherit pghistory.DatabaseTracker are assumed to have manual events created by the user.

  • fields (List[str], default=None) – The list of fields to snapshot when the event takes place. When no fields are provided, the entire model is snapshot when the event happens. Note that snapshotting of the OLD or NEW row is configured by the snapshot attribute of the DatabaseTracker object. Manual events must specify these fields during manual creation.

  • exclude (List[str], default=None) – Instead of providing a list of fields to snapshot, a user can instead provide a list of fields to not snapshot.

  • obj_field (pghistory.ObjForeignKey, default=unset) – The foreign key field configuration that references the tracked object. Defaults to an unconstrained non-nullable foreign key. Use None to create a event model with no reference to the tracked object.

  • context_field (Union[pghistory.ContextForeignKey, pghistory.ContextJSONField], default=unset) – The context field configuration. Defaults to a nullable unconstrained foreign key. Use None to avoid attaching historical context altogether.

  • context_id_field (pghistory.ContextUUIDField, default=unset) – The context ID field configuration when using a ContextJSONField for the context_field. When using a denormalized context field, the ID field is used to track the UUID of the context. Use None to avoid using this field for denormalized context.

  • model_name (str, default=None) – Use a custom model name when the event model is generated. Otherwise a default name based on the tracked model and fields will be created.

  • app_label (str, default=None) – The app_label for the generated event model. Defaults to the app_label of the tracked model. Note, when tracking a Django model (User) or a model of a third-party app, one must manually specify the app_label of an internal app to use so that migrations work properly.

  • base_model (models.Model, default=`pghistory.models.Event`) – The base model for the event model. Must inherit pghistory.models.Event.

  • attrs (dict, default=None) – Additional attributes to add to the event model

  • meta (dict, default=None) – Additional attributes to add to the Meta class of the event model.

pghistory.admin

class pghistory.admin.EventModelAdmin(model, admin_site)[source]

The base admin for event models

class pghistory.admin.EventsAdmin(model, admin_site)[source]
get_changelist(request, **kwargs)[source]

Return the ChangeList class for use on the changelist page.

get_list_display(request)[source]

Return a sequence containing the fields to be displayed on the changelist.

get_list_filter(request)[source]

Return a sequence containing the fields to be displayed as filters in the right sidebar of the changelist page.

get_queryset(request)[source]

Return a QuerySet of all model instances that can be edited by the admin site. This is used by changelist_view.

pghistory.middleware

pghistory.middleware.HistoryMiddleware(get_response)[source]

Annotates the user/url in the pghistory context.

class pghistory.middleware.WSGIRequest(environ)[source]

Although Django’s auth middleware sets the user in middleware, apps like django-rest-framework set the user in the view layer. This creates issues for pghistory tracking since the context needs to be set before DB operations happen.

This special WSGIRequest updates pghistory context when the request.user attribute is updated.

pghistory.models

class pghistory.models.AggregateEvent(*args, **kwargs)[source]

A proxy model for aggregating events together across tables and rendering diffs

exception DoesNotExist
exception MultipleObjectsReturned
class pghistory.models.BaseAggregateEvent(*args, **kwargs)[source]

A proxy model for aggregating events together across tables and rendering diffs

class pghistory.models.Context(id, created_at, updated_at, metadata)[source]
exception DoesNotExist
exception MultipleObjectsReturned
classmethod install_pgh_attach_context_func(using='default')[source]

Installs a custom store procedure for upserting context for historical events. The upsert is aware of when tracking is enabled in the app (i.e. using pghistory.context())

This stored procedure is automatically installed in pghistory migration 0004.

class pghistory.models.Event(*args, **kwargs)[source]

An abstract model for base elements of a event

property can_revert

True if the event model can revert the tracked model

classmethod check(**kwargs)[source]

Allow proxy models to inherit this model and define their own fields that are dynamically pulled from context

classmethod pghistory_setup()[source]

Called when the class is prepared (see apps.py) to finalize setup of the model and register triggers

revert(using='default')[source]

Reverts the tracked model based on the event fields.

Raises a RuntimeError if the event model doesn’t track all fields

class pghistory.models.EventQuery(model, where=<class 'django.db.models.sql.where.WhereNode'>, alias_cols=True)[source]

A query over an event CTE when proxy fields are used

get_compiler(*args, **kwargs)[source]

Overrides the Query method get_compiler in order to return an EventQueryCompiler.

class pghistory.models.EventQuerySet(model=None, query=None, using=None, hints=None)[source]

QuerySet with support for proxy fields

class pghistory.models.Events(*args, **kwargs)[source]

A proxy model for aggregating events together across tables and rendering diffs

exception DoesNotExist
exception MultipleObjectsReturned
classmethod check(**kwargs)[source]

Allow proxy models to inherit this model and define their own fields that are dynamically pulled from context

class pghistory.models.EventsQuery(*args, **kwargs)[source]

A query over an aggregate event CTE

chain(klass=None)[source]

Return a copy of the current Query that’s ready for another operation. The klass argument changes the type of the Query, e.g. UpdateQuery.

class pghistory.models.EventsQuerySet(model=None, query=None, using=None, hints=None)[source]

QuerySet with support for Common Table Expressions

across(*event_models)[source]

Aggregates events across the provided event models

references(*objs)[source]

Query any rows that reference the objs.

If, for example, a foreign key or pgh_obj field points to the object, it will be aggregated.

tracks(*objs)[source]

Query any rows with pgh_obj equal to the objs.

class pghistory.models.MiddlewareEvents(*args, **kwargs)[source]

A proxy model for aggregating events. Includes additional fields that are captured by the pghistory middleware

exception DoesNotExist
exception MultipleObjectsReturned
class pghistory.models.NoObjectsManager(*args, **kwargs)[source]

Django’s dumpdata and other commands will not work with Events models by default because of how they aggregate multiple tables based on objects.

We use this as the default manager for aggregate events so that dumpdata and other management commands still work with these models

get_queryset(*args, **kwargs)[source]

Return a new QuerySet object. Subclasses can override this method to customize the behavior of the Manager.

class pghistory.models.PghEventModel[source]

A descriptor for accessing the pgh_event_model field on a tracked model