🚀 django_resaas
Full-stack framework for building secure, multi-tenant business applications with Django and Quasar — this is the Django backend; quasar_resaas is the frontend.
If you've ever built a SaaS API in Django, you know the drill: multi-tenancy, per-group and per-branch permissions, soft delete, file uploads, PDF generation, i18n, usage limits… all of it again, project after project.
django_resaas solves that part once and for all. It's a framework built on top of Django + DRF that gives any application a production-ready foundation: multi-tenancy, RBAC, smart CRUD, dynamic search, per-client feature modules and entitlements (features and usage limits) — so your team can focus on what actually matters: the business.
pip install django_resaas
Table of contents
- Why it exists
- Features
- Installation & setup
- Architecture
- Full example in 3 files
- Multi-tenancy & RBAC
- Soft delete
- Automatic search & filters
- Per-client modules & entitlements
- Middlewares
- Internationalization (i18n)
- CLI / management commands
- Tech stack
- Documentation
- Roadmap
- Contributing
- License
🎯 Why it exists
"Building SaaS shouldn't be repetitive."
Every multi-tenant SaaS app ends up needing the same set of building blocks. django_resaas ships them ready-made, tested, and consistent with each other:
| Without django_resaas | With django_resaas |
|---|---|
| Multi-tenancy hand-rolled on every project | BaseModel already ships entity + branch |
| Permissions checked manually in every view | Automatic RBAC via Entity + Branch + Group |
| CRUD written from scratch for every resource | BaseAPIView gives full CRUD in ~5 lines |
| Destructive delete with no way back | Native soft delete + restore + hard delete |
| Custom search per endpoint | Automatic dynamic search (?search=) |
| "All or nothing" modules | Per-client module activation (App + EntityApp) |
| Translations scattered across the code | Central i18n system (DB + lang/ files) |
✨ Features
- 🔐 RBAC — permissions by
User+Group+ context (Entity/Branch) - 🏢 Native multi-tenancy — isolation by
EntityandBranch - ⚡ Automatic CRUD —
BaseAPIViewwith pagination, ordering, filtering and permissions built in - 🔎 Dynamic search — automatic search across text fields and relations
- ♻️ Soft delete —
delete()/restore()/hard_delete()+ dedicated managers - 🧩 Per-client modules — toggle features on/off per entity without a deploy (
App+EntityApp) - 🎚️ Entitlements — features, capacities (quantitative limits) and modules per installation/tenant, behind a replaceable provider, enforced by the backend
- 📎 Files & PDF — secure uploads, automatic metadata, PDF generation (WeasyPrint)
- 🔑 JWT auth + 2FA —
simplejwt, OTP (pyotp) and QR codes built in - 🌍 Built-in i18n — file-based translations (
pt-pt,en-us,es-es,fr-fr) and database-backed - 🌐 Dedicated middlewares — tenant context, frontend protection and file access control
- 💵 Native money support (
django-money)
⚙️ Installation & setup
pip install django_resaas
# or, for local development:
pip install -e .
# settings.py
INSTALLED_APPS = [
"django_resaas.saas", # the core
"django_resaas.notifications", # email / SMS / WhatsApp outbox
"your_app", # your own modules
...
]
MIDDLEWARE = [
...
"django_resaas.saas.core.middleware.file_access.FileAccessMiddleware",
"django_resaas.saas.core.middleware.tenant.TenantContextMiddleware",
]
The complete, working settings (REST framework, JWT, CORS, RESAAS_*) are in
Installation. Then:
python manage.py migrate
python manage.py create_root
python manage.py runserver
🧠 Architecture
EntityType
↓
Entity (tenant)
↓
Branch
↓
Group ──→ Permission
Person ──→ User ──→ BranchUserGroup (User + Branch + Group)
Business modules (HR, Health, Sales, ...) are separate Django apps built on this core; the framework itself ships none.
Key concepts:
| Concept | Role |
|---|---|
Entity |
The tenant — typically the client/company |
Branch |
A unit/location within an Entity |
Person |
Human data (name, email, contacts) |
User |
Authentication |
EntityType |
The kind of tenant; carries default groups and modules |
BranchUserGroup |
Links User + Branch + Group, allowing multiple groups per branch |
🧪 Full example in 3 files
Model
from django.db import models
from django_resaas.saas.core.base.models import BaseModel
class Product(BaseModel):
name = models.CharField(max_length=150)
price = models.DecimalField(max_digits=12, decimal_places=2)
BaseModel already ships entity, branch, created_at/updated_at, created_by/updated_by and soft delete.
Serializer
from django_resaas.saas.core.base.serializers import BaseSerializer
class ProductSerializer(BaseSerializer):
class Meta:
model = Product
fields = "__all__"
View
from django_resaas.saas.core.base.views import BaseAPIView, register_view
@register_view("products", module="your_app")
class ProductAPIView(BaseAPIView):
queryset = Product.objects.all()
serializer_class = ProductSerializer
(registerView is the same decorator under its original name, still supported.)
The module must be activated for the Entity (App + EntityApp) — see the
Quick start.
That's enough to automatically get: full CRUD, multi-tenant isolation, permissions, search, soft delete, restore, and protection based on the active module.
🔐 Multi-tenancy & RBAC
Tenant context is never trusted from raw client-supplied values. It travels as a single signed, short-lived token, issued by the API after checking the user actually has access to the Entity/Branch/Group requested.
1. Issue a context, once the user is authenticated:
POST /api/resaas/context/
Authorization: Bearer <access_token>
{
"entity_id": "...",
"branch_id": "...", // optional
"group_id": "..." // optional
}
ResaasContextService validates access (via EntityUser / BranchUser / BranchUserGroup, with a superuser/entity-admin bypass) and signs the result with django.core.signing — versioned and bound to a TTL (1h by default, RESAAS_CONTEXT_TTL setting):
{ "token": "<signed-context-token>", "context": { "entity_id": "...", "branch_id": "...", "group_id": "..." } }
2. Send it back on every request, alongside auth and language — three headers, one job each:
| Header | Purpose |
|---|---|
Authorization |
Bearer <JWT> — who you are |
X-RESAAS-Context |
signed tenant context — where you're operating (entity/branch/group) |
L |
active language id |
TenantContextMiddleware decodes the token and verifies its signature and expiry on every request, exposing:
request.entity_type_id
request.entity_id
request.branch_id
request.group_id
BaseAPIView then re-validates that the context still belongs to the authenticated user (ResaasContextService.validate_for_user) before touching the queryset — a forged, expired, or replayed token from another user/tenant is rejected even if it was valid at issue time.
If a resource's module isn't active for the Entity, access is blocked automatically — no extra code in the view.
⚠️ Breaking change from earlier versions: the old scheme (raw
ET/E/S/Gheaders sent directly by the client) has been replaced by the signedX-RESAAS-Contexttoken above. If you're upgrading, swap those headers for a call toPOST /resaas/context/and forward the returned token instead.
🔁 Soft delete
obj.delete() # soft delete
obj.restore() # restore
obj.hard_delete() # permanently delete
Model.objects # active only
Model.deleted_objects # deleted only
Model.all_objects # everything
🔎 Automatic search & filters
GET /api/your_app/products/?search=john
BaseAPIView automatically searches text fields and relations (ForeignKey), with no per-endpoint configuration required.
🧩 Per-client modules & entitlements
Each Entity only sees the modules it has activated:
| Entity | Module | Status |
|---|---|---|
| Company A | Sales | ✅ |
| Company A | CRM | ❌ |
Activation is a direct App ↔ Entity link via EntityApp (toggled with its state field):
EntityApp.objects.get_or_create(app=app, entity=entity, state='Active')
Entitlements add what the installation/tenant may use, on top of activation:
features (multi_entity), capacities (quantitative limits such as branches) and
allowed modules. They are off by default (nothing restricted), configured with
RESAAS_ENTITLEMENTS or a custom provider, and always enforced by the backend:
# illustrative values - RESAAS ships no plans or official limits
RESAAS_ENTITLEMENTS = {"features": {"advanced_audit": True},
"capacities": {"branches": 3, "users": 20}}
Entitlements sit between the tenant context and permissions — they never replace a permission:
Authentication → Signed tenant context → Entitlements → Permissions → Object scope → Field authorization → Business operation
Installing the package does not mean unlimited use: what an installation may use follows the license and its entitlements. The framework knows capabilities only — commercial plans are mapped to them by a provider. There is no billing or payment layer. See Entitlements.
🌐 Middlewares
| Middleware | Responsibility |
|---|---|
TenantContextMiddleware |
Decodes the signed X-RESAAS-Context token and resolves entity, branch, group (L header for language) |
FrontEndMiddleware |
Protects access via FEK/FEP frontend credentials and route/HTTP-method permissions |
FileAccessMiddleware |
Controls access to protected files and media |
🌍 Internationalization (i18n)
Translations are resolved in cascade — database first, then each app's lang/ files — with automatic caching:
from django_resaas.saas.core.utils.translate import Translate
Translate.tdc(request, "Register")
Languages included out of the box: pt-pt, en-us, es-es, fr-fr.
🛠 CLI / management commands
python manage.py setup # initial SaaS bootstrap
python manage.py create_entity # creates a new Entity (tenant)
python manage.py create_root # creates the root user
python manage.py sync_language # loads the default languages
python manage.py sync_actions # syncs views registered in VIEW_REGISTRY
python manage.py check # Django's system check framework
python manage.py check_metano # validates compliance with the MetanoStack standard
🧰 Tech stack
| Layer | Technology |
|---|---|
| Backend | Django 5.2 + Django REST Framework |
| Auth | djangorestframework-simplejwt, 2FA (pyotp, qrcode) |
| Database | PostgreSQL (psycopg) |
| Documents | WeasyPrint (PDF), python-barcode |
| Filtering | django-filter |
| Money | django-money |
| Deployment | Gunicorn |
📚 Documentation
Full technical documentation lives in docs/:
- Installation · Quick start
- Architecture · Multi-tenancy · Request lifecycle · Middleware · View registry
- Schema 1.0 contract · Public API reference
- BaseAPIView · Search · Filters & pagination
- Permissions · Field-level permissions · Entitlements
- Soft delete · Files & PDF
- Creating a new resource · Building a module · Management commands
- Upgrading (breaking changes)
- Git Flow & releases
- Troubleshooting
🚀 Roadmap
- Stripe integration
- Billing dashboard
- Resource auto-router
- Action auditing
- Multi-tenant logs
- Permission cache (Redis)
🤝 Contributing
Pull requests are welcome. For larger changes, please open an issue first to discuss direction.
git clone https://github.com/metanochava/django_resaas.git
cd django_resaas
pip install -e .
make check
📄 License
Distributed under the RESAAS Commercial License. The package can be installed and used within the terms of that license and the entitlements enabled for your installation; anything beyond that needs a written license. Versions published before this license keep the license they were published with.
Made by Metano Chavana
Metadata
Release files for django-resaas 0.0.652
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_resaas-0.0.652.tar.gz | 413.4 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| django_resaas-0.0.652-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 963.9 kB
Release files / django_resaas-0.0.652.tar.gz
| Download URL | django_resaas-0.0.652.tar.gz |
|---|---|
| Size | 413.4 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
87f4894eb405b4142507b33229639208639e1d17d4675e763e08ae8d6256e1f7
|
|
BLAKE2b-256 checksum How to use checksums |
a857d4f90efe5df59ce31e0251b12ac26f254c564e2ea8aac29652ee7f741e1a
|
| 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_resaas-0.0.652-py3-none-any.whl
| Download URL | django_resaas-0.0.652-py3-none-any.whl |
|---|---|
| Size | 550.5 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
f1c3c509c04f65a2c7e471f723a931f072a89a8c1deaa77054d0602cfff1ec2d
|
|
BLAKE2b-256 checksum How to use checksums |
f226d65132e7a43f0a4fdd9b90d27ba572c2c3f8a145ac41baa3fa375aba60d3
|
| 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