Skip to main content

Registry-based plugin architecture for Django applications

Project description

django-stratagem

![Latest on Django Packages](https://img.shields.io/badge/PyPI-{{ package.slug}}-tags-8c3c26.svg)

Many Django projects reach a point where you want to make the system configurable and need a some of the app's behavior to be swappable. For instance, if you need to support multiple payment processors and each merchant picks one. Maybe you offer several export formats and users choose CSV, XLSX, or PDF at download time. Maybe different customers get different notification channels depending on their plan.

The usual approach is a mess of nested if/elif chains, settings flags, or one-off plugin systems that each work a little differently. django-stratagem replaces all of those with a single pattern: you write each option as a small Python class, and the library auto-discovers it at startup, wires up model fields, populates form and admin dropdowns, and optionally exposes it through DRF.

How it helps the developer:

  • Add a new option by creating one class in one file. No manual wiring, no migrations.
  • Store a user's or tenant's selection in the database with a model field that understands your registry.
  • Get dropdowns in forms and the admin automatically - choices stay in sync as you add or remove options.
  • Control which options are available to which users using permissions, feature flags, or custom rules.
  • Third-party packages can contribute their own options through a plugin entry point.

What this gives your end users:

  • Admins see a clean dropdown of available options instead of typing class paths or magic strings.
  • Options can be enabled, disabled, or restricted per user, role, or tenant without code changes.
  • Deploying a new class is enough - no migration needed.

Example use cases

  • Notification channels - email, SMS, push, Slack, webhook - let admins pick which channels are active. (Getting started)
  • Payment gateways - Stripe, PayPal, Braintree - store the chosen gateway per merchant in a model field and swap it at runtime.
  • Export/import formats - CSV, Excel, PDF, JSON - register each format as an option, then offer them as choices in a form or API endpoint.
  • Authentication backends - LDAP, SAML, OAuth providers - enable or disable per-tenant with conditional availability tied to feature flags or permissions.
  • Pricing / discount strategies - percentage off, fixed amount, buy-one-get-one - attach the active strategy to a model and let business users pick it in the admin.
  • Report generators - sales summary, inventory audit, user activity - each report type is a class, and adding a new report is just adding a new module.

Installation

pip install django-stratagem

Add to INSTALLED_APPS:

INSTALLED_APPS = [
    "django_stratagem",
    # ...
]

Quickstart

1. Define a Registry and Interface

# 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

# 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.

3. Use in Models

# myapp/models.py
from django.db import models
from myapp.registry import NotificationRegistry

class NotificationConfig(models.Model):
    # Stores a reference to the implementation class
    strategy = NotificationRegistry.choices_field()

    # Or store an instance (instantiated on access)
    # strategy = NotificationRegistry.instance_field()

4. Use in Code

from myapp.registry import NotificationRegistry

# Get all registered implementations
for impl_class in NotificationRegistry:
    print(impl_class.slug)

# Get by slug
impl = NotificationRegistry.get(slug="email")
impl.send("Hello!", "user@example.com")

# Get 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
choices = NotificationRegistry.get_choices()
# [("email", "Email Notification"), ("sms", "SMS Notification")]

Features

Conditional Availability

Use conditions to control when implementations are available:

from django_stratagem import ConditionalInterface, PermissionCondition

class AdminNotification(ConditionalInterface):
    registry = NotificationRegistry
    slug = "admin_only"
    condition = PermissionCondition("myapp.admin_notifications")

    def send(self, message, recipient):
        ...

Built-in conditions: FeatureFlagCondition, PermissionCondition, SettingCondition, CallableCondition, and several more. Conditions support & (AND), | (OR), and ~ (NOT) operators.

Hierarchical Registries

Define parent-child relationships between registries for advanced needs:

from django_stratagem import HierarchicalRegistry, HierarchicalInterface

class CategoryRegistry(Registry):
    implementations_module = "categories"

class SubcategoryRegistry(HierarchicalRegistry):
    implementations_module = "subcategories"
    parent_registry = CategoryRegistry

class MySubcategory(HierarchicalInterface):
    registry = SubcategoryRegistry
    slug = "sub_a"
    parent_slug = "category_a"  # Only valid under category_a

Model Fields

Field Description
RegistryClassField Stores class reference, returns class on access
RegistryField Stores class reference, returns instance on access
MultipleRegistryClassField Comma-separated classes
MultipleRegistryField Comma-separated instances
HierarchicalRegistryField With parent field dependency

Django Admin

from django.contrib import admin
from django_stratagem.admin import ContextAwareRegistryAdmin

@admin.register(MyModel)
class MyModelAdmin(ContextAwareRegistryAdmin):
    pass

DRF Integration

Install with DRF support:

pip install django-stratagem[drf]
from django_stratagem.drf.serializers import DrfRegistryField

class MySerializer(serializers.Serializer):
    strategy = DrfRegistryField(registry=NotificationRegistry)

Template Tags

{% load stratagem %}

{% get_implementations my_registry as implementations %}
{% for slug, impl in implementations.items %}
    {{ impl|display_name }} - {{ impl|registry_icon }}
{% endfor %}

Plugin System

External packages can register implementations via entry points:

# In the plugin's pyproject.toml
[project.entry-points."django_stratagem.plugins"]
my_plugin = "my_plugin.stratagem_plugin"

Management Commands

# List all registries and implementations
python manage.py list_registries
python manage.py list_registries --format json

# Clear registry caches
python manage.py clear_registries_cache

# Re-initialize registries
python manage.py initialize_registries

Configuration

# 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
}

License

MIT

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

django_stratagem-2026.2.1b0.tar.gz (96.4 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

django_stratagem-2026.2.1b0-py3-none-any.whl (46.6 kB view details)

Uploaded Python 3

File details

Details for the file django_stratagem-2026.2.1b0.tar.gz.

File metadata

  • Download URL: django_stratagem-2026.2.1b0.tar.gz
  • Upload date:
  • Size: 96.4 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.9.18 {"installer":{"name":"uv","version":"0.9.18","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

File hashes

Hashes for django_stratagem-2026.2.1b0.tar.gz
Algorithm Hash digest
SHA256 97ddca7fa9079fa6a3af6078fcd172f4d99e5711a978b72cda8aa3f777b18f9a
MD5 57b61f3260b5ff4d2a3f3da46ae4b458
BLAKE2b-256 85ef2b99bb9dd35ba8ad3bad8aae4aaf97112c74a5decae6164b70c10892ae77

See more details on using hashes here.

File details

Details for the file django_stratagem-2026.2.1b0-py3-none-any.whl.

File metadata

  • Download URL: django_stratagem-2026.2.1b0-py3-none-any.whl
  • Upload date:
  • Size: 46.6 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.9.18 {"installer":{"name":"uv","version":"0.9.18","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

File hashes

Hashes for django_stratagem-2026.2.1b0-py3-none-any.whl
Algorithm Hash digest
SHA256 e27bc30eb91435b73fcc303cfa9923ad808b8626f45f89be4ca099735673ee79
MD5 dcd30d5424f9483ba3b2c9b194204711
BLAKE2b-256 6153497ba8aa9394a5ac96b42b3d27d3175a8c0e200e9d6d47aa40d884d80b65

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page