Persistent Storage
A widget built with persist=True keeps its state across restarts, like a bar chart's totals or a keyed table's rows. That state is saved to the dashboard's storage.
import dachshund
from dachshund.storage import SQLiteStorage
from dachshund.widgets import BarChart
dachshund.init("My service", storage=SQLiteStorage("widget_state.db"))
with dachshund.row():
dachshund.add(BarChart("commands", x="name", persist=True))
storage only applies to the first call that touches the dashboard, so pass it to init() before adding widgets or including pages. Adding a widget with persist=True to a dashboard without storage raises an error.
To make your own widget persistable, see Persisting.
SQLiteStorage
The built-in backend, used above, keeps the state in a SQLite file of its own. SQLiteStorage(":memory:") keeps it in memory, for tests.
Writing your own
A backend is any class with these two methods:
async def save(self, key: str, value: dict) -> None: ...
async def load(self, key: str) -> dict | None: ...
save replaces the value under key, and load returns it, or None for an unknown key.
A backend on a synchronous client, such as an ORM, subclasses SyncStorage and writes _save and _load as plain methods. Each call runs in a thread, so the client must be safe to call from any thread.
import json
from dachshund.storage import SyncStorage
from sqlmodel import Field, Session, SQLModel, create_engine
class WidgetState(SQLModel, table=True):
key: str = Field(primary_key=True)
value: str
class SQLModelStorage(SyncStorage):
def __init__(self, url: str) -> None:
self.engine = create_engine(url)
SQLModel.metadata.create_all(self.engine)
def _save(self, key: str, value: dict) -> None:
with Session(self.engine) as session:
existing = session.get(WidgetState, key)
if existing is None:
session.add(WidgetState(key=key, value=json.dumps(value)))
else:
existing.value = json.dumps(value)
session.add(existing)
session.commit()
def _load(self, key: str) -> dict | None:
with Session(self.engine) as session:
row = session.get(WidgetState, key)
return json.loads(row.value) if row else None
dachshund.init("My service", storage=SQLModelStorage("sqlite:///widget_state.db"))
Raw events
The events feeding charts aren't saved to storage. They're kept in their own SQLite file, the dashboard's path, and trimmed over time.