# Tutorial This tutorial walks through a complete example: creating a data model, setting up a database, and performing operations with CommitDatabase. > **Prerequisites**: This tutorial assumes you have read [DSM](dsm.md) and > understand the basics of DSM syntax and the assemble → parse → introspect > workflow. ## The User/Login Model We'll create a simple model with Users who have Login credentials and Identity information. ### Step 1: Define the Data Model Create a file `model.dsm`: ```{literalinclude} ../_fixtures/Tuto/model.dsm :language: dsm ``` ### Step 2: Validate the Model Check the DSM syntax: ```bash python3 tools/dsm_util.py check model.dsm ``` ### Step 3: Create a Database Create a Commit database that embeds the definitions: ```bash python3 tools/dsm_util.py create_commit_database model.dsm model.cdb ``` ### Step 4: Open and Explore Open the database in Python and list the types and attachments it carries: ```{doctest} >>> sorted(str(t) for t in db.definitions().types()) ['Tuto::Account', 'Tuto::Identity', 'Tuto::Login', 'Tuto::Status', 'Tuto::Texture', 'Tuto::Thumbnail', 'Tuto::User'] >>> sorted(str(a).split()[-1] for a in db.definitions().attachments()) ['Tuto::account', 'Tuto::avatar', 'Tuto::identity', 'Tuto::login', 'Tuto::portrait'] ``` ### Step 5: Inject Constants `db.definitions().inject()` makes types accessible as constants in the caller's namespace (already done in this tutorial's setup): ```{doctest} >>> TUTO_A_USER_LOGIN attachment Tuto::login >>> TUTO_S_LOGIN Tuto::Login ``` See [DSM Processing — Inject Constants](dsm.md#inject-constants) for the naming convention. ### Step 6: Create a Key and Document Create a new User key. `instance_id()` returns the underlying UUID (randomly generated, so we just check the type here): ```{doctest} >>> key = TUTO_A_USER_LOGIN.create_key() >>> isinstance(key.instance_id(), ValueUUId) True ``` Create a Login document: ```{doctest} >>> login = TUTO_A_USER_LOGIN.create_document() >>> login {nickname='', password=''} >>> login.nickname = "zoop" >>> login.password = "robust" >>> login {nickname='zoop', password='robust'} ``` ### Step 7: Commit to Database Create a mutable state, associate the document with the key, and commit: ```{doctest} >>> mutable_state = CommitMutableState(CommitStateBuilder.initial_state(db)) >>> mutable_state.attachment_mutating().set(TUTO_A_USER_LOGIN, key, login) >>> commit_id = db.commit_mutations("First Commit", mutable_state) >>> isinstance(commit_id, ValueCommitId) and len(str(commit_id)) == 40 True ``` ```{note} **Scope of this tutorial** The single-author flow shown here does not exercise the reduction behavior described in [The Dual-Layer Contract](../commit/commit_contract.md): with one author committing in sequence, mutations are never silently dropped and no LWW arbitration takes place. The contract becomes load-bearing once **several authors' writes are reduced automatically** — see [Modes of Use](../commit/commit_modes.md) for the diagnostic and which mode applies to your application. ``` ### Step 8: Read from Database Read the document back: ```{doctest} >>> state = CommitStateBuilder.state(db, commit_id) >>> result = state.attachment_getting().get(TUTO_A_USER_LOGIN, key) >>> result Optional({nickname='zoop', password='robust'}) >>> result.unwrap() {nickname='zoop', password='robust'} ``` ### Step 9: Update a Field Update using a path-based setter. Capture the new commit id returned by `commit_mutations` — the database's mutating APIs don't auto-advance an implicit "current commit" pointer, so chaining commits requires the explicit id: ```{doctest} >>> mutable_state = CommitMutableState(CommitStateBuilder.state(db, commit_id)) >>> mutable_state.attachment_mutating().update(TUTO_A_USER_LOGIN, key, TUTO_P_LOGIN_NICKNAME, "zoopy") >>> updated_id = db.commit_mutations("Update Nickname", mutable_state) ``` ### Step 10: View History Read from different commits: ```{doctest} >>> state = CommitStateBuilder.state(db, updated_id) >>> state.attachment_getting().get(TUTO_A_USER_LOGIN, key) Optional({nickname='zoopy', password='robust'}) >>> state = CommitStateBuilder.state(db, commit_id) >>> state.attachment_getting().get(TUTO_A_USER_LOGIN, key) Optional({nickname='zoop', password='robust'}) >>> state = CommitStateBuilder.initial_state(db) >>> state.attachment_getting().get(TUTO_A_USER_LOGIN, key) nil ``` ### Step 11: Inspect Commit Headers ```{doctest} >>> header = db.commit_header(updated_id) >>> header.label() 'Update Nickname' >>> header.parent_commit_id() == commit_id True ``` The walkthrough above uses the **dynamic API** (definitions loaded at runtime). The same operations are available through the **static API** (typed Python package generated by Kibo). See [Value Chains](../ecosystem/value-chains.md) for the contrast.