Skip to main content

drf-actions

PyPI Python versions Django Tests License: MIT

Django app that keeps an event journal of row changes (INSERT / UPDATE / DELETE) for selected models using native PostgreSQL triggers, and exposes the journal through a read-only Django REST Framework API.

Because the journal is populated by database triggers (not Django signals), it captures every change — including raw SQL, bulk_update, and writes from other services sharing the same database.

How it works

  1. You describe which models (tables) to watch in the DRF_ACTIONS_SETTINGS setting.
  2. For each watched model you create an ActionContentType record. On save, the app generates a PL/pgSQL function and an AFTER INSERT OR UPDATE OR DELETE trigger on the table.
  3. Every change to the table inserts a row into the EventJournal table with the reason (INSERT / UPDATE / DELETE), the object id and a JSON snapshot of the configured fields.
  4. Consumers read the journal via the REST endpoint (protected by API keys) or directly via the ORM.

Requirements

  • Python 3.10+
  • Django 4.2 / 5.x (5.2 LTS is the primary target)
  • Django REST Framework 3.16+
  • PostgreSQL (the event journal is populated by native PG triggers)

Installation

pip install drf-actions

Add the app and its dependencies to INSTALLED_APPS:

INSTALLED_APPS = [
    # ...
    "rest_framework",
    "rest_framework_api_key",
    "django_filters",
    "django_json_widget",  # nicer JSON display in the admin
    "drf_actions",
]

Run the migrations:

python manage.py migrate drf_actions

Configuration

Describe the models you want to track with DRF_ACTIONS_SETTINGS in your Django settings:

DRF_ACTIONS_SETTINGS = {
    "content_types": {
        "user": {
            # primary key field of the watched table
            "pk": "id",
            # optional: column whose value is stored as "owner" in the event data
            "owner": None,
            # optional: fire UPDATE events only when these columns change
            "catch_update": [],
            # optional: many-to-many relations to embed into the event data
            "m2m": [
                # (through_table, fk_to_related, fk_to_this,
                #  related_table, related_pk, related_column, json_key)
                ("users_user_groups", "group_id", "user_id",
                 "auth_group", "id", "name", "groups"),
            ],
            # (app_label, model_name) of the watched model
            "model": ("users", "user"),
            # columns to snapshot: (json_key, table_column)
            "fields": (
                ("email", "email"),
                ("full_name", "full_name"),
            ),
        },
    },
    # backfill events for rows that already exist when a trigger is installed
    "create_event_for_new_entity": True,
}

Content type options

Key Required Description
pk yes Name of the primary key column of the watched table.
model yes (app_label, model_name) tuple used to resolve the Django model.
fields yes Iterable of (json_key, table_column) pairs snapshotted into EventJournal.data.
owner no Column stored as owner in the event data (e.g. to route notifications).
catch_update no List of columns; when set, UPDATE events fire only if one of them changes.
m2m no Many-to-many relations aggregated into the event data (see the tuple layout above).

Installing the triggers

Create an ActionContentType for each configured content type — the PostgreSQL function and trigger are created automatically on save:

from drf_actions.models import ActionContentType

ActionContentType.objects.create(content_type="user", table="users_user")

If create_event_for_new_entity is True, existing rows are backfilled into the journal as INSERT events at that moment. Deleting an ActionContentType drops the trigger and its function.

REST API

Hook up the bundled router:

# urls.py
from django.urls import include, path

urlpatterns = [
    # ...
    path("api/", include("drf_actions.urls")),
]

This exposes a read-only endpoint at api/actions/events/, protected with rest_framework_api_key (send Authorization: Api-Key <key>).

Supported query parameters:

  • filtering: id, id__in, id__lt/lte/gt/gte, created__range/lt/lte/gt/gte, reason, reason__in, content_type, content_type__in
  • ordering: ordering=id, ordering=-created
  • pagination: page, page_size (default 100, max 10000)

Example response item:

{
    "id": 42,
    "reason": "UPDATE",
    "object_id": "7",
    "content_type": "user",
    "data": {"email": "user@example.com", "full_name": "Jane Doe", "groups": ["editors"]},
    "created": "2026-07-13T12:00:00Z",
    "modified": "2026-07-13T12:00:00Z"
}

Development

docker run -d --name drf-actions-test-pg \
  -e POSTGRES_PASSWORD=postgres -e POSTGRES_DB=drf_actions \
  -p 54329:5432 postgres:16-alpine
uv venv --python 3.13 .venv
uv pip install --python .venv/bin/python -e . pytest pytest-django "psycopg[binary]"
.venv/bin/python -m pytest

License

MIT

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

drf_actions-0.5.0.tar.gz (12.2 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

drf_actions-0.5.0-py3-none-any.whl (14.0 kB view details)

Uploaded Python 3

File details

Details for the file drf_actions-0.5.0.tar.gz.

File metadata

  • Download URL: drf_actions-0.5.0.tar.gz
  • Upload date:
  • Size: 12.2 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: poetry/2.4.1 CPython/3.12.8 Linux/6.17.0-1018-azure

File hashes

Hashes for drf_actions-0.5.0.tar.gz
Algorithm Hash digest
SHA256 3223ec7f900f04fbac611ba4a8f3a2967053f335700d34c82a0341b657cc44c3
MD5 19ab434b6aa7b00bfaaa5bfc3ce1602d
BLAKE2b-256 01e272d0154b4768bb1ecf4395014099fc39ecdb97df8b1de1d0e335dfb2b799

See more details on using hashes here.

File details

Details for the file drf_actions-0.5.0-py3-none-any.whl.

File metadata

  • Download URL: drf_actions-0.5.0-py3-none-any.whl
  • Upload date:
  • Size: 14.0 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: poetry/2.4.1 CPython/3.12.8 Linux/6.17.0-1018-azure

File hashes

Hashes for drf_actions-0.5.0-py3-none-any.whl
Algorithm Hash digest
SHA256 ddae6f29c1355dce4c942d3952ffdf6a3effa8ddca5dcd7bdc130c10e0c7325e
MD5 5d36c186d73f5dd2c8494b433a9460aa
BLAKE2b-256 35c63d046023d5e0af63ac28879fb93685b3a8d8cd84f3205b15fdcbe9695cd5

See more details on using hashes here.

Release history Release notifications | RSS feed

This release

0.5.0 This release

2 files

0.4.0

2 files

0.3.0

2 files

0.2.1

2 files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page