API Reference¶
Full reference for the public API. For narrative documentation and examples, see the quickstart and how-to guides.
django_stratagem.registry¶
Registry¶
class Registry(Generic[TInterface], metaclass=RegistryMeta)
Base class to define and manage registries of Interface implementations.
Class Attributes:
implementations_module: str- Name of the module to autodiscover for implementations.implementations: dict[str, ImplementationMeta]- Map of slug to implementation metadata. Populated automatically.choices_fields: list[tuple[str, type[Model]]]- List of (field_name, model_class) tuples for fields whose choices should be updated from this registry.label_attribute: str | None- Optional attribute name to use for display labels instead of the class name.interface_class: type[TInterface] | None- Optional interface class to validate implementations against.
Class Methods:
register(implementation)- Register an implementation class. Calls hooks and emitsimplementation_registeredsignal.unregister(slug)- Unregister by slug. Calls hooks and emitsimplementation_unregisteredsignal. RaisesImplementationNotFoundif not found.discover_implementations()- Autodiscover and load implementations fromimplementations_moduleand plugins.get(*, slug=None, fully_qualified_name=None) -> TInterface- Instantiate and return an implementation by slug or fully qualified name (FQN). RaisesImplementationNotFound.get_or_default(*, slug=None, fully_qualified_name=None, default=None) -> TInterface | None- Likeget()but returnsNoneor a default on failure.get_class(*, slug=None, fully_qualified_name=None) -> type[TInterface]- Return the class without instantiating.get_implementation_class(slug) -> type[TInterface]- Get implementation class by slug.get_implementation_meta(slug) -> ImplementationMeta- Get full metadata for an implementation.get_choices() -> list[tuple[str, str]]- Return cached (slug, label) pairs sorted by priority.get_display_name(implementation) -> str- Human-readable label for an implementation.get_items() -> list[tuple[str, type[TInterface]]]- Cached list of (slug, class) pairs.get_available_implementations(context=None) -> dict[str, type[TInterface]]- Implementations available in context.get_choices_for_context(context=None) -> list[tuple[str, str]]- Choices filtered by context.get_for_context(context=None, *, slug=None, fully_qualified_name=None, fallback=None) -> TInterface- Get implementation with context check and fallback.is_valid(value) -> bool- Check if value (string, class, or instance) is a registered implementation.contains(item) -> bool- Alias foris_valid().iter_implementations()- Iterator over implementation metadata dicts.count_implementations() -> int- Number of implementations.choices_field(*args, **kwargs) -> AbstractRegistryField- Factory forRegistryClassFieldtied to this registry.instance_field(*args, **kwargs) -> AbstractRegistryField- Factory forRegistryFieldtied to this registry.clear_cache()- Evict this registry’s cache entries.clear_all_cache()- Static method. Evict cache for all registries.check_health() -> dict[str, object]- Basic health metrics (count,last_updated).get_cache_key(suffix) -> str- Construct cache key string.
Extension Hooks:
Override these classmethods to customize the registration lifecycle. See Extension Hooks and Customization Points for examples and patterns.
validate_implementation(implementation)- Called byregister()before storage. Raise to reject. Default checks slug and interface subclass.build_implementation_meta(implementation) -> dict[str, Any]- Called byregister()after validation. Returns metadata dict. Default returns{klass, description, icon, priority}. Override to add custom keys.on_register(slug, implementation, meta)- Called after storage and cache clear, before signal. Default: no-op.on_unregister(slug, meta)- Called after removal and cache clear, before signal. Default: no-op. Receives the popped metadata dict.
Metaclass Behavior (RegistryMeta):
"slug" in MyRegistry- Check if slug is valid viais_valid().for impl in MyRegistry- Iterate over implementation classes.len(MyRegistry)- Number of implementations.
HierarchicalRegistry¶
class HierarchicalRegistry(Registry)
Registry that supports parent-child relationships.
Additional Class Attributes:
parent_registry: type[Registry] | None- The parent registry class.parent_slugs: list[str] | None- Restrict to specific parent slugs.
Additional Class Methods:
get_parent_registry() -> type[Registry] | Noneget_children_for_parent(parent_slug, context=None) -> dict[str, type[Interface]]get_choices_for_parent(parent_slug, context=None) -> list[tuple[str, str]]validate_parent_child_relationship(parent_slug, child_slug) -> boolget_hierarchy_map() -> dict[str, list[str]]- Cached map of parent slugs to child slugs.
RegistryRelationship¶
class RegistryRelationship
Manages relationships between parent and child registries.
Class Methods:
register_child(parent_registry, child_registry)- Register a parent-child relationship.get_children_registries(parent_registry) -> list[type[Registry]]get_all_descendants(registry) -> list[type[Registry]]- Recursive.clear_relationships()- Clear all relationships.
ImplementationMeta¶
class ImplementationMeta(TypedDict)
klass: type[Any] | None- The implementation class.description: stricon: strpriority: int
Functions¶
discover_registries()- Re-run auto-discovery for all registries, clearing caches first. Called during app startup; you rarely need to call this directly.update_choices_fields()- Set model fields’ choices from each registry.register(registry_cls) -> Callable- Decorator for explicit registration.
Module-Level¶
django_stratagem_registry: list[type[Registry]]- Global list of all registry classes.
django_stratagem.interfaces¶
Interface¶
class Interface
Base class for implementation interfaces. Auto-registers subclasses that have both registry and slug set.
Class Attributes:
slug: str- Unique identifier within the registry.registry: type[Registry] | None- Registry to register with.description: str- Human-readable description. Default:"".icon: str- Icon URL or identifier. Default:"".priority: int- Sort order (lower = higher priority). Default:0.
HierarchicalInterface¶
class HierarchicalInterface(Interface)
Additional Class Attributes:
parent_slug: str | None- Single parent slug requirement.parent_slugs: list[str] | None- Multiple parent slug requirements.
Class Methods:
is_valid_for_parent(parent_slug) -> bool
ConditionalInterface¶
class ConditionalInterface(Interface)
Additional Class Attributes:
condition: Condition | None- Condition for availability.
Class Methods:
is_available(context=None) -> bool
django_stratagem.conditions¶
Condition¶
class Condition(ABC)
Abstract base class for conditions. Subclass this and implement is_met(context) to create custom conditions.
is_met(context: dict) -> bool- Abstract. Check if condition is met.explain() -> str- Human-readable description.check_with_details(context) -> tuple[bool, str]- Result with explanation.__and__(other) -> AllConditions__or__(other) -> AnyCondition__invert__() -> NotCondition
CompoundCondition¶
class CompoundCondition(Condition)
Base class for compound conditions.
__init__(conditions: list[Condition])
AllConditions¶
class AllConditions(CompoundCondition)
All conditions must be met (AND logic).
AnyCondition¶
class AnyCondition(CompoundCondition)
At least one condition must be met (OR logic).
NotCondition¶
class NotCondition(Condition)
Negates a condition.
__init__(condition: Condition)
FeatureFlagCondition¶
class FeatureFlagCondition(Condition)
__init__(flag_name: str)- Checkssettings.FEATURE_FLAGS[flag_name]orwaffle.flag_is_active().
PermissionCondition¶
class PermissionCondition(Condition)
__init__(permission: str)- Checksuser.has_perm(permission)fromcontext["user"].
SettingCondition¶
class SettingCondition(Condition)
__init__(setting_name: str, expected_value: Any)- Checksgetattr(settings, setting_name) == expected_value.
CallableCondition¶
class CallableCondition(Condition)
__init__(check_func: Callable[[dict], bool])- Wraps any callable.
AuthenticatedCondition¶
class AuthenticatedCondition(Condition)
No arguments. Checks context["user"].is_authenticated. Returns False if user is missing or lacks the attribute.
StaffCondition¶
class StaffCondition(Condition)
No arguments. Checks context["user"].is_staff. Returns False if user is missing or lacks the attribute.
SuperuserCondition¶
class SuperuserCondition(Condition)
No arguments. Checks context["user"].is_superuser. Returns False if user is missing or lacks the attribute.
GroupCondition¶
class GroupCondition(Condition)
__init__(group_name: str)- Checkscontext["user"].groups.filter(name=group_name).exists(). ReturnsFalseif user is missing or has nogroupsattribute.
TimeWindowCondition¶
class TimeWindowCondition(Condition)
__init__(start_time: datetime.time, end_time: datetime.time, days: list[int] | None = None)- Checks if current local time is within the window. Handles overnight windows (start > end).daysuses Python weekday convention (0=Monday, 6=Sunday);Nonemeans every day.
DateRangeCondition¶
class DateRangeCondition(Condition)
__init__(start_date: datetime.date | None = None, end_date: datetime.date | None = None)- Checks if current local date is within the range (inclusive).Nonemeans no bound on that side.
EnvironmentCondition¶
class EnvironmentCondition(Condition)
__init__(env_var: str, expected_value: str | None = None)- Ifexpected_valueisNone, checks that the env var exists and is non-empty. Otherwise checks for exact string match.
django_stratagem.fields¶
AbstractRegistryField¶
class AbstractRegistryField(Field)
Base class for all registry model fields. Internal type: CharField.
__init__(*args, import_error=None, max_length=200, registry=None, **kwargs)contribute_to_class(cls, name, private_only=False)- Sets up the descriptor.formfield(...)- ReturnsRegistryFormFieldorRegistryMultipleChoiceFormField. Passshow_description=Trueto useRegistryDescriptionWidgetautomatically.choices- Property that returnsregistry.get_choices().
RegistryClassField¶
class RegistryClassField(AbstractRegistryField)
Returns the implementation class on access. Descriptor: RegistryClassFieldDescriptor.
RegistryField¶
class RegistryField(RegistryClassField)
Returns an implementation instance on access. Descriptor: RegistryFieldDescriptor.
__init__(*args, factory=lambda klass, obj: klass(), **kwargs)
MultipleRegistryClassField¶
class MultipleRegistryClassField(AbstractRegistryField)
Stores comma-separated FQNs. Returns list of classes. Descriptor: MultipleRegistryClassFieldDescriptor.
MultipleRegistryField¶
class MultipleRegistryField(MultipleRegistryClassField)
Returns list of instances. Descriptor: MultipleRegistryFieldDescriptor.
__init__(*args, factory=lambda klass, obj: klass(), **kwargs)
HierarchicalRegistryField¶
class HierarchicalRegistryField(RegistryField)
__init__(*args, parent_field=None, **kwargs)get_parent_value(obj) -> str | Nonevalidate(value, model_instance)- Validates parent-child relationship.
MultipleHierarchicalRegistryField¶
class MultipleHierarchicalRegistryField(MultipleRegistryField)
__init__(*args, parent_field=None, **kwargs)
Descriptors¶
RegistryClassFieldDescriptor-__get__returns class,__set__accepts class/slug/FQN.RegistryFieldDescriptor-__get__returns instance viafactory(klass, obj).MultipleRegistryClassFieldDescriptor-__get__returns list of classes.MultipleRegistryFieldDescriptor-__get__returns list of instances.HierarchicalRegistryFieldDescriptor- Adds parent-child validation on__set__.
django_stratagem.forms¶
RegistryFormField¶
class RegistryFormField(ChoiceField)
__init__(*args, registry, empty_value="", **kwargs)clean(value) -> type- Returns the implementation class.
RegistryMultipleChoiceFormField¶
class RegistryMultipleChoiceFormField(TypedMultipleChoiceField)
__init__(*args, registry, **kwargs)coerce(value) -> type | None
ContextAwareRegistryFormField¶
class ContextAwareRegistryFormField(RegistryFormField)
__init__(*args, context=None, **kwargs)set_context(context)- Update context and refresh choices.
HierarchicalRegistryFormField¶
class HierarchicalRegistryFormField(ContextAwareRegistryFormField)
__init__(*args, parent_field=None, parent_value=None, **kwargs)set_parent_value(parent_value)- Update parent and refresh choices.
RegistryContextMixin¶
class RegistryContextMixin(BaseForm)
__init__(*args, registry_context=None, **kwargs)- Passes context to allContextAwareRegistryFormFieldfields.
HierarchicalFormMixin¶
class HierarchicalFormMixin(BaseForm)
Sets up parent-child relationships and validates them in clean().
django_stratagem.widgets¶
RegistryWidget¶
class RegistryWidget(forms.Select)
__init__(attrs=None, choices=(), registry=None)Adds
title,data-description,data-icon,data-priorityattributes to options.
RegistryDescriptionWidget¶
class RegistryDescriptionWidget(RegistryWidget)
__init__(attrs=None, choices=(), registry=None, description_attrs=None)Renders the select element plus a
<div>container that displays the selected option’s description.description_attrs- dict of HTML attributes applied to the container (e.g.{"class": "alert alert-info"}).Includes
Media.jsreferencingdjango_stratagem/js/registry_description.js.The container carries
aria-live="polite"for screen readers.Works with HTMX - reinitialises after
htmx:afterSettle.
HierarchicalRegistryWidget¶
class HierarchicalRegistryWidget(forms.Select)
__init__(attrs=None, choices=(), parent_field=None)Adds
data-parent-fieldanddata-hierarchicalattributes.
django_stratagem.admin¶
RegistryFieldListFilter¶
class RegistryFieldListFilter(ChoicesFieldListFilter)
Context-aware admin list filter for registry fields. Auto-registered for all AbstractRegistryField instances.
ContextAwareRegistryAdmin¶
class ContextAwareRegistryAdmin(admin.ModelAdmin)
Injects request context into registry form fields.
RegistryListMixin¶
class RegistryListMixin(admin.ModelAdmin)
Adds registry fields to list_display and list_filter.
HierarchicalRegistryAdmin¶
class HierarchicalRegistryAdmin(ContextAwareRegistryAdmin)
Adds hierarchical field support with data attributes for JavaScript.
DjangoStratagemAdminSite¶
class DjangoStratagemAdminSite(AdminSite)
Admin site with registry dashboard at /admin/registry-dashboard/.
EnhancedDjangoStratagemAdminSite¶
class EnhancedDjangoStratagemAdminSite(DjangoStratagemAdminSite)
Enhanced dashboard with hierarchy visualization at /admin/enhanced-registry-dashboard/.
django_stratagem.plugins¶
PluginProtocol¶
class PluginProtocol(Protocol)
name: strversion: strregistry: strimplementations: list[str]enabled: bool
PluginInfo¶
@dataclass
class PluginInfo
Concrete implementation of PluginProtocol.
PluginLoader¶
class PluginLoader
ENTRY_POINT_GROUP = "django_stratagem.plugins"discover_plugins() -> list[PluginProtocol]load_plugin_implementations(registry_cls)- Load plugin implementations for a specific registry.
django_stratagem.drf.serializers¶
DrfRegistryField¶
class DrfRegistryField(serializers.ChoiceField)
__init__(registry, representation="slug", **kwargs)to_representation(value) -> str- Slug or FQN.to_internal_value(data) -> type- Returns the implementation class.
DrfMultipleRegistryField¶
class DrfMultipleRegistryField(serializers.MultipleChoiceField)
__init__(registry, **kwargs)to_representation(value) -> list[str]to_internal_value(data) -> list[type]
Aliases¶
DrfStrategyField = DrfRegistryFieldDrfMultipleStrategyField = DrfMultipleRegistryField
django_stratagem.drf.views¶
RegistryChoicesAPIView¶
class RegistryChoicesAPIView(View)
JSON endpoint: GET ?registry=Name&parent=slug
RegistryHierarchyAPIView¶
class RegistryHierarchyAPIView(View)
JSON endpoint: GET returns hierarchy maps for all hierarchical registries.
django_stratagem.signals¶
implementation_registered = Signal() # kwargs: registry, implementation
implementation_unregistered = Signal() # kwargs: registry, slug
registry_reloaded = Signal() # kwargs: registry
django_stratagem.exceptions¶
ImplementationNotFound¶
class ImplementationNotFound(KeyError)
RegistryNameError¶
class RegistryNameError(ValueError)
__init__(name: str, message: str | None = None)
RegistryClassError¶
class RegistryClassError(ValueError)
__init__(name: str, message: str | None = None)
RegistryImportError¶
class RegistryImportError(ImportError)
RegistryAttributeError¶
class RegistryAttributeError(AttributeError)
__init__(name: str, module_path: str, class_str: str, message: str | None = None)
django_stratagem.validators¶
ClassnameValidator¶
@deconstructible
class ClassnameValidator(BaseValidator)
Validates that a value is a valid, importable class name.
RegistryValidator¶
@deconstructible
class RegistryValidator(ClassnameValidator)
__init__(registry: type[Registry], message: str | None = None)
Validates that a value is registered in a specific registry. Supports single values and lists.
django_stratagem.utils¶
is_running_migrations() -> bool¶
Check if Django is running migrate or makemigrations. Result is cached for the process lifetime.
import_by_name(name: str) -> Any¶
Dynamically load and cache a class by its dotted path. Raises RegistryNameError if no dot in name. Raises RegistryAttributeError if attribute not found.
get_class(value: str | type | None) -> Any¶
Get a class from a string reference, return a class as-is, or return the type of an instance.
get_fully_qualified_name(obj: Any) -> str¶
Returns the fully qualified name (module.ClassName) of a class or instance.
get_display_string(klass: type, display_attribute: str | None = None) -> str¶
Get a human-readable display string for a class, using display_attribute if provided, otherwise converting CamelCase to Title Case.
camel_to_title(text: str) -> str¶
Convert CamelCase to Title Case. Handles consecutive capitals (e.g., HTTPServer -> HTTP Server).
stringify(values: Sequence[Any]) -> str¶
Convert a sequence to a sorted, comma-separated string of FQNs.
get_attr(obj, attr, default=None)¶
Recursively get an attribute using dot notation.
store_raw_name(obj, field_name, original)¶
Store the FQN or None after an import attempt.
django_stratagem.app_settings¶
PROJECT_STRATAGEM = getattr(settings, "DJANGO_STRATAGEM", {})
PROJECT_SKIP_DURING_MIGRATIONS: bool # Default: True
PROJECT_CACHE_TIMEOUT: int # Default: 300 (5 minutes)
django_stratagem.lookups¶
Custom lookups registered for RegistryField, RegistryClassField, MultipleRegistryClassField, and MultipleRegistryField:
RegistryFieldExact-exactlookup with FQN conversion.RegistryFieldIExact-iexactlookup.RegistryFieldContains-containslookup.RegistryFieldIContains-icontainslookup.RegistryFieldIn-inlookup that converts classes/instances to FQN strings.
django_stratagem.checks¶
check_registries(app_configs, **kwargs)¶
System check function registered under tag django_stratagem.
ID |
Level |
Condition |
|---|---|---|
|
Error |
Registry has empty/invalid |
|
Error |
Model field |
|
Warning |
Hierarchical registry parent not in global registry |
|
Warning |
Field registry not in global registry |
Diagnostics: stratagem_doctor¶
A read-only management command that runs a health check across every registry:
python manage.py stratagem_doctor
python manage.py stratagem_doctor --format json
It reports each registry’s implementations, flags registries with no
implementations and slugs whose implementation class failed to load
(klass is None), and folds in the django_stratagem system checks. It
exits non-zero when any error-level finding exists, so it can gate CI.
Debugging availability¶
Registry.explain_availability(slug, context=None) returns
(available, reason) for a registered slug, surfacing the same condition
reasoning the admin inspector shows - from the shell, with no UI:
>>> NotificationRegistry.explain_availability("premium", {"user": request.user})
(False, "PermissionCondition(app.premium) -> failed")
An unknown slug raises ImplementationNotFound with the available slugs and a
“did you mean?” suggestion.
Registry.describe() returns a multi-line summary of a registry for
interactive use:
>>> print(NotificationRegistry.describe())
NotificationRegistry - 3 implementation(s)
email: Email (priority 10)
sms: SMS (priority 20)
push: Push (priority 30)
Registry classes and Interface instances also have legible repr() output
(for example <NotificationRegistry: 3 implementation(s) [email, sms, push]>).