Repository pattern and dependency injection in Django

Backend Live Coding November 13, 2026 python

In Django the ORM is already a repository, so I do not wrap every query.

python
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
# ======================================================================
# Q: Repository pattern and dependency injection: do you need them in Django?
#
# In Django the ORM is already a repository, so I do not wrap every query. I do
# keep ports for external dependencies, because that is where the real value is.
# In FastAPI/SQLAlchemy the repository is more natural.
# Protocol gives structural typing: the fake does not need to inherit from
# anything, and mypy checks that it matches.
# ======================================================================

from typing import Protocol

class ErpClient(Protocol):
    def push_order(self, order_id: int, payload: dict) -> str: ...

class HttpErpClient:
    def __init__(self, session: httpx.Client, base_url: str):
        self._s, self._url = session, base_url
    def push_order(self, order_id, payload):
        r = self._s.post(f'{self._url}/orders', json=payload,
                         headers={'Idempotency-Key': f'order-{order_id}'}, timeout=10)
        r.raise_for_status()
        return r.json()['erp_ref']

class FakeErpClient:                          # for tests
    def __init__(self): self.sent = []
    def push_order(self, order_id, payload):
        self.sent.append((order_id, payload)); return 'ERP-1'

def sync_order(order: Order, erp: ErpClient) -> None:    # injected
    order.external_ref = erp.push_order(order.id, serialize(order))
    order.save(update_fields=['external_ref'])