Python Order Tutorial
Build understanding around one draft Order: load its complete item list, change Notebook quantity from 2 to 3, save the Order and line totals together, then read the result in a new process. Follow the recorded native SQLite application and learn why each query, expression and transaction boundary is needed. The version and publication boundaries below apply to this specific example.
The retained pagination, analytics and lifecycle paths use a workspace runtime: WORKSPACE PASS — RELEASE PENDING. The receipt retains the earlier published failure; a successful wheel installation does not change this qualification.
1. Prepare the example
You need the Python toolchain, the site checkout, and the retained built
python-app-console workspace from the core Order receipt.
Read its build.toolchain, build.runtimeSource, build.runtimePackage and
verification.qualification entries for python before selecting dependencies.
The commands below assume that recorded workspace and its dependencies already
exist; they are not a clean-machine bootstrap or a claim that every published
runtime passes. For build/replay preparation use the retained workspace instructions.
Start a new model with the Python Quick Start, then use Order-scoped Assist for its generated Q/E/mutation methods. This page reuses the evaluated Order v1.0 model and the three-order fixture. Do not substitute the isolation/stock fixture: those are separately evaluated model revisions.
The complete application
contains imports, helpers, startup and all phase handlers. The excerpts below
are function-body excerpts from that application, not standalone files.
The complete source imports application-local Q, E, GENERATED_RUNTIME_MODULE, create_sqlite_service and UserContext. Keep its InvalidQuantity type, report helper and EDIT audit string when reusing the edit function.
2. Understand startup and run the first edit
UserContext.new().install(GENERATED_RUNTIME_MODULE) installs metadata and behaviors. The console registers create_sqlite_service(ORDER_DATABASE) as dataService, then explicitly awaits context.ensure_schema(). Run it as the app.main module in the prepared virtual environment so generated imports resolve.
Module installation registers metadata and behavior. Schema reconciliation is an explicit database operation. Keep startup/admin schema work separate from request execution, and obtain tenant/actor/policy from trusted server context.
From the site root, run this sequence with a new disposable database:
export ORDER_WORKSPACE="$PWD/artifacts/order/2026-10-27/python/python-app-console"
export ORDER_FIXTURE="$PWD/static/examples/order/fixture.json"
export ORDER_DATABASE="$(mktemp -d)/orders.sqlite"
cd "$ORDER_WORKSPACE"
for phase in bootstrap seed draft-graph note-value note-null note-empty note-not-loaded invalid-quantity draft-edit restart-readback; do
./.venv/bin/python -m app.main "$phase"
done
Run seed once on the fresh database; the fixture program is not an import/upsert
service. Each phase starts a fresh process against the same absolute database
path. The two NotLoaded probes are intentional negative examples; their handler
reports the native error and finishes successfully.
| Stop and inspect | Expected result |
|---|---|
bootstrap | Explicit schema setup and ORDER_OK phase=bootstrap. |
seed and draft-graph | Three Orders, six items; ORD-2026-1001 is DRAFT with total 3850 USD cents. |
| Note phases | Value, SQL NULL, empty text and NotLoaded remain distinct. |
invalid-quantity | 0, -1, 1.5 and 101 rejected before save. |
draft-edit | Notebook quantity 3, Notebook line total 3750, Order total 5100. |
restart-readback | The same totals in a new process; Order and changed item version 2, untouched item version 1. |
The handlers print ORDER_CASE JSON and an ORDER_OK completion line. Find the
Order by business number and the line by SKU; generated numeric IDs are not
portable fixture keys. Compare the returned graph with these expected values
before adapting the operation to your own model.
3. Load the full Order before editing
rows = await (Q.customer_orders().with_order_number_is("ORD-2026-1001")
.select_order_item_list_with(Q.order_items().order_by_sku_code_ascending().order_by_id_ascending().limit(101))
.limit(2).comment("Load full draft order and complete bounded items")
.purpose("Validate and edit one draft order").execute_for_list(context))
The outer bound of 2 lets the application reject a duplicate business identity. The child bound of 101 is an overflow sentinel for a supported maximum of 100 items. Require exactly one Order, DRAFT status and at most 100 loaded items before continuing. A one-item preview is useful for a list screen, but cannot supply the complete total for an edit. The full source performs these checks after the query; keep them when adapting the excerpt.
comment describes the operation and purpose records the business reason.
Both travel with execution through the trusted context. A buyer filter in this
v1.0 fixture is a business predicate; it does not authenticate or authorize a
buyer. Use the separate buyer/tenant isolation contract
when implementing protected access.
4. Validate money and save one atomic edit
The application validates integral quantity in [1, 100], USD currency, each stored
line amount and the complete new Order total. Prices and totals are integer cents.
It finds exactly one SKU-NOTEBOOK, computes 3 × 1250 + 3 × 450 = 5100, then
changes the selected item and Order together. The following write excerpt assumes
those validated, fully loaded variables; it is not a replacement for validation.
The audit reason is Edit draft Order quantity and totals atomically, named
EDIT in most examples and editReason/EditReason in Go, Swift and .NET.
changed.update_quantity(quantity)
changed.update_line_total_minor(quantity * E.order_item(changed).unit_price_minor().eval())
order.update_total_minor(total)
print("ORDER_GRAPH_ATTEMPT", flush=True)
await order.audit_as(EDIT).save(context)
Python bool is an int subclass, so the application explicitly rejects booleans as quantities, as well as fractional, non-finite and out-of-range values. The Order graph save owns the transaction. Keep cancellation propagation and provider cleanup in the async boundary; a broad exception-to-success conversion hides failed writes.
A successful audited save returns the persisted typed entity. The core example reads again after mutation, rather than treating a pre-save object as independent evidence of committed state. A rejected stale write requires reloading authoritative state and a business decision before retrying; it is not permission to overwrite the winner or retry every provider error.
5. Use projections, nulls and pages deliberately
| Task | Python API in this example | Result / responsibility |
|---|---|---|
| Execute a bounded query | await query.execute_for_list(context) | Typed SmartList; preserve native errors and check cardinality. |
| Read selected money | E.customer_order(order).total_minor().eval() | Loaded integer cents; not a formatting or currency conversion API. |
| Access selected items | order.order_item_list() | Only the relation that was explicitly loaded. |
| Default a selected SQL NULL note | order_note().or_if_null("No note") | Null-only fallback; an empty string stays empty and NotLoaded still reports a missing field. |
| Save an audited mutation | await order.audit_as(EDIT).save(context) | Persisted typed entity; the required transaction includes all changed entities. |
| Request an exact page total | offset(offset).limit(limit).count() → page.total_count | Total of the filtered set, independent of page length. |
The note accessors in the table are called on the corresponding E expression.
required, report, pageRows, load, edit and prepare_edit are application
helpers where present; they are not additional generated terminal methods.
| Note state | Expected behavior |
|---|---|
| Selected value | Leave at reception stays that value. |
| Selected SQL NULL | A null-only default produces No note. |
| Selected empty text | Remains the empty string. |
| Omitted field | Native NotLoaded diagnostic, including when a null-only default is attempted. |
For a read-only list screen, the existing bounded-page handler validates
offset >= 0 and 1 <= limit <= 3, sets a stable total/ID order and explicitly
selects one item preview per Order. With validated offset, limit and
buyer, its native query is:
page = await (Q.customer_orders().with_buyer_reference_is(buyer).order_by_total_minor_ascending().order_by_id_ascending()
.select_order_item_list_with(Q.order_items().order_by_sku_code_ascending().order_by_id_ascending().limit(1)).offset(offset).limit(limit).count()
.comment('Read bounded Order pages').purpose('Compare filtered totals and per-order item previews').execute_for_list(context))
This query only reads. Do not save its one-item preview as if it were a full
Order edit or infer deletion of omitted items. The pagination fixture has eight
matching BUYER-EXPORT Orders, whereas the first-edit fixture above has three
Orders overall. See the page API decision guide
for fixture selection, continuous export, retained-ID jumps and aggregate results.
6. Diagnose a failure before changing code
The console deliberately refuses optimized Python (-O or PYTHONOPTIMIZE) because it uses assertions for fixture checks. Use the recorded virtual environment with assertions enabled; do not remove the guard to make the example start.
| Symptom | First action |
|---|---|
| Workspace or package missing | Confirm the retained artifact prerequisite; follow the Quick Start for fresh generation instead of assuming this checkout ships compiled outputs. |
| Empty Order or duplicate row | Confirm absolute DB path, fixture and business number; preserve the cardinality check. |
| Wrong total after edit | Check complete item loading and integer validation before the audited save. |
| Missing field error | Reload the field/relation explicitly; do not substitute a null default. |
| Version conflict | Reload authoritative state and decide whether the requested edit is still valid. |
| Busy/locked, commit or cancellation failure | Inspect native provider error and transaction boundary; follow the Python Order troubleshooting table. |
Continue with the Order API usage guide, Python advanced data operations, version and qualification table and documentation coverage matrix. The School regression baseline and all prior Order receipts remain unchanged.
Continue with advanced Order writes
Follow Python Advanced Order Mutations for explicit line deletion, legal state transitions and stale-version recovery. Each recipe explains its starting state, transaction boundary, native errors and independent readback using the existing qualified example. Start a fresh recipe database; do not reuse the quantity-edited database above.
Continue with Order queries
Use Python Order Queries for continuous export, retained-ID page jumps and provider summaries. The query recipes use separate nine-/ten-order datasets; keep the fixture and context/store lifetime with the operation you are learning.
Continue with buyer and tenant isolation
Use Python Order Isolation to upgrade the model to v1.1 and build root/buyer scope from an application-owned trusted session.