This project is a SaaS multi-tenant application using the specific combination of:
- Django (core framework)
- django-tenants (schema-based multi-tenancy)
- django-tenant-users (shared user pool with per-tenant permissions)
This project implements a schema-based multi-tenancy architecture, distinguishing between a "Public" context and a "Tenant" context.
- Public Schema (
public):- Acts as the landing page and marketing site.
- Used for global administration (managing tenants).
- Renders the "SaaS Platform" brand and links to the Blog or Tenant list.
- Tenant Schemas:
- Each customer (tenant) gets their own isolated schema (subdomain).
- Contains the actual business data (Projects, Tasks, Users).
- Displays a specific "Dashboard" with project/task counts.
This is the core functional component of the SaaS platform, designed for tenants to manage their work.
- Data Model:
- Project: A container for work with a unique key, name, and owner.
- Task: A unit of work linked to a Project with status, priority, and assignee.
- API Layer:
- Exposes a REST API using Django Rest Framework (DRF).
ProjectViewSetandTaskViewSethandle CRUD operations.- Optimized with
.select_relatedto prevent N+1 query issues.
- UI Layer:
- Uses standard Django Server-Side Rendering (SSR) with Bootstrap 5.
- Views enforce
TenantSchemaRequiredMixinandLoginRequiredMixinfor security.
The templates are dynamic based on the tenant context:
base.html: Features a smart Navbar(Sidebar) that changes links based on context (Public vs Tenant).core/index.html:- Public: Displays a welcome message and blog links.
- Tenant: Displays a Dashboard with statistics cards.
The application requires a PostgreSQL database. The database user must have CREATEDB privileges to create new schemas dynamically.
See database.txt for the specific SQL commands to configure the database user and permissions.
- Users are global (live in
publicschema) - Authentication is shared across all tenants
- Authorization (permissions) is per-tenant
- The
publicschema is the global (shared) namespace - Each tenant has its own PostgreSQL schema (e.g.,
demo) - The
connection.schema_namevariable indicates the current schema context
The Public Schema (lvh.me) acts as the management layer. It hosts the Landing Page, Sign Up Form, and the Global User Database. Crucially, the Tenant table (the registry of all customers) lives only in the Public Schema.
To set up the environment from scratch (e.g., for development), the system uses a seeding script (often wrapped in a management command like populate_db):
- Database Reset: Drop and recreate the application database to ensure a clean slate.
- Shared Migrations: Run
migrate_schemas --sharedto create tables in thepublicschema. - Public Tenant Creation: Initialize the
publictenant (domain:lvh.me). - Demo Tenant Provisioning: Iterate through a data file (e.g.,
tenants.json) to:- Create tenant owners (Users).
- Call
provision_tenantto create the schema and domain for each customer. - Link root admin users to new tenants for administrative access.
Global Superusers manage the system via the Admin Panel at http://lvh.me:8000/admin/.
- Provisioning Tenants: Standard "Add" buttons are disabled to prevent misconfiguration. Instead, use the custom "Provision Tenant" button on the Tenant List page. This ensures the User, Tenant, and Domain are created correctly in a single transaction.
- User Management: Users are global. Deleting a user from the Public Admin removes them from all tenants (
delete_user_globally). - Impersonation: Superusers can "Login as" any user to troubleshoot issues within specific tenant contexts.
- User visits lvh.me:8000/accounts/signup/ (Public Schema).
- User creates account: Added to tenants_user (Global) and linked to Public Tenant (so they can view the onboarding page).
- User fills Onboarding Form:
- App creates new Tenant (e.g., mycompany).
- App creates new Domain (mycompany.lvh.me).
- App switches context to mycompany schema -> Creates "First Project".
- Redirect: User is sent to mycompany.lvh.me:8000/ to start working.