Init Data - Overview¶
FastEdgy provides an init-data loader to seed reference data declaratively and idempotently. Records are described in plain Python files and identified by stable external IDs, so running the loader again updates existing rows instead of creating duplicates. The approach mirrors Odoo's ir.model.data pattern.
Key features¶
- Declarative data files: One file per model under the server
data/directory, exposing adatalist of records - Stable external IDs: Each record is tagged with
id("my_key"), decoupling seed data from auto-incremented primary keys - Idempotent upsert: Re-running compares the stored record and only creates or updates what changed
- Relation resolution:
ref("my_key")wires foreign keys, one-to-many and many-to-many relations across files - File uploads:
file("path")uploads an asset through the Storage service and stores the resulting path - Automatic ordering: Dependencies expressed with
ref(...)are topologically sorted, even across files - CLI and programmatic: Run via
db init-dataor callload_data()from code and tests
How it works¶
- Discovery: Every
*.pyfile in thedata/directory is loaded; the file name matches a model (its table name or metadata name). Files starting with_are ignored, so you can keep helper modules alongside data files. - Parsing: Each file exposes a
datalist of dictionaries. Every record declares its external key through theid("...")marker. - Ordering: Records referencing other records via
ref("...")are topologically sorted so dependencies load first. A circular reference raises a clear error. - Upsert: For each record, the loader looks up its external key in the registry. If the target row still exists, scalar fields are compared and saved only when they changed; otherwise a new row is created. Relations are reconciled by diffing the current and target sets.
- Transaction: The whole load runs inside a single transaction, so a failure leaves the database untouched.
External ID registry¶
External keys are persisted in a dedicated data_records table (backed by the DataRecord model). Each entry maps a key to its target model and record_id. This registry is what makes the loader idempotent and lets ref("...") resolve to real primary keys across separate runs.
The DataRecord model ships with the framework and is registered automatically, so it appears in your migrations without any manual declaration.