django-maker-checker
Not every change should take effect immediately.
django-maker-checker is an open-source, headless maker-checker (four-eyes) approval engine for Django. It lets you require a second authorized person to approve sensitive changes before they take effect.
In financial and enterprise systems, a user might need to change a credit limit, create a privileged user, modify account details, or initiate another sensitive operation. You need more than an approval button: you need enforceable separation of duties, configurable policies, controlled execution and an audit trail.
django-maker-checker provides reusable infrastructure for these controls, so you don't have to build a separate approval mechanism for every Django application.
Why django-maker-checker?
- Changes are held until approval. A guarded action becomes an approval request rather than taking effect immediately. On approval, the action executes against the current state of the data; if that state has changed, the request fails instead of silently overwriting newer changes.
- Separation of duties is enforced. The maker cannot approve their own request, including through privileged-user or delegation paths. A person cannot approve multiple steps of the same request and a request cannot be raised if no eligible checker exists.
- Approval rules are data, not code. Configure which actions require approval, who can approve them and how approval steps work without deploying code for every policy change. Policy changes are versioned with who made them, when, and why.
- Approvals have operational controls. Configure SLAs, reminders, and escalation to backup approvers so pending requests don't depend entirely on someone remembering to check a queue.
- Decisions are auditable. Requests have a timeline, decisions are recorded, and events can be passed to your audit log.
- Concurrent decisions are controlled. Decisions lock the relevant row and only one open request may exist per item, subject to the database requirements below.
- Your UI stays yours. The library provides models, rules, services, and read queries without imposing a frontend or admin interface.
Who is it for?
django-maker-checker is designed for Django applications where sensitive operations need an independent approval step.
| Use case | Example |
|---|---|
| Fintech and financial systems | Changing credit limits or payment instructions |
| User administration | Creating or deleting privileged users |
| Accounting and back-office systems | Approving sensitive account or configuration changes |
| Multi-tenant SaaS | Letting each tenant define its own approval policies |
| Enterprise applications | Requiring independent authorization for sensitive operations |
These are examples of workflows the library can help implement; each action must still be integrated and configured by the application.
How it works
The typical lifecycle is straightforward:
- Define an action. Mark a sensitive operation as requiring approval when its policy says so.
- Submit a request. A maker initiates the action. Instead of applying the change immediately, the library creates an approval request.
- Evaluate the policy. The request is routed to eligible checkers according to the configured rules.
- Review the request. A checker can approve, reject, or request changes, depending on the workflow.
- Apply the change. Once the approval requirements are satisfied, the action executes against the current data state.
- Record the outcome. The request's decisions and events remain available for auditing.
The approval engine handles the workflow; your application remains responsible for defining the business operation and building the user experience.
How it compares
| Capability | django-maker-checker | Workflow engines (e.g. django-approval-workflow) | Moderation libraries (e.g. django-approval) |
|---|---|---|---|
| Primary purpose | Approval-gated execution of sensitive actions | Orchestrating workflows across steps | Reviewing proposed object edits |
| Approval policy | Tenant-configurable rules | Typically defined per workflow | Typically defined per model or workflow |
| Separation of duties | Enforced by the approval engine | Depends on workflow configuration | Depends on implementation and permissions |
| SLAs and escalation | Built in | May require additional scheduling and escalation logic | Depends on the library |
| Multi-tenancy | Supported through configurable integration points | Depends on the engine | Depends on the library |
| User interface | Headless | Depends on the engine | Depends on the library |
Choose a broader workflow engine if you need complex orchestration, such as parallel branches, organizational hierarchies, or rich process routing.
Choose django-maker-checker when your primary requirement is to ensure that sensitive changes cannot be made by one person acting alone, with approval rules and decisions that can be audited.
Requirements
- Python 3.11+
- Django 5.2, 6.0, or 6.1
django.contrib.authanddjango.contrib.contenttypes- django-fsm-2, installed automatically
Database: PostgreSQL is recommended for production. SQLite works for development and tests. On MySQL/MariaDB, the guarantee that only one open request exists per item is not enforced because these databases ignore Django's conditional unique constraints.
Background jobs: Celery is optional. It can run the SLA job; alternatively, schedule the relevant management command with cron.
Install the Celery integration with:
pip install "django-maker-checker[celery]"
Quick start
1. Install the package
pip install django-maker-checker
2. Add it to Django
A single-tenant project using Django's built-in users, groups, and permissions requires no additional integration configuration.
# settings.py
INSTALLED_APPS = [
# ...
"django.contrib.auth",
"django.contrib.contenttypes",
"django_maker_checker",
]
3. Define an approval-gated action
Suppose you want credit-limit changes to require approval.
from django_maker_checker.actions import approval_action
@approval_action(
"credit_limit_change",
label="Credit Limit Change",
category="Accounts",
lock_key=lambda p: f"account:{p['account_id']}",
)
def change_credit_limit(
*,
tenant,
maker,
context,
account_id,
new_limit,
):
Account.objects.filter(pk=account_id).update(
credit_limit=new_limit
)
The lock_key identifies the account associated with the request, allowing the library to prevent multiple open requests for the same item when the database supports the required constraint.
Register the action from your application's AppConfig.ready().
4. Configure an approval policy
Run the migrations:
python manage.py migrate
The migrations also create the permission approvals.approve_credit_limit_change. Grant it to the appropriate group, then configure the policy to use that group.
from django_maker_checker import policies, services
from django_maker_checker.constants import Decision
from django_maker_checker.models import Tenant
policies.save_policy(
policies.policy_for(
Tenant.default(),
"credit_limit_change",
),
policies.PolicySpec(
approvers=[f"r:{risk_officers.pk}"],
),
by=admin_user,
)
5. Submit and approve a request
When a maker initiates the action, the change is held pending approval.
outcome = change_credit_limit(
tenant=Tenant.default(),
maker=request.user,
justification="Annual review",
account_id=42,
new_limit="5000.00",
)
assert outcome.pending # The credit limit has not changed yet.
An eligible checker can approve the request:
services.decide(
outcome.approval_request.pk,
risk_officer,
Decision.APPROVE,
"Within appetite",
)
Once the approval requirements are satisfied, the action executes and the resulting change is recorded.
The same workflow supports rejection with a reason and requesting changes from the maker.
Note: This example illustrates the core API. Your application must supply its own Account model, user and group setup, action registration, and approval interface.
Configurable approval workflows
Approval policies can express different requirements, including:
- A single approver.
- Any N of several eligible approvers.
- Approval from all required approvers.
- Ordered approval steps.
- SLAs, reminders and escalation to backup approvers.
Policies are stored as data, allowing authorized administrators to adjust approval requirements without deploying application code for every policy change.
Integrating with your application
The library is headless by design. It doesn't force you to use a particular frontend, admin interface, or API framework.
For multi-tenant applications or projects with custom roles and permissions, configure the relevant integration points:
APPROVALS_TENANT_MODELAPPROVALS_ROLE_MODELAPPROVALS_ADAPTER
See the integration guide.
Documentation
Full documentation: https://django-maker-checker.readthedocs.io
- Quick start
- Concepts and lifecycle
- Defining actions
- Configuring approval rules
- Building a UI
- Settings
- API reference
- Development guide
Contributing
Contributions are welcome! Whether you're improving the API, reporting edge cases, adding integrations, or strengthening the test suite, your input is valuable.
- Read CONTRIBUTING.md for contribution guidelines.
- Follow the development guide to set up your environment.
- Start with tests/README.md, which describes the test suite and how it specifies expected library behaviour.
- Open an issue to discuss substantial changes before submitting a pull request.
Bug reports, use cases, design feedback, and pull requests are all appreciated.
License
Released under the MIT License.
Metadata
Release files for django-maker-checker 0.8.0
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_maker_checker-0.8.0.tar.gz | 82.6 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| django_maker_checker-0.8.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 153.1 kB
Release files / django_maker_checker-0.8.0.tar.gz
| Download URL | django_maker_checker-0.8.0.tar.gz |
|---|---|
| Size | 82.6 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
0c6080d39d80d6ed13c1cecda61a13895227092f411e3cc9f16b80773ffce400
|
|
BLAKE2b-256 checksum How to use checksums |
bb439cab9b3c42f9ce5f7135b044214ced369fd20e41fa9d1c9dc1d127614fdf
|
| 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 14, 2026.
Transparency logRelease files / django_maker_checker-0.8.0-py3-none-any.whl
| Download URL | django_maker_checker-0.8.0-py3-none-any.whl |
|---|---|
| Size | 70.4 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
c643c8517fc136daa30a7db3153028d5d8ef587ba1396db3b193bcfefcd3d266
|
|
BLAKE2b-256 checksum How to use checksums |
173639a8373fe93576b5db163370948b72abc73ed1de8f356a8e61f2825c8c16
|
| 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 14, 2026.
Transparency log