ECM-Modell
Das ECM-Modell ist das zentrale Konzept von ecmind-blue-client. Es bietet eine typisierte, ORM-artige Schnittstelle für ECM-Objekte — Ordner, Register und Dokumente.
Jedes Objekt, das von ecm.dms.select(), ecm.dms.get(), ecm.dms.insert_and_get() oder ecm.dms.update_and_get() zurückgegeben wird, ist eine Instanz einer Modellklasse. Modellinstanzen enthalten sowohl Indexfelddaten (benutzerdefinierte Felder) als auch Systemmetadaten (immer über obj.system).
1. Modellklassen
Eine Modellklasse wird durch Ableitung von einer der drei Basisklassen definiert. Die Felder werden als typisierte Klassenannotationen deklariert:
from ecmind_blue_client.ecm.model import ECMFolderModel, ECMRegisterModel, ECMDocumentModel, ECMField, ECMTableField, ECMTableRowModel
class RechnungsZeile(ECMTableRowModel):
Betrag: ECMField[float]
Beschreibung: ECMField[str]
class RechnungsOrdner(ECMFolderModel):
_internal_name_ = "InvoiceFolder" # interner Name des Objekttyps auf dem Server
Titel: ECMField[str]
Jahr: ECMField[int]
Positionen: ECMTableField[RechnungsZeile]
class RechnungsRegister(ECMRegisterModel):
_internal_name_ = "InvoiceRegister"
Name: ECMField[str]
class RechnungsDokument(ECMDocumentModel):
_internal_name_ = "InvoiceDocument"
Betreff: ECMField[str]
Betrag: ECMField[float]
| Basisklasse | Verwendung |
|---|---|
|
Ordner-Objekttypen |
|
Register-Objekttypen (Unterordner) |
|
Dokument-Objekttypen |
1.1. Dynamische Modelle
Wenn der Objekttyp erst zur Laufzeit bekannt ist, können statt einer Klassendefinition Factory-Funktionen verwendet werden:
from ecmind_blue_client.ecm.model import make_folder_model, make_register_model, make_document_model
RechnungsOrdner = make_folder_model("InvoiceFolder")
Dynamische Modelle unterstützen dieselbe Query-API. Nicht deklarierte Felder werden über obj["internalName"] abgerufen.
2. ECMField
ECMField[T] ist der Descriptor für typisierte Indexfelder. Der Typparameter T legt den Python-Typ des Feldwertes fest (str, int, float, datetime, date, time, bool).
Auf Klassenebene wirkt ECMField als Condition-Builder:
RechnungsOrdner.Jahr == 2024 # ECMCondition
RechnungsOrdner.Jahr >= 2020 # ECMCondition
RechnungsOrdner.Titel.in_("A", "B") # ECMCondition
RechnungsOrdner.Jahr.DESC # ECMSortOrder für order_by()
Auf Instanzebene gibt er den gespeicherten Wert zurück:
ordner = ecm.dms.get(RechnungsOrdner, 12345)
print(ordner.Jahr) # int | None
3. ECMTableField
ECMTableField[RowT] enthält mehrreihige Tabellenfelder. Die Zeilenklasse muss ECMTableRowModel ableiten und ihre Spalten als ECMField-Annotationen deklarieren.
for zeile in ordner.Positionen:
print(zeile.Betrag, zeile.Beschreibung)
Jede Zeile stellt folgende Attribute bereit:
| Attribut | Beschreibung |
|---|---|
|
Interne Zeilenkennung, die vom Server vergeben wird. |
|
|
|
Gibt |
4. system-Eigenschaften
Jede Modellinstanz besitzt ein system-Attribut, das alle serverseitig befüllten Metadaten enthält. Indexfelder (eigene ECMField-Deklarationen) und Systemeigenschaften sind strikt getrennt.
4.1. Immer verfügbar
Die folgenden Eigenschaften sind immer gefüllt, unabhängig von den Flags des Abfrage- oder Abrufaufrufs:
| Eigenschaft | Typ | Beschreibung |
|---|---|---|
|
|
Numerische ID des Objekts auf dem Server. |
|
|
Anzeigename des Objekts, wie er auf dem Server gespeichert ist. Entspricht in der Regel dem Wert des Schlüsselfeldes. |
|
|
|
|
|
|
|
|
Menge der internen Feldnamen, die seit dem Laden geändert wurden. |
|
|
Menge der Tabellenfeldnamen, bei denen Zeilen hinzugefügt, entfernt oder geändert wurden. |
|
|
Gibt |
4.2. Je nach Objekttyp verfügbar
Einige Systemeigenschaften sind nur bei bestimmten Modelltypen vorhanden:
| Eigenschaft | Typ | Verfügbar bei | Beschreibung |
|---|---|---|---|
|
|
|
ID des übergeordneten Ordners. Wird nur befüllt, wenn |
|
|
|
ID des direkt übergeordneten Registers, wenn das Register in einem anderen Register verschachtelt ist. |
|
|
|
ID des übergeordneten Registers. Wird nur befüllt, wenn |
|
|
|
Typ-ID des übergeordneten Registers. |
4.3. Optional — auf Anfrage geladen
Die folgenden Eigenschaften sind standardmäßig None und müssen über Flags bei select(), get(), insert_and_get() oder update_and_get() explizit angefordert werden.
4.3.1. system.rights
| Attribut | Typ | Beschreibung |
|---|---|---|
|
|
Darf untergeordnete Objekte anlegen (Register oder Dokumente in einem Ordner; Dokumente in einem Register). |
|
|
Darf die Indexfelder des Objekts ändern. |
|
|
Darf die Datei des Dokuments lesen. |
|
|
Darf die Datei des Dokuments ändern. |
|
|
Darf das Objekt löschen. |
ordner = ecm.dms.get(RechnungsOrdner, 12345, rights=True)
if ordner.system.rights.edit_metadata:
print("Bearbeitung erlaubt")
4.3.2. system.base_params
| Attribut | Typ | Beschreibung |
|---|---|---|
|
|
Benutzername des Erstellers. |
|
|
Erstellungszeitpunkt. |
|
|
Aktueller Eigentümer des Objekts. |
|
|
Benutzername des letzten Bearbeiters. |
|
|
Zeitstempel der letzten Änderung. |
|
|
Anzahl der Verknüpfungen auf dieses Objekt. |
|
|
Anzahl der Textnotizen (Bemerkungen) am Objekt. |
ordner = ecm.dms.get(RechnungsOrdner, 12345, base_params=True)
print(f"Erstellt von {ordner.system.base_params.creator} am {ordner.system.base_params.creation_date}")
4.3.3. system.file_properties
Befüllt bei file_properties=True (get()) oder .file_properties() (select()). Nur für ECMDocumentModel relevant. Typ: ECMModelFileProperties.
| Attribut | Typ | Beschreibung |
|---|---|---|
|
|
Anzahl der Dateien (Primär- und Sekundärdateien). |
|
|
Gesamtdateigröße in Bytes. |
|
|
Dateiendung (z. B. |
|
|
MIME-Typ (z. B. |
|
|
MIME-Gruppe (z. B. |
|
|
ID des Dateitypicons. |
|
|
Seitenanzahl, sofern bekannt. |
dok = ecm.dms.get(RechnungsDokument, 42, file_properties=True)
fp = dok.system.file_properties
print(f"{fp.extension}, {fp.size} Bytes, {fp.documentpagecount} Seiten")
4.3.4. system.variants
Befüllt bei variants=True (get()) oder .variants() (select()). Nur für ECMDocumentModel mit aktiviertem W-Modul relevant. Typ: list[ECMModelDocumentVariant].
Jeder Eintrag enthält:
| Attribut | Typ | Beschreibung |
|---|---|---|
|
|
Dokument-ID dieser Variante. |
|
|
Versionsbezeichnung (z. B. |
|
|
|
|
|
ID der übergeordneten Variante, oder |
|
|
IDs der aus dieser Variante abgezweigten Kindvarianten. |
dok = ecm.dms.get(RechnungsDokument, 42, variants=True)
for v in dok.system.variants:
print(v.doc_ver, "✓" if v.is_active else "")
5. Änderungsverfolgung
Modellinstanzen verfolgen Feldänderungen automatisch. Das Zuweisen eines neuen Wertes an ein ECMField markiert das Feld als geändert:
ordner = ecm.dms.get(RechnungsOrdner, 12345)
ordner.Titel = "Aktualisierter Titel"
print(ordner.system.is_modified) # True
print(ordner.system.modified_fields) # {"Titel"}
print(ordner.system.is_field_modified("Jahr")) # False
update() liest system.is_modified, um zu entscheiden, ob der Serveraufruf übersprungen werden kann. Wurden keine Felder geändert und werden keine Dateien übergeben, wird der Aufruf stillschweigend ausgelassen (außer bei force=True).
Das Entfernen einer Zeile aus einem ECMTableField setzt system.has_removed_table_rows = True, wodurch update() REPLACETABLEFIELDS=1 zur Anfrage hinzufügt.
6. Siehe auch
-
select() — Query-Builder mit
.rights(),.base_params(),.file_properties(),.variants(),.remarks() -
get() — Einzelnes Objekt per ID laden mit optionalen Flag-Parametern
-
insert() / insert_and_get() — Neue Objekte anlegen
-
update() / update_and_get() — Änderungen auf dem Server speichern