ecm-generate-models
ecm-generate-models is a command-line tool that generates typed Python model classes (ECMFolderModel, ECMRegisterModel, ECMDocumentModel) from the enaio object definition.
The generated classes contain a typed ECMField declaration for every field, plus enums for list catalog fields. This gives full code completion and type checking in the IDE without having to maintain field names manually.
1. Installation
The command is included in the base installation — no additional extra required:
-
uv
-
pip
uv add ecmind-blue-client
pip install ecmind-blue-client
2. Arguments
2.1. Source (exactly one required)
| Argument | Default | Description |
|---|---|---|
|
— |
Hostname or IP address of the enaio server. Mutually exclusive with |
|
— |
Path to a local |
2.2. Server options (used with --host)
| Argument | Default | Description |
|---|---|---|
|
|
TCP port of the enaio server. |
|
— |
Login username. Required with |
|
— |
Login password. Required with |
|
SSL enabled |
Disable SSL/TLS. Default: SSL is enabled. |
|
— |
Application name shown in Enterprise Manager (optional). |
2.3. Output
| Argument | Default | Description |
|---|---|---|
|
stdout |
Directory to write generated |
|
all |
Only generate models for the cabinet with this internal name. If omitted, all cabinets are generated. |
3. Examples
3.1. Generate from a live server
The most common use case: connect to the server and write all cabinets into a directory.
ecm-generate-models \
--host enaio.example.com \
--username admin \
--password secret \
--output-dir ./models
Terminal output:
Written: models/Invoices.py
Written: models/Contracts.py
3.2. Generate only a specific cabinet
ecm-generate-models \
--host enaio.example.com \
--username admin \
--password secret \
--cabinet Invoices \
--output-dir ./models
3.3. Generate from a local XML file
The asobjdef file can be exported once from the server and reused locally — no active server connection required.
ecm-generate-models --file asobjdef.xml --output-dir ./models
4. Output format
One .py file is generated per cabinet with the following structure:
-
Imports (only those actually needed)
-
Enums for fields with list catalogs
-
ECMTableRowModelsubclasses for table fields -
Model classes (
ECMFolderModel,ECMRegisterModel,ECMDocumentModel)
Example of a generated file:
"""Auto-generated ECM model classes for cabinet "Invoices".
DO NOT EDIT — regenerate with ecm-generate-models.
"""
from enum import Enum
from datetime import date
from ecmind_blue_client.ecm.model import (
ECMDocumentModel,
ECMField,
ECMFolderModel,
ECMRegisterModel,
ECMTableField,
ECMTableRowModel,
)
class InvoiceFolder_StatusEnum(str, Enum):
OPEN = "Open"
PAID = "Paid"
CANCELLED = "Cancelled"
class InvoiceDocument_PositionsRow(ECMTableRowModel):
Description: ECMField[str] = ECMField(str, default=None)
Amount: ECMField[float] = ECMField(float, default=None)
Date: ECMField[date] = ECMField(date, default=None)
class InvoiceFolder(ECMFolderModel):
_internal_name_ = "InvoiceFolder"
Title: ECMField[str] = ECMField(str, mandatory=True, default=None)
Year: ECMField[int] = ECMField(int, default=None)
Status: ECMField[InvoiceFolder_StatusEnum] = ECMField(InvoiceFolder_StatusEnum, default=None)
class InvoiceRegister(ECMRegisterModel):
_internal_name_ = "InvoiceRegister"
Name: ECMField[str] = ECMField(str, mandatory=True, default=None)
class InvoiceDocument(ECMDocumentModel):
_internal_name_ = "InvoiceDocument"
Title: ECMField[str] = ECMField(str, mandatory=True, default=None)
InvoiceDate: ECMField[date] = ECMField(date, default=None)
Positions: ECMTableField[InvoiceDocument_PositionsRow] = ECMTableField(default_factory=list)
5. Using the generated models
The generated files are imported once after creation and then used throughout the application:
-
Sync
-
Async
from ecmind_blue_client.ecm import ECM
from ecmind_blue_client.pool import SyncPoolClient
from models.Invoices import InvoiceFolder, InvoiceRegister, InvoiceDocument
ecm = ECM(SyncPoolClient(servers="enaio.example.com:4000:1", username="admin", password="secret"))
# Query with full type support
for folder in (
ecm.dms.select(InvoiceFolder)
.where(InvoiceFolder.Year >= 2024)
.order_by(InvoiceFolder.Year.DESC)
.stream()
):
print(folder.system.id, folder.Title, folder.Status)
# Create a new object
folder = ecm.dms.insert_and_get(
InvoiceFolder(Title="Invoice 2024", Year=2024)
)
# Update a field
folder.Title = "Invoice 2024 (updated)"
ecm.dms.update(folder)
from ecmind_blue_client.ecm import ECM
from ecmind_blue_client.pool import AsyncPoolClient
from models.Invoices import InvoiceFolder, InvoiceRegister, InvoiceDocument
ecm = ECM(AsyncPoolClient(servers="enaio.example.com:4000:1", username="admin", password="secret"))
# Query with full type support
async for folder in (
ecm.dms.select(InvoiceFolder)
.where(InvoiceFolder.Year >= 2024)
.order_by(InvoiceFolder.Year.DESC)
.stream()
):
print(folder.system.id, folder.Title, folder.Status)
# Create a new object
folder = await ecm.dms.insert_and_get(
InvoiceFolder(Title="Invoice 2024", Year=2024)
)
# Update a field
folder.Title = "Invoice 2024 (updated)"
await ecm.dms.update(folder)
6. When to regenerate
The generated files should be recreated when:
-
New fields or object types have been added to the enaio archive schema
-
Field types or mandatory field flags have changed
-
Entries in list catalogs have been added or removed
|
The files carry the comment |
7. See also
-
Getting started — overview of first steps with the library
-
select() — query objects with a typed model
-
insert() — create new objects