Skip to content

Background tasks

The middleware never runs for Celery, RQ, cron jobs or management commands, so they start on the shared database. django_db_tenants.tasks gives them a tenant.

One tenant per call: @with_tenant

The decorated function receives the tenant (a slug or a Client) as the tenant keyword or as its first positional argument; its body runs inside tenant_context(tenant).

from celery import shared_task
from django_db_tenants.tasks import with_tenant


@shared_task
@with_tenant
def send_invoices(tenant):
    Invoice.objects.filter(sent=False)  # the tenant's database


send_invoices.delay(tenant="acme")  # pass the slug: it serialises

Put @with_tenant under the task decorator, so the context is set when the worker runs the function. A None tenant runs the body on the shared database.

Every tenant: for_each_tenant

from django_db_tenants.tasks import for_each_tenant

counts = for_each_tenant(lambda client: Invoice.objects.count())
# {"acme": 12, "globex": 3}

func(client) runs inside each client's context; the result is a dict keyed by slug. Only active clients by default; only_active=False includes the others. Clients run one after the other, and an exception stops the loop.

The demo's notes/tasks.py and notes_report command use both helpers.

django_db_tenants.tasks

Helpers for background work (Celery, RQ, cron) which never passes through the middleware.

with_tenant

with_tenant(func)

Decorator: the wrapped callable must receive tenant (slug or Client) as a kwarg or first positional argument; the body runs inside that tenant's context.

@shared_task
@with_tenant
def send_invoices(tenant):
    Invoice.objects.filter(sent=False)   # routed to the tenant DB

for_each_tenant

for_each_tenant(func, *, only_active=True)

Run func(client) for every tenant, inside each tenant's context.