Getting Started¶
Installation¶
Install django-stratagem with pip:
pip install django-stratagem
Add django_stratagem to your INSTALLED_APPS:
INSTALLED_APPS = [
"django_stratagem",
# ...
]
Requirements¶
Python 3.10+
Django 4.2+
For DRF integration, install the optional extra:
pip install django-stratagem[drf]
Scaffold a registry¶
Generate a registry, interface, and implementations module in one step:
python manage.py startregistry Notification --app myapp
This creates myapp/registry.py (a NotificationRegistry and
NotificationInterface) and myapp/notification_implementations.py (a
DefaultNotification sample). Pass --module gateways to name the
implementations file yourself, and --force to overwrite existing files.
Edit the generated slug values and logic, and the registry auto-discovers
your implementations on the next startup.
Core Concepts¶
The main pieces are a Registry (holds a set of implementations), an Interface (the base class implementations extend), implementations (concrete classes with a slug), and auto-discovery (imports them at startup). You define a registry by subclassing Registry and setting implementations_module to name the module where implementations live. Each implementation subclasses your Interface, sets a unique slug, and optionally provides description, icon, and priority. On app startup, django-stratagem calls autodiscover_modules() for each registry’s implementations_module, importing and registering everything automatically.
classDiagram
class Registry {
implementations_module = "notifications"
get(slug)
get_choices()
}
class Interface {
registry = NotificationRegistry
send(message, recipient)*
}
class EmailNotification {
slug = "email"
send(message, recipient)
}
class SMSNotification {
slug = "sms"
send(message, recipient)
}
Registry "1" *-- "*" Interface : holds
Interface <|-- EmailNotification
Interface <|-- SMSNotification
Your First Registry¶
1. Define the Registry and Interface¶
Create a registry module in your app:
# myapp/registry.py
from django_stratagem import Registry, Interface
class NotificationRegistry(Registry):
implementations_module = "notifications"
class NotificationInterface(Interface):
registry = NotificationRegistry
def send(self, message: str, recipient: str) -> bool:
raise NotImplementedError
2. Create Implementations¶
Create the implementations module matching implementations_module:
# myapp/notifications.py
from myapp.registry import NotificationInterface
class EmailNotification(NotificationInterface):
slug = "email"
description = "Send notifications via email"
priority = 10
def send(self, message, recipient):
# send email...
return True
class SMSNotification(NotificationInterface):
slug = "sms"
description = "Send notifications via SMS"
priority = 20
def send(self, message, recipient):
# send SMS...
return True
Implementations are auto-registered when their module is imported. django-stratagem discovers them automatically via autodiscover_modules("notifications") on app startup.
File Layout¶
myapp/
├── __init__.py
├── registry.py # Registry + Interface
├── notifications.py # Implementations (matches implementations_module)
├── models.py
└── ...
What happens at startup¶
When Django starts, django-stratagem finds your registries, imports the implementation modules, and populates model field choices - all before the first request is served.
flowchart LR
A[Django starts] --> B[Discover registries]
B --> C["Import each registry's implementations_module"]
C --> D[Classes register themselves via __init_subclass__]
D --> E[Model field choices are populated]
For the full startup lifecycle, including migration safety and plugin loading, see How Auto-Discovery Works.
Using the Registry¶
Once registered, you interact with implementations through the registry:
from myapp.registry import NotificationRegistry
# Iterate over all registered implementation classes
for impl_class in NotificationRegistry:
print(impl_class.slug)
# Get an instance by slug
impl = NotificationRegistry.get(slug="email")
impl.send("Hello!", "user@example.com")
# Get the class without instantiation
cls = NotificationRegistry.get_class(slug="email")
# Safe get with fallback
impl = NotificationRegistry.get_or_default(slug="nonexistent", default="email")
# Get choices for forms (list of (slug, label) tuples)
choices = NotificationRegistry.get_choices()
# [("email", "Email Notification"), ("sms", "SMS Notification")]
# Membership check
"email" in NotificationRegistry # True
# Count implementations
len(NotificationRegistry) # 2
Using in Models¶
django-stratagem provides model fields that store references to registry implementations in the database.
choices_field() - Store a Class Reference¶
from django.db import models
from myapp.registry import NotificationRegistry
class NotificationConfig(models.Model):
# Stores the class; accessing the field returns the class
strategy = NotificationRegistry.choices_field()
config = NotificationConfig()
config.strategy = EmailNotification # Set by class
config.strategy = "email" # Or by slug
config.save()
config.strategy # Returns the EmailNotification class
instance_field() - Store and Instantiate¶
class NotificationConfig(models.Model):
# Stores the class; accessing the field returns an instance
strategy = NotificationRegistry.instance_field()
config = NotificationConfig.objects.get(pk=1)
config.strategy.send("Hello!", "user@example.com") # Already an instance
See How to Use Model Fields for all field types and options.
Exposing Options to Users¶
The model fields above are enough to store a selection, but the real point is giving your users a way to pick one. django-stratagem plugs into forms, the admin, and DRF so the choices show up automatically.
Forms¶
Registry model fields produce form dropdowns by default. You can also use RegistryFormField directly:
from django_stratagem import RegistryFormField
class NotificationConfigForm(forms.Form):
strategy = RegistryFormField(registry=NotificationRegistry)
The user sees a <select> with your registered implementations as options. See How to Use Forms, Widgets, and the Admin for context-aware filtering and hierarchical fields.
Admin¶
One mixin gives you dropdowns and list filters with no extra work:
from django.contrib import admin
from django_stratagem.admin import ContextAwareRegistryAdmin
@admin.register(NotificationConfig)
class NotificationConfigAdmin(ContextAwareRegistryAdmin):
pass
This automatically filters choices based on the logged-in admin’s permissions when you’re using conditional availability. See How to Use Forms, Widgets, and the Admin for hierarchical admin support and dashboard views.
DRF¶
If you’re building an API, install the optional drf extra and use DrfRegistryField:
from rest_framework import serializers
from django_stratagem.drf.serializers import DrfRegistryField
class NotificationConfigSerializer(serializers.Serializer):
strategy = DrfRegistryField(registry=NotificationRegistry)
Accepts slugs as input, validates against the registry, and serializes back to slugs. See How to Use DRF Integration for multiple-choice fields and the built-in API views.
Configuration¶
Configure django-stratagem via the DJANGO_STRATAGEM dict in your Django settings:
# settings.py
DJANGO_STRATAGEM = {
"CACHE_TIMEOUT": 300, # Cache TTL in seconds (default: 300)
"SKIP_DURING_MIGRATIONS": True, # Skip registry ops during migrations (default: True)
"ENABLED_PLUGINS": None, # List of enabled plugin names, or None for all
"DISABLED_PLUGINS": [], # List of disabled plugin names
}