Package¶
pghistory¶
- class pghistory.AfterInsert(label=None, *, condition=None)[source]¶
For events that happen after a database insert
- class pghistory.AfterInsertOrUpdate(label=None, *, condition=None)[source]¶
A database event that happens after insert/update
- class pghistory.AfterUpdate(label=None, *, condition=None)[source]¶
For events that happen 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 events that happen before a database deletion.
- class pghistory.BeforeUpdate(label=None, *, condition=None)[source]¶
For events that happen before a database update. The OLD values of the row will be snapshot to the event model
- class pghistory.DatabaseEvent(label=None, *, when=None, condition=None, operation=None, snapshot=None)[source]¶
For tracking an event automatically based on database changes.
- class pghistory.Event(label=None)[source]¶
For storing an event when a condition happens on a model
Events that inherit this base class are assumed to be manually created by the user. Only a “label” for the event is required.
Events that are automatically created in a Postgres trigger should inherit
DatabaseEvent
- class pghistory.Snapshot(label=None)[source]¶
A special database event that tracks changes to fields. A snapshot event always fires for an insert and also fires for updates when any fields change.
NOTE: Two triggers must be 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.
- 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 ofpghistory.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 callpghistory.context
as a function without entering it. The metadata set in the function call will be part of the context ifpghistory.context
has previously been entered. Otherwise it will be ignored.Usage:
- 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’)
- Parameters
metadata (dict) – Metadata that should be attached to the tracking context
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.
- Raises
ValueError – If the event label has not been registered for the model.
- Returns
The created event model.
- Return type
models.Model
- pghistory.get_event_model(model, *events, fields=None, exclude=None, obj_fk=<object object>, context_fk=<object object>, related_name=None, name=None, app_label=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 original model.Usage:
- class MyEventModel(get_event_model(
TrackedModel, pghistory.AfterInsert(‘model_create’),
- )):
# Add custom indices or change default field declarations…
- Parameters
model (models.Model) – The model that is being tracked.
*events (List[
Event
]) – See “events” help frompghistory.track
.fields (List[str], default=None) – See “fields” help from
pghistory.track
.exclude (List[str], default=None) – See “exclude” help from
pghistory.track
.obj_fk (models.ForeignKey) – See “obj_fk” help from
pghistory.track
.related_name (str, default=None) – See “related_name” help from
pghistory.track
.name (str, default=None) – See “model_name” help from
pghistory.track
.app_label (str, default=None) – See “app_label” help from
pghistory.track
.abstract (bool, default=True) –
True
if the generated model should be an abstract model.
- pghistory.track(*events, fields=None, exclude=None, obj_fk=<object object>, context_fk=<object object>, related_name=None, model_name=None, app_label=None)[source]¶
A decorator for tracking events for a mdoel.
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
*events (List[
Event
]) – The events to track. When using any event that inheritspghistory.DatabaseEvent
, such aspghistory.AfterInsert
, a Postgres trigger will be installed that automatically tracks the event with a generated event model. Events that do not inheritpghistory.DatabaseEvent
are assumed to be manually tracked 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 theDatabaseEvent
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_fk (models.ForeignKey, default=unset) – The foreign key field that references the eventged object. Defaults to a non-nullable foreign key that cascade deletes. Use
None
to create a event model with no reference to the tracked object. Seerelated_name
attribute for how the related_name is determined.context_fk (models.ForeignKey, default=unset) – The foreign key to tracked context, if any. Use
None
to avoid attaching historical context altogether.related_name (str, default=None) – The related name of the event model. If not provided, defaults to “event” if one is tracking changes to the entire model, otherwise defaults to a name based on the combination of fields.
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.
- 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.
- 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.AggregateEventQuery(*args, **kwargs)[source]¶
A query over an aggregate event CTE
- class pghistory.models.AggregateEventQuerySet(model=None, query=None, using=None, hints=None)[source]¶
QuerySet with support for Common Table Expressions
- 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()[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
- class pghistory.models.NoObjectsManager(*args, **kwargs)[source]¶
Django’s dumpdata and other commands will not work with AggregateEvent 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
- class pghistory.models.PGHistoryJSONField(verbose_name=None, name=None, encoder=None, decoder=None, **kwargs)[source]¶
- pghistory.models.create_event_model(base_class, tracked_model, fields=None, exclude=None, obj_fk=<object object>, context_fk=<object object>, abstract=True, related_name=None, name=None, app_label=None, attrs=None, meta=None)[source]¶
The primary factory function for dynamically creating a history model.
- Parameters
base_class (models.Model) – The base class from which the created model will inherit
tracked_model (models.Model) – The model being tracked
fields (List[str], default=None) – The fields to track. If None, all fields on tracked_model are tracked.
exclude (List[str], default=None) – If no fields are provided, exclude these fields from tracking.
obj_fk (models.ForeignKey, default=unset) – For overriding the foreign key that references the tracked object. If unset, defaults to a non-nullable foreign key that cascades. The related name defaults to
related_name
if all fields are tracked. Otherwise defaults to a combintion ofrelated_name
andfields
. UseNone
to create a tracking model without a foreign key to the tracked model.context_fk (models.ForeignKey, default=unset) – The foreign key to tracked context, if any. Use
None
to avoid attaching historical context altogether.abstract (bool, default=True) – True if the created model should be abstract
related_name (str) – The primary way to identify the relation of the created model and the tracked model
name (str, default=None) – The name of the created model. If None, defaults to a combination of the
related_name
,tracked_model
, andfields
.app_label (str, default=None) – The app_label for the created model. Defaults to the app_label of
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 for the tracking model.attrs (dict, default=None) – Additional attributes to add to the created model.
meta (dict, default=None) – Additional options to add to the model Meta