dbzero.read_only()
dbzero-procommercial editionwith dbzero.read_only():
The read_only() context manager protects a code block that is known and expected to be non-mutating. It lets reads proceed, but rejects dbzero durable mutations immediately so accidental writes are caught close to the source.
Use it around validation, authorization checks, query handlers, report generation, dry-run logic, and other code paths that should only inspect dbzero state.
Parameters
This method takes no parameters.
Yields
The context manager does not yield a required value.
Side Effects
- Allows Reads: Reading fields, running queries, and fetching existing objects are allowed.
- Rejects Mutations: Memo field assignment, collection mutation, tag changes, index updates,
dbzero.touch(), and durable object creation raiseRuntimeError. - Protects the Current Context: The guard applies to dbzero durable mutations in the current execution context, not just to a single prefix.
- Nests Safely: Nested
read_only()blocks are allowed and restore the previous read-only state when they exit.
Errors
Mutating dbzero operations inside the block raise RuntimeError with a message that identifies read-only mutation protection.
Examples
Protecting Validation Logic
def can_approve(document, user):
with db0.read_only():
policy = next(db0.find(ApprovalPolicy, "active"))
user_roles = list(db0.find(db0.as_tag("ROLE", user)))
return (
document.status == "pending"
and policy.minimum_role in user_roles
)Rejecting Accidental Writes
document = next(db0.find(Document, "pending"))
with db0.read_only():
assert document.status == "pending"
# Raises RuntimeError.
document.status = "approved"Interaction with atomic()
Starting dbzero.atomic() inside read_only() is optimized as a no-op, because a protected non-mutating block cannot validly create atomic changes. Mutation attempts still fail.
with db0.read_only():
with db0.atomic():
# Raises RuntimeError.
order.status = "submitted"Async Functions
There is no separate async_read_only() API. Use the regular context manager in async functions.
async def render_dashboard(user):
with db0.read_only():
widgets = list(db0.find(DashboardWidget, db0.as_tag("OWNER", user)))
await load_external_metrics()
return build_dashboard(widgets)Keep a read_only() block active across await only when that is intentional. Child tasks created while the block is active may inherit the protected non-mutating context.