Lookup Patterns
When to use get vs get_or_none vs one vs first vs count vs iter — exact raise semantics and when each is appropriate.
Every Management API collection offers the same six lookup methods. Choosing the right one makes your code's intent clear and prevents silent bugs.
Method overview
| Method | Returns | Raises | When to use |
|---|---|---|---|
get(id) | T | NotFoundError if missing | You expect the item to exist; missing is a bug. |
get_or_none(id) | T | None | Never | The item may legitimately not exist. |
one(**filters) | T | NotFoundError (0), MultipleResultsError (>1) | Exactly one item must match; anything else is a bug. |
first(**filters) | T | None | Never | The most recent (or top-ordered) match, or None. |
count(**filters) | int | Never | How many items match, without fetching them. |
iter(**filters) | Iterator[T] | Never (lazy) | Walk all matching items without managing cursors. |
get(id, *, include=())
Fetch exactly one item by its ID. Raises NotFoundError if no item with that ID exists.
# Raises NotFoundError if "team-id" does not exist:
team = project.teams.get("team-id", include=["counts"])Use get when you have an ID from a previous call and expect the item to be present. If the item might be missing, use get_or_none.
get_or_none(id, *, include=())
Same as get but returns None instead of raising if the item is not found.
team = project.teams.get_or_none("team-id")
if team is None:
# handle missing team
...one(*, include=(), **filters)
Apply filters and expect exactly one result. Raises:
NotFoundError— if no items match.MultipleResultsError— if more than one item matches.
# Exactly one team named "Engineering" in category "engineering" must exist:
team = project.teams.one(name="Engineering", category="engineering")
# Exactly one audit event with this request_id:
event = project.audit_events.one(request_id="req-abc-123")Use one when your business logic requires a unique match and you want to be alerted if the assumption is violated.
first(*, include=(), **filters)
Apply filters and return the first result in the current ordering, or None if no items match.
# Most recent simulation node, or None if none exist:
node = project.workflow_nodes.first(label="SimulationRun")
# Most recent "project.updated" audit event:
event = project.audit_events.first(action="project.updated")Use first when zero matches is acceptable and you want the top-ordered item. The ordering is controlled by order_by and order on the collection — defaults vary per collection (e.g. workflow nodes default to created_at desc).
count(*, **filters)
Return how many items match the filters, without fetching the items themselves.
sim_count = project.workflow_nodes.count(label="SimulationRun")
error_events = project.audit_events.count(level="error")
engineering_teams = project.teams.count(category="engineering")Use count for dashboards, validation checks, or before deciding whether to paginate.
iter(*, include=(), **filters)
Return an iterator that walks all matching pages transparently. Does not require managing cursors.
for team in project.teams.iter(category="engineering"):
print(team.id, team.name)
for node in project.workflow_nodes.iter(label="SimulationRun"):
process(node)Use iter for bulk reads. Pages are fetched lazily — if you break out of the loop early, only the pages needed are fetched.
Raise semantics summary
from optixlog import NotFoundError, MultipleResultsError
# get — NotFoundError on missing id:
try:
team = project.teams.get("nonexistent-id")
except NotFoundError:
print("team not found")
# one — NotFoundError on zero matches, MultipleResultsError on >1:
try:
team = project.teams.one(category="engineering")
except NotFoundError:
print("no matching team")
except MultipleResultsError:
print("more than one team matched")See Error Model for the full exception hierarchy.
Special case: team memberships
team.memberships.get(...) and team.memberships.get_or_none(...) take a user_id= keyword argument instead of a positional ID, because the primary key of a membership is the user-team pair:
membership = team.memberships.get(user_id="user-id", include=["user"])
maybe_membership = team.memberships.get_or_none(user_id="user-id")Special case: node labels
Node labels do not support one or first — use get, get_or_none, list, iter, or count.
Pagination, Filtering, and Includes
How Page[T] works, how iter() walks all pages, AND filter semantics, include= for related data, and ordering with order_by and cursor.
Error Model
The OptixLog exception hierarchy — OptixLogError, AuthenticationError, NotFoundError, MultipleResultsError, ValidationError — and when each is raised.