OptixLog Docs
Concepts

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

MethodReturnsRaisesWhen to use
get(id)TNotFoundError if missingYou expect the item to exist; missing is a bug.
get_or_none(id)T | NoneNeverThe item may legitimately not exist.
one(**filters)TNotFoundError (0), MultipleResultsError (>1)Exactly one item must match; anything else is a bug.
first(**filters)T | NoneNeverThe most recent (or top-ordered) match, or None.
count(**filters)intNeverHow 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.

On this page