Skip to content

Schema System (zentral_systems)

Single source of truth for schema versions and forward-compatible migration logic across all plant design data models (library and planning).

Centralised here because library schema changes often cascade to project items -- ProjectItems are created from LibraryItems, and the export flow copies data back. Having both migration paths side by side prevents version drift.

Sub-packages

Directory Purpose
constants/ versions.py -- all schema version constants in one place
migration/ Per-domain upgrade logic (LibrarySchemaValidator, ProjectSchemaValidator)

Version constants (constants/versions.py)

Constant Scope Current
LIBRARY_FORMAT_VERSION LibraryContainer envelope 1
LIBRARY_ITEM_SCHEMA_VERSION LibraryItem fields 1
LIBRARY_FOLDER_SCHEMA_VERSION Folder fields 1
PROJECT_FORMAT_VERSION ProjectContainer envelope 1
PROJECT_ITEM_SCHEMA_VERSION ProjectItem fields 2

Migration files

File Class Upgrades
migration/library_migration.py LibrarySchemaValidator upgrade_item(), upgrade_folder()
migration/project_migration.py ProjectSchemaValidator upgrade_item(), upgrade_container()

How migration works

  1. Each model's from_dict() checks schema_version against the current constant.
  2. If the persisted version is lower, the matching validator is called.
  3. The validator walks through each version step (if from_version < N), filling missing fields with defaults or removing deleted fields.
  4. The dict is stamped with the current version before being passed to the constructor.

Adding a new schema version

  1. Bump the relevant constant in constants/versions.py.
  2. Add a new if from_version < N: block in the matching migration file.
  3. Use data.setdefault("new_field", default) for added fields.
  4. Use data.pop("removed_field", None) for removed fields.
  5. No changes needed in from_dict() -- the version check triggers automatically.

Backward compatibility

The per-system constants/schema.py files (library_system/constants/schema.py, planning_system/constants/schema.py) re-export from this package with their original names so existing imports continue to work.