Custom Python per field
The python field type runs a small Python snippet per row and uses the return value
as the field’s value. Use it when none of the built-in types fit — think industry-specific
identifiers, inter-field arithmetic, or values pulled from a workspace file.
Quick example
A field that returns an “internal customer ID” combining a fixed prefix, a country code from a sibling field, and a 6-digit sequential number:
def value(rng, row, dm): seq = dm.counter("customer_seq").next() # workspace-wide counter country = row["country"] # already-generated sibling return f"CUST-{country}-{seq:06d}"In the field editor, pick Type → Python, paste the code, and the live preview runs it.
The function signature
def value(rng, row, dm): ... return <some value>| Parameter | What it is |
|---|---|
rng | A seeded random.Random instance. Use this for reproducibility. |
row | A dict of all fields generated before this one in the template. |
dm | DataMaker context: dm.counter(), dm.workspace_file(), dm.template(). |
The return value is serialised as JSON — strings, numbers, booleans, lists, dicts,
None, and ISO-formatted dates all work.
Field order matters
row only contains fields generated before this one. In the template builder, drag
the Python field below any siblings it reads.
If you need to read a field that comes later, use a derived field instead — derived fields run in a second pass.
What dm exposes
dm.counter(name)
A workspace-wide monotonic counter. Useful for sequential IDs that survive across generations.
dm.counter("orders").next() # 1, 2, 3, ...dm.counter("orders").peek() # current without incrementingdm.counter("orders").reset() # back to 1 (rare; usually only in scenarios)dm.workspace_file(path)
Read a file uploaded to the workspace. Useful for pulling from a CSV reference table.
import csvdef value(rng, row, dm): with dm.workspace_file("currency_rates.csv").open("r") as f: rates = {r["code"]: float(r["rate"]) for r in csv.DictReader(f)} return round(row["amount_usd"] * rates[row["currency"]], 2)Workspace files are project-scoped — see Scenarios → Workspace files for upload mechanics.
dm.template(name_or_id)
Generate one row from another template, inline. Useful when the simple nested-template field can’t express what you need.
def value(rng, row, dm): if row["country"] in ("DE", "AT", "CH"): return dm.template("Address (DACH)").generate_one() return dm.template("Address (Generic)").generate_one()Limits
- Max execution time per call: 2 seconds. Longer-running logic belongs in a scenario, not a per-field Python.
- Standard library only. No
pip installfrom inside a field. If you need third-party packages, use a scenario. - Stateless across rows except via
dm.counter(). Don’t use module-level globals.
When to reach for a scenario instead
If your field logic needs:
- A network call (REST or SAP),
- Multi-row coordination (referential integrity),
- A third-party Python package,
- More than 2s of compute,
use a scenario. Scenarios run in a fuller Python environment, support
pip packages, and have proper logs and retries.