API Integration

django-tenant-options ships optional, fully-guarded API integration extras. The package core has no API dependencies - these extras live in django_tenant_options.contrib and are imported only when you opt in.

Installation

Install the extra that matches your stack:

# Django REST Framework only
pip install django-tenant-options[drf]

# DRF plus drf-spectacular OpenAPI schema
pip install django-tenant-options[spectacular]

# Experimental django-bolt adapter
pip install django-tenant-options[bolt]

Add rest_framework to your INSTALLED_APPS when using the DRF extra.

Serializers

Serializers are model-agnostic. Subclass and bind your concrete Option model, or use the factory:

from django_tenant_options.contrib.rest_framework.serializers import option_serializer_factory

from myapp.models import TaskPriorityOption

PriorityOptionSerializer = option_serializer_factory(TaskPriorityOption)

Or subclass explicitly:

from django_tenant_options.contrib.rest_framework.serializers import OptionSerializer

from myapp.models import TaskPriorityOption


class PriorityOptionSerializer(OptionSerializer):
    class Meta(OptionSerializer.Meta):
        model = TaskPriorityOption

There are matching SelectionSerializer and selection_serializer_factory.

ViewSets and resolving the tenant

The package never assumes how you resolve a tenant from a request. Subclass the base viewset, bind your model and serializer, and override get_tenant:

from django_tenant_options.contrib.rest_framework.views import BaseOptionViewSet

from myapp.models import TaskPriorityOption


class PriorityOptionViewSet(BaseOptionViewSet):
    option_model = TaskPriorityOption
    serializer_class = PriorityOptionSerializer

    def get_tenant(self):
        # Resolve however your app identifies tenants.
        return self.request.user.tenant

BaseOptionViewSet is read-only and provides:

  • list - all options available to the tenant.

  • selected (GET .../selected/) - only the tenant’s actively selected options.

BaseSelectionViewSet is read/write: listing returns the tenant’s active selections, creating sets the tenant automatically, and deleting performs a soft-delete.

Wiring URLs

from django.urls import include
from django.urls import path

from django_tenant_options.contrib.rest_framework.routers import build_router

from myapp.api import PriorityOptionViewSet

router = build_router("priorities", PriorityOptionViewSet)

urlpatterns = [
    path("api/", include(router.urls)),
]

OpenAPI schema (drf-spectacular)

Decorate views with maybe_extend_schema, which applies drf_spectacular.utils.extend_schema when drf-spectacular is installed and is a no-op otherwise - so your views never take a hard dependency:

from django_tenant_options.contrib.rest_framework.schema import maybe_extend_schema


class PriorityOptionViewSet(BaseOptionViewSet):
    @maybe_extend_schema(summary="List available priority options")
    def list(self, request, *args, **kwargs):
        return super().list(request, *args, **kwargs)

django-bolt (experimental)

A thin adapter registers GET endpoints for available and selected options on a django-bolt app object. It is fully guarded and intentionally small:

from django_tenant_options.contrib.bolt.app import register_option_routes

from myapp.models import TaskPriorityOption

register_option_routes(app, TaskPriorityOption, get_tenant=lambda request: request.tenant)