Gestion du Contexte Tenant¶
Les context variables (contextvars) propagent le tenant, l’utilisateur
et la membership courants à travers toute la pile d’appels, y compris les
coroutines ASGI.
Module : src.share_kernel.infrastructure.context
Accesseurs¶
Retourne le tenant courant, ou
None.
Retourne l’utilisateur courant, ou
None.
Retourne la membership courante, ou
None.
Retourne l’ID de corrélation courant, ou
None.
Context Manager : tenant_context¶
Context manager pour basculer vers un tenant spécifique.
Accepte un objet
Tenant, unUUID, ou un slug (string).Active le backend d’isolation et configure les contextvars pour la durée du bloc, puis restaure l’état précédent.
- Parameters:
tenant_or_id – Tenant, UUID, ou slug
user – Utilisateur optionnel à associer
membership – Membership optionnelle à associer
- Yields:
L’instance Tenant résolue
from src.share_kernel.infrastructure.context import tenant_context # Par objet Tenant with tenant_context(my_tenant) as tenant: products = Product.objects.all() # Scopé au tenant # Par slug with tenant_context("acme-corp"): do_something() # Par UUID with tenant_context(uuid.UUID("...")) as tenant: orders = Order.objects.all() # Avec utilisateur et membership with tenant_context(tenant, user=admin_user, membership=admin_membership): # PIP peut résoudre les attributs de l'utilisateur policy_check()
Context Manager : async_tenant_context¶
Version asynchrone de
tenant_context()pour les vues ASGI.async with async_tenant_context("acme-corp") as tenant: products = await Product.objects.all().aall()
Context Manager : cross_tenant_context¶
Désactive temporairement l’isolation tenant pour les opérations cross-tenant (admin, reporting, migrations de données).
Désactive le backend d’isolation
Suspend RLS si applicable
Efface les contextvars tenant/user/membership
Active le flag
cross_tenant_mode_var
Prérequis PostgreSQL (RLS) : le rôle DB doit avoir
BYPASSRLS.from src.share_kernel.infrastructure.context import cross_tenant_context from django.db.models import Sum with cross_tenant_context(): # Pas de filtrage tenant — requêtes sur tous les tenants all_orders = Order.objects.all() total = all_orders.aggregate(Sum("amount")) print(f"Revenu total multi-tenant: {total}")
Warning
Utiliser avec précaution. Le code dans ce bloc a accès à toutes les données de tous les tenants.
Diagramme de flux¶
TenantMiddleware
│
├── Résout le tenant (JWT/Header)
├── current_tenant_var.set(tenant)
├── current_user_var.set(user)
├── current_membership_var.set(membership)
├── correlation_id_var.set(uuid4())
├── backend.activate_tenant(tenant, connection)
│
▼
Traitement de la requête
│
├── TenantSpecificManager.get_queryset()
│ └── Lit current_tenant_var pour filtrer
│
├── PIP.resolve_attributes()
│ └── Lit current_user_var, current_membership_var
│
├── TenantContextFilter (logging)
│ └── Lit tenant_id, user_id, correlation_id
│
└── tenant_cache_key()
└── Lit current_tenant_var pour préfixer
│
▼
Fin de requête
│
├── backend.deactivate_tenant(connection)
└── Reset de tous les contextvars