Write a Custom Interface¶
Sometimes your data lives outside of Django or requires a bespoke persistence strategy. Implement a custom interface by subclassing InterfaceBase.
Step 1: Subclass InterfaceBase¶
from general_manager.interface.base_interface import InterfaceBase
from general_manager.manager import GeneralManager, Input
from typing import Generator
class ExternalReportInterface(InterfaceBase):
input_fields = {
"id": Input(int),
"year": Input(int),
}
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self._payload = self.fetch_report()
def fetch_report(self) -> dict[str, object]:
return external_api.get_report(self.identification["id"], self.identification["year"])
Step 2: Implement manager operations¶
Provide the methods your manager needs. For read-only data, implement filter, exclude, and all. For write capabilities, add create, update, and delete (the legacy deactivate name now simply proxies to delete).
class ExternalReportInterface(InterfaceBase):
...
@classmethod
def all(cls) -> Generator[dict[str, object], None, None]:
for payload in external_api.get_reports():
yield {"id": payload["id"], "year": payload["year"]}
def update(self, **kwargs):
external_api.update_report(self.identification["id"], **kwargs)
...
Step 3: Handle dependency tracking¶
Call DependencyTracker.track() when you fetch data so cache invalidation works:
from general_manager.cache.cache_tracker import DependencyTracker
def fetch_report(self) -> dict[str, object]:
DependencyTracker.track("ExternalReport", "fetch", str(self.identification))
return external_api.get_report(...)
Step 4: Wire up permissions¶
Attach a permission class like any other manager. Custom interfaces participate fully in permission checks and GraphQL type generation as long as they expose attribute metadata via get_attribute_types().
Step 5: Document limitations¶
External systems may not support transactions or historical lookups. Document these limitations in your API layer and adjust your GraphQL mutations to surface meaningful errors when remote calls fail.