Appearance
Django adapter API
Everything in allowances_django. Requires pip install "allowances[django]" and "allowances_django" in INSTALLED_APPS.
Import from submodules
allowances_django/__init__.py is deliberately empty. Django imports an application package while building the app registry, before models are ready, so anything importing models from there would break startup. Use from allowances_django.shortcuts import fulfill.
Shortcuts
allowances_django.shortcuts — the same functions as the core engine, minus the ceremony. The store and provider are supplied for you, and subjects are the model instances you already have.
python
from allowances_django.shortcuts import (
peek, fulfill, attach, unfulfill, plan_switch,
archive_expired_wallets, find_unattached_fulfillment_records,
)python
peek(feature, quantity, subjects, *, provider=None, at=None, store=None)
fulfill(feature, quantity, subjects, *, guid=None, allow_partial=False,
provider=None, at=None, store=None)
attach(guid, obj, *, store=None)
unfulfill(guid, *, at=None, store=None)
plan_switch(subjects, open_wallets, effective_date=None, *, store=None)
archive_expired_wallets(at=None, *, store=None)
find_unattached_fulfillment_records(older_than=None, *, store=None)subjects and obj take model instances or refs, interchangeably. store= targets another database; provider= swaps the definition source — per-tenant configuration assembled at request time, say.
python
fulfill("send_email", 1, {"user": request.user, "team": team})
attach(result.guid, message)Subjects
allowances_django.subjects
python
subject_ref(obj) -> SubjectRef # instance or ref -> ref
object_ref(obj) -> ObjectRef # same, for attached objects
subject_refs(subjects: dict) -> dict # a whole mapping at once
resolve(ref) -> Model | None # ref -> instance, or None if gonesubject_ref produces SubjectRef("auth.user", "42") from a User. It raises ValueError on an unsaved instance and TypeError on something that isn't a model.
resolve returns None rather than raising when the subject has been deleted or its model no longer exists — these references aren't database-enforced, so an admin page should be able to render an orphaned row.
Store
allowances_django.store
python
DjangoStore(using: str | None = None)A Store over the ORM. using routes every query to that database alias. Cheap to construct — it holds no connection, only a small cache mapping record guids to primary keys.
Row-to-value converters, useful when writing reports against the models directly:
python
to_wallet(row) -> Wallet
to_transaction(row, record_guid=None) -> Transaction
to_record(row) -> FulfillmentRecordProviders
allowances_django.providers
python
SettingsProvider(config: dict | None = None)Reads settings.ALLOWANCES, or the config dict if given — which bypasses settings entirely and is what tests and per-tenant setups want.
Models
allowances_django.models. Tables are prefixed allowances_; the app label is allowances.
These are a persistence detail, not the library's vocabulary — the engine never imports them. Query them for reporting and admin; call the shortcuts to change anything.
Wallet
subject_type, subject_id, wallet_type, currency, balance, valid_from, valid_until, overdraft_limit, first_overdraft_at, reset_config, created_at, updated_at.
Amounts are DecimalField(max_digits=20, decimal_places=6).
Transaction
wallet (FK, cascade), record (FK, null), kind, amount, feature, storno_of (self FK, protected), object_type, object_id, created_at.
A partial unique constraint on storno_of enforces that a transaction is reversed at most once.
FulfillmentRecord
guid (unique), created_at, updated_at.
LicenseModifier
subject_type, subject_id, feature, factor, valid_from, valid_until.
Create these yourself — the engine only reads them:
python
LicenseModifier.objects.create(
subject_type="auth.user", subject_id=str(user.pk),
feature="send_email", factor=Decimal("0.5"),
valid_until=end_of_promo,
)WalletArchive / TransactionArchive
Where spent-out and expired wallets go. No foreign key back to the live wallet — it's gone by then; original_wallet_id is a plain reference. TransactionArchive.storno_of points at archived copies, rebuilt during archival.
Settings
python
ALLOWANCES = {
"currencies": {...},
"features": {...},
}The full shape is in Concepts. Absent, SettingsProvider reads an empty config and every lookup raises DefinitionNotFoundError.
Admin
All six models registered. Wallets and modifiers editable; transactions and archives read-only, on purpose.