def tag_fields(*field_names: str) -> Callable[[type], type]
Declare memo fields whose current values should be reflected as passive tags.
dbzero.tag_fields() is a class decorator. Use it with @db0.memo when an attribute is also a common query dimension, such as a task status, owner, parent, category, or tenant.
import dbzero as db0
@db0.memo
@db0.tag_fields("status", "owner")
class Task:
def __init__(self, title, owner):
self.title = title
self.status = "open"
self.owner = ownerThe decorated fields remain ordinary Python attributes. dbzero also adds passive tags from their values, so you can query by type and field value:
alice = User("Alice")
task = Task("Review invoice", alice)
open_tasks = list(db0.find(Task, "open"))
alice_tasks = list(db0.find(Task, alice))Parameters
*field_namesstr
Field names to mirror as passive tags. Duplicate names are ignored. Passing no names has no effect.
Returns
The decorated class.
Behavior
Tag fields are synchronized automatically during initialization, assignment, and deletion.
task.status = "done"
assert list(db0.find(Task, "open")) == []
assert list(db0.find(Task, "done")) == [task]Assigning None removes the current tag and leaves the field set to None:
task.owner = None
assert list(db0.find(Task, alice)) == []Deleting a tag field removes its current tag and then deletes the attribute:
del task.status
assert list(db0.find(Task, "done")) == []Each field update and its matching tag update are applied atomically. If a new value is not a supported tag value, dbzero raises an error and keeps the previous field value and tag unchanged.
Tag fields create passive tags. They are queryable with an anchored query such as db0.find(Task, "open"), but they do not keep the tagged object alive. Use regular tags, direct references, or collection ownership when a relationship should control lifetime.
Supported Values
A tag field maps one field value to one tag. Supported values are:
None, which contributes no tag- scalar values accepted by the normal tag API, such as strings, enum values, or memo objects
Containers are not expanded into multiple tags. Assigning a list, set, tuple, or other container value raises TypeError.
task.status = ["open", "urgent"] # Raises TypeErrorIf you need many tags, store them explicitly with db0.tags(obj).add(...) instead of using one tag field.
Decorator Composition
db0.tag_fields works before or after @db0.memo:
@db0.memo
@db0.tag_fields("status")
class Task:
pass
@db0.tag_fields("status")
@db0.memo
class Task:
passMultiple db0.tag_fields decorators on the same class are merged:
@db0.tag_fields("owner")
@db0.memo
@db0.tag_fields("status")
class Task:
passThis is equivalent to @db0.tag_fields("status", "owner").
Schema Changes
Changing a class's tag-field declaration is a schema change. When automatic migration is enabled, dbzero updates existing objects so newly declared fields add passive tags and removed declarations stop contributing tags.
@db0.memo
class Task:
def __init__(self, status):
self.status = status
# Later:
@db0.tag_fields("status")
@db0.memo
class Task:
def __init__(self, status):
self.status = statusIf automatic migration is disabled, a declaration mismatch raises db0.MigrateError and leaves the previous tag-field declaration active until you run an explicit migration:
try:
Task("open")
except db0.MigrateError:
db0.migrate(Task)Renaming a tag field with db0.rename_field(...) preserves the field's tag-field identity, so the existing tag-field relationship follows the renamed field.