Python Database API
The Python Database API is currently in Beta. Methods, parameters, and behavior are subject to change as we refine the interface.
Overview
The Python Database API provides live read and write access to model objects from Davinci Python code. It is available through the globaldatabase object and the equivalent davinci.database alias.
The API supports:
- Searching model objects by name, type, fields, path, and graph relationships
- Loading full objects or selected fields
- Creating objects under a parent, including full nested trees
- Updating editable fields on matching objects
- Saving objects with create-or-update behavior
- Deleting objects, optionally recursively
- Reading and writing table cells with A1 notation
- Adding and removing relationships between objects
- Retrieving project-level configuration
Runtime Use
You can write code in either of these styles:await or result = await main().
Do not use asyncio.run(main()) there, because the runtime already provides an active event loop.
Both the global database object and davinci.database point to the same API.
Object References
Anywhere an object reference is expected, bracketed UUID strings are the preferred style:Common Return Shape
Whenfields="common" is used (the default), objects are projected to a standardised set of fields. The exact fields depend on the object type.
Base fields (all types):
Type-specific fields included in common:
resolvedValue is included as a read-only nested object representing the system-computed result:
- For
attribute/constraint/state:{ value, error, errorMsg, equationString }— the innervalueis the computed numeric or string result. - For
task:{ startDate, endDate, duration, progression, error, errorMsg }.
Writable Fields
database.update() and database.save() enforce a per-type allowlist. Attempting to write a read-only field raises an error.
Writable on all types: name, shortName, documentation
Guidance:
- Use
namefor the human-readable object label - Use
shortNamefor numbering, identifiers, diagram numbers, and compact codes - Example:
name="Mission Analysis"andshortName="1.2.3"
Methods
database.search(...)
Search returns a list of matching objects.
path="Package.Part.Attribute"— dot-path resolutiontype="attribute"ortype=["attribute", "constraint"]where={...}— field filtersgraph={...}— graph filtersfields="common"orfields=[...]limit=50— max results (default 50, max 500)
where operators
Graph search
database.load(...)
Load returns full object data or selected fields. If no table cell selector is used:
- one match returns a single object dict
- multiple matches return a list
- if the returned object is a table, it is a dict-like table object that also supports A1 reads such as
await table["B2"]
database.create(...)
Create is idempotent by default for named objects. If an object with the same parent + name + type already exists, that existing object is reused and updated instead of creating a duplicate with a new UUID.
- Use
type, notobjectType parentis a top-level call argument ondatabase.create(...), not a field inside each object- Good:
await database.create(parent=package_id, objects=[{"type": "item", "name": "X"}]) - Bad:
await database.create(objects=[{"type": "item", "name": "X", "parent": package_id}]) - If an object has a number/code/hierarchy label, put that in
shortName - Example:
{"type": "item", "name": "Mission Analysis", "shortName": "1.2.3"} - Writable fields may be supplied either directly on the object or under
fields - Example:
{"type": "item", "name": "X", "shortName": "1.2"} - Example:
{"type": "item", "name": "X", "fields": {"shortName": "1.2"}} - If both are provided, values inside
fieldswin - Nested creation is supported through
children - Re-running the same payload under the same parent will generally reuse the same object IDs
- Explicit
idstill wins when provided - For
referenceobjects, this reuse applies to the metadata object. Separately stored reference file/blob content remains attached to the reused reference ID rather than creating a duplicate metadata object.
Creating a parts tree
Thechildren key on any node is recursively processed. The entire tree is created in a single operation with all parent/child relationships wired automatically.
database.update(...)
Update applies field changes to matching objects. Only writable fields are accepted (see Writable Fields above).
Important:
- Use
fields={...}, notupdates={...} target={"id": ...}expects a single id string, not a list of ids
database.save(...)
Save performs create-or-update behavior and returns the saved objects.
Default identity behavior:
- If
idis provided and exists, that object is updated - Otherwise, if an object with the same
parent + name + typealready exists, that object is reused and updated - Otherwise, a new object is created
- For
referenceobjects, the metadata object is reused by identity; separately stored reference content stays associated with the reused reference ID unless updated through the file-specific save path parentis a top-level call argument ondatabase.save(...), not a field inside each object- Good:
await database.save(parent=package_id, objects=[{"type": "item", "name": "X"}]) - Bad:
await database.save(objects=[{"type": "item", "name": "X", "parent": package_id}]) - Writable fields may be supplied either directly on the object or under
fields - If both are provided, values inside
fieldswin
database.delete(...)
Delete removes matching objects.
Important:
target={"id": ...}expects a single id string- If you have many ids, do not pass a list into
target.id; use another supported target form or issue separate batched deletes at a higher level
database.move(...)
Move re-parents existing objects into a different package or parent object.
Use move(...) for hierarchy changes. Do not try to move objects by writing parent through database.update(...).
Basic move:
parentis required and is the destination parent/packagetarget={"id": ...}expects a single id stringneighbourordering is currently for single-target moves only- return value is a list of moved objects
database.move(...)raises an error if the selector matches 0 objects instead of silently succeeding- when debugging a move, print the selected ids or names before the move call and compare them to the returned moved objects
database.relate(...)
Add or remove a relationship between two objects.
uses, satisfies, verify, allocate, subject, realize, derive, trace, performs, actor, mitigation, source, impact, result, resource, dependency. Custom relationship types defined in the project are also accepted.
Both source and target accept UUIDs or dot-path strings.
They may also be arrays for batched relationship updates.
source and target are arrays, database.relate(...) applies the
Cartesian product in one batched call. The return value is:
- a single dict for one source + one target
- a list of dicts for batched array input
database.get_project_info()
Returns project-level configuration as a dict.
Tables
Tables can be loaded and updated through the samedatabase API.
Read cells with A1 notation
Cell queries return a 2D list (grid) for easy processing. Each entry is the cell content value. Read one cell:"".
Read a range:
cell_fields:
Use table data to build model objects
Tables are a valid input source for model generation. A common pattern is:- load a table range with A1 notation
- transform the rows into object payloads
- create or save the objects in one batched call
Read table structure
The common projection for a table now includesrowCount and colCount directly:
Update cells
Update a single cell:References
Reference loading works throughdatabase.load(..., include_reference_data=True).
davinci_save_file(...). A database-native reference save method is planned.
Current Notes and Constraints
- The API is async. Use
await. fields="common"is the default and returns the standardised field set described above.parentfor create/save is a string reference, not{"id": ...}.- Both
"uuid"and"[uuid]"forms are accepted where object references are expected. - Path-based loads hydrate the full object before projection, so nested fields like
rowsandcolumnswork when explicitly requested. - Database writes are validated and applied through the shared operations pipeline.
- Attempting to write a read-only field raises:
Field 'resolvedValue' is read-only and cannot be updated on type 'attribute'.