Skip to content

tylersuehr7/django-autowired

Repository files navigation

django-autowired

Spring Boot-style @injectable autowiring for Python. Any DI backend. Zero manual wiring.

PyPI version Python versions License CI

Why

In large Django / FastAPI / Flask codebases, hand-rolling injector.Module subclasses with binder.bind(IRepository, to=SqlRepository) calls does not scale. It's boilerplate, it's easy to forget, and every new service becomes a coordination problem.

django-autowired brings the Spring Boot ergonomic to Python: decorate a class with @injectable, point an AppConfig (or FastAPI lifespan, or Flask extension) at the packages to scan, and the rest happens at boot.

Features

Zero wiring No Module subclasses, no binder.bind() calls. Just @injectable.
Backend-agnostic Works with injector, lagom, wireup, or dishka. Swap with one line.
Framework-agnostic core Django, FastAPI, Flask, or plain Python — all first-class.
Fail loud at boot Duplicate bindings, missing backends, and unresolvable types raise typed errors.
Test-friendly Pytest fixtures, overrides, and a context manager ship with the library.
Thread-safe registry Concurrent registration is safe by construction.
Introspection python -m django_autowired inspect prints your DI graph as a table, tree, JSON, or Mermaid diagram.

Quickstart (Django)

# myapp/apps.py
from django_autowired.integrations.django import AutowiredAppConfig

class MyAppConfig(AutowiredAppConfig):
    name = "myapp"
    autowired_packages = ["myapp.services", "myapp.adapters"]

# myapp/services.py
from django_autowired import injectable

@injectable()
class GreetingService:
    def greet(self) -> str:
        return "hello"

# myapp/views.py
from django_autowired import container
from myapp.services import GreetingService

def index(request):
    svc = container.get(GreetingService)
    return HttpResponse(svc.greet())

That's it. No modules. No binder. No manual wiring.

Backend support

Backend Status Priority Notes
injector default Most complete. Thread scope supported.
lagom Auto-resolves concretes from type hints.
wireup Thread scope falls back to singleton.
dishka Rebuild-on-override semantics.

Install the backend you want:

pip install "django-autowired[injector]"   # default
pip install "django-autowired[lagom]"
pip install "django-autowired[wireup]"
pip install "django-autowired[dishka]"

Framework support

Framework Status Priority
Django priority
FastAPI
Flask
Plain Python

Installation

pip install django-autowired                       # core only (no backend)
pip install "django-autowired[injector]"           # default backend
pip install "django-autowired[django,injector]"    # Django + injector
pip install "django-autowired[fastapi,injector]"   # FastAPI + injector
pip install "django-autowired[flask,injector]"     # Flask + injector
pip install "django-autowired[dev]"                # everything + test/lint/docs

Or with uv:

uv add "django-autowired[django,injector]"

Documentation

Full docs at tylersuehr7.github.io/django-autowired.

License

MIT © 2026 Tyler R. Suehr

About

Spring Boot-style `@injectable` autowiring for Python. Any DI backend. Zero manual wiring.

Topics

Resources

License

Contributing

Stars

Watchers

Forks

Contributors