django-trusts 1.0.0rc1 — declarative object-level permissions

Release notes

django-trusts 1.0.0rc1 is the first release candidate of the new schema-neutral object-authorization library for Django. With django-trusts, applications keep ownership of their permission models and declare relational paths connecting users, permissions, and protected objects. Those declarations support familiar user.has_perm(permission, obj) checks, permission enumeration, authorized querysets, and view guards.

Highlights

  • Register application-owned trust models using validated Django relationship paths.
  • Combine multiple complete authorization paths with OR.
  • Apply relationship conditions and named filters as restrictions; they cannot grant permission by themselves.
  • Compile persisted permission data into fixed, count-independent database queries.
  • Reject unsupported declarations and evaluation paths fail closed.
  • Express multi-tenancy or organization permissions as ordinary model relations (for example school → branch → record, or org → team → resource).
  • Support Python 3.12–3.14 with Django 6.1.

This RC supports Python 3.12–3.14 and Django 6.1. The provisional inherited-relationship evaluator is currently SQLite-only. Feedback on the registration API and real application permission models would be especially useful before v1.0.0.

Why another Django permissions framework?

django-guardian stores per-object ACL rows; django-rules evaluates Python predicates; django-trusts defines declarative relational policies compiled into database queries.

Neither approach directly models multi-tenant authorization as relationships. In many applications, access already follows the data model: users belong to organizations and teams, teams have roles, projects belong to organizations, and resources belong to projects. Copying those relationships into per-object ACL rows creates additional state to synchronize, while arbitrary Python predicates are difficult to apply consistently when filtering querysets.

django-trusts lets applications register the relational paths that grant a permission. Changes to membership or ownership therefore affect authorization through the existing data—without regenerating grants for every affected object. The same policy supports individual object checks and authorized queryset filtering, so permission filtering happens in the database before pagination.

The policy language is deliberately bounded. It uses registered Django relationships and validated conditions rather than arbitrary runtime callbacks. That makes the authorization surface smaller, inspectable, and suitable for fail-closed evaluation.