update()

Aktualisiert ein bestehendes Objekt auf dem Server. Intern wird dms.XMLUpdate verwendet. Die übergebene Modell-Instanz wird nicht verändert.

ECM-Modell-Referenz — vollständige Beschreibung von system.is_modified, system.modified_fields, system.modified_table_fields, system.has_removed_table_rows, system.requires_table_field_replacement und der Änderungsverfolgung.

Wurden keine Felder geändert und keine Dateien übergeben, wird der Server-Aufruf standardmäßig übersprungen. Mit force=True kann der Aufruf erzwungen werden.

Wird die aktualisierte Instanz direkt nach dem Speichern benötigt, ist update_and_get() die kompaktere Alternative.

1. Signatur

  • Sync

  • Async

ecm.dms.update(
    model: T,
    files: list[JobRequestFile] | None = None,
    replace_files: bool = True,
    force: bool = False,
    *,
    check_mandatory: bool = True,
    replace_table_fields: bool | None = None,
) -> None
await ecm.dms.update(
    model: T,
    files: list[JobRequestFile] | None = None,
    replace_files: bool = True,
    force: bool = False,
    *,
    check_mandatory: bool = True,
    replace_table_fields: bool | None = None,
) -> None

2. Parameter

Name Typ Beschreibung

model

ECMFolderModel | ECMRegisterModel | ECMDocumentModel

Die zu aktualisierende Modell-Instanz. Muss vom Server geladen worden sein, damit model.system.id gesetzt ist.

files

list[JobRequestFile] | None

Optionale Dateiliste für das Dokument. Nur für ECMDocumentModel erlaubt — bei Ordner- oder Register-Modellen wird TypeError geworfen. Standard: None.

replace_files

bool

Wenn True (Standard) und files übergeben wurden, ersetzen die neuen Dateien die vorhandenen Dokumentdateien (REPLACEFILES=1). Wenn False, werden die Dateien den bestehenden hinzugefügt. Nur wirksam für ECMDocumentModel mit files.

force

bool

Wenn True, wird der Server-Aufruf immer ausgeführt, auch wenn keine Änderungen erkannt wurden. Standard: False.

check_mandatory

bool

Wenn True (Standard), wird clientseitig geprüft, ob alle als mandatory=True deklarierten ECMField-Felder einen Wert haben. Bei False wird die client- und serverseitige Pflichtfeldprüfung deaktiviert (CHECKOBLIGATION=0).

replace_table_fields

bool | None

Überschreibt die automatische Steuerung der Option REPLACETABLEFIELDS=1 für Tabellenfelder. None (Standard) behält die Automatik bei (siehe unten): die Option wird nur gesetzt, wenn die Änderungsverfolgung erkennt, dass geladene Zeilen verändert, entfernt oder ersetzt wurden (model.system.requires_table_field_replacement). True erzwingt die Option auch ohne erkannte Änderung; False unterdrückt sie auch dann, wenn eine Änderung erkannt wurde.

replace_table_fields=False hebelt die Schutzautomatik aus. Wurden geladene Zeilen verändert oder entfernt und die Option unterdrückt, fällt der Server auf sein Anhänge-Verhalten zurück und die Änderung wird möglicherweise nicht wie beabsichtigt persistiert. False nur verwenden, wenn die Konsequenzen bekannt sind.

3. Rückgabewert

None

4. Automatisches Überspringen

Wenn alle der folgenden Bedingungen zutreffen, wird der Server-Aufruf ohne Fehler übersprungen:

  • force=False (Standard)

  • model.system.is_modified ist False — keine Felder wurden geändert

  • files ist None

Das Änderungsprotokoll (is_modified, modified_fields, modified_table_fields) wird vom Modell automatisch beim Setzen von Feldwerten geführt. Nur diese tatsächlich geänderten Felder werden serialisiert — unveränderte Felder bleiben dem Server fern und werden nicht überschrieben.

Tabellenfelder werden differenziert behandelt:

  • Wurde eine geladene Zeile verändert, entfernt oder ersetzt (d. h. system.requires_table_field_replacement ist True), wird das Tabellenfeld komplett neu übertragen und der Server-Aufruf erhält automatisch REPLACETABLEFIELDS=1, sodass der Server die gesamte Tabelle ersetzt.

  • Wurden nur neue Zeilen angefügt, werden ausschließlich diese neuen Zeilen übertragen — der Server-Default „APPEND" verhindert Duplikate der geladenen Zeilen.

  • Tabellenfelder ohne Änderung werden gar nicht in der XML aufgenommen und bleiben serverseitig unangetastet.

Diese Automatik lässt sich über den Parameter replace_table_fields überschreiben: True erzwingt REPLACETABLEFIELDS=1 (auch bei reinem Anhängen), False unterdrückt es (auch bei erkannter Änderung). None (Standard) belässt die oben beschriebene automatische Erkennung.

5. Feld leeren

Wird ein geladenes Feld auf None gesetzt, leert der Client es auf dem Server. Das Feld wird mit field_function="NULL" übertragen, sodass der gespeicherte Wert entfernt und nicht unverändert gelassen wird:

folder = ecm.dms.get(InvoiceFolder, 12345)
folder.Year = None       # zuvor gesetztes Feld zurücksetzen
ecm.dms.update(folder)   # der Server leert das Feld (field_function="NULL")

Die Änderungsverfolgung wertet None nur dann als Änderung, wenn der vom Server geladene Wert nicht None war; ein Leeren wird also genau einmal gesendet, ein bereits leeres Feld nicht erneut. Bei insert() wird ein None-Feld einfach weggelassen — bei einer Neuanlage gibt es nichts zu leeren.

6. Schreibbare Systemfelder

Systemfelder (SystemFields-Enum) werden standardmäßig aus der dms.XMLUpdate-XML herausgefiltert, da der enaio-Server fast alle mit ECMAccessDeniedException („The specified system field cannot be changed by the user") ablehnt. Eine Allowlist WRITABLE_SYSTEM_FIELDS (in ecmind_blue_client.const) listet die wenigen Felder, die der Server tatsächlich akzeptiert:

Feld Verhalten

OBJECT_FOREIGNID

String-Feld. Insert + Update. Hält die Referenz auf das Dokument im externen Archivsystem; in Kombination mit OBJECT_SYSTEMID wird ein Dokument zum Referenzdokument („grüner Pfeil" wenn system_id=0). Beliebige String-Werte zulässig — manche Archiv-Konfigurationen erzwingen serverseitig eine numerische Spalte und casten dann zu '0'. Über das Attribut field_function="NULL" auf dem <Field> clearbar.

OBJECT_SYSTEMID

Int-Feld. Nur Update. Beim Insert verlangt der Server, dass OBJECT_FOREIGNID zusätzlich gesetzt ist (sonst Fehler -1013 „A system ID was specified but no foreign ID").

OBJECT_USERGUID

String-Feld (Owner). Insert + Update. Der Wert muss der Benutzername sein (z. B. "ROOT"), nicht die GUID-Hex. Der Server löst den Benutzer auf und speichert die GUID, die beim nächsten Read zurückkommt. Eine GUID-Hex direkt zu senden schlägt mit Fehler -1019 („The specified owner could not be determined") fehl.

Nicht über <Field> verfügbar, sondern als XML-Attribute auf <Object>/<ObjectType> gepflegt und über andere APIs (z. B. insert(), move()) gesteuert: OBJECT_MAIN (= maintype), OBJECT_CO (= cotype), OBJECT_ID (= object_id), SDSTA_ID/SDREG_ID/FOLDERID/REGISTERID/… (= folder_id/register_id/…). Diese liefern als <Field> ebenfalls ECMAccessDeniedException zurück.

Weitere bislang nicht entdeckte schreibbare Systemfelder können nachträglich in WRITABLE_SYSTEM_FIELDS ergänzt werden, ohne Modelländerungen.

from ecmind_blue_client import WRITABLE_SYSTEM_FIELDS
# Programmatisch erweitern (Run-time; persistiert nicht):
WRITABLE_SYSTEM_FIELDS.add("OBJECT_AVID")

7. Ausnahmen

ValueError

model.system.id ist None (Objekt wurde nicht vom Server geladen) oder check_mandatory=True und ein Pflichtfeld ist nicht gesetzt, oder ein schreibgeschütztes Feld (ECMField(read_only=…)) wurde geändert — siehe ECM-Modell.

TypeError

files wurde für ein Ordner- oder Register-Modell übergeben.

8. Beispiele

8.1. Einfaches Feldupdate

  • Sync

  • Async

folder = ecm.dms.get(InvoiceFolder, 12345)
folder.Title = "Rechnung 2024 (aktualisiert)"
ecm.dms.update(folder)
folder = await ecm.dms.get(InvoiceFolder, 12345)
folder.Title = "Rechnung 2024 (aktualisiert)"
await ecm.dms.update(folder)

8.2. Dokument mit Datei ersetzen

  • Sync

  • Async

from ecmind_blue_client.rpc import JobRequestFileFromPath

doc = ecm.dms.get(InvoiceDocument, 42)
doc.Title = "Rechnung Nr. 42 (korrigiert)"
ecm.dms.update(
    doc,
    files=[JobRequestFileFromPath("/tmp/rechnung_v2.pdf")],
    replace_files=True,  # vorhandene Dateien ersetzen (Standard)
)
from ecmind_blue_client.rpc import JobRequestFileFromPath

doc = await ecm.dms.get(InvoiceDocument, 42)
doc.Title = "Rechnung Nr. 42 (korrigiert)"
await ecm.dms.update(
    doc,
    files=[JobRequestFileFromPath("/tmp/rechnung_v2.pdf")],
    replace_files=True,
)

8.3. Datei anhängen statt ersetzen

  • Sync

  • Async

from ecmind_blue_client.rpc import JobRequestFileFromPath

doc = ecm.dms.get(InvoiceDocument, 42)
ecm.dms.update(
    doc,
    files=[JobRequestFileFromPath("/tmp/anhang.pdf")],
    replace_files=False,  # Datei hinzufügen, bestehende behalten
)
from ecmind_blue_client.rpc import JobRequestFileFromPath

doc = await ecm.dms.get(InvoiceDocument, 42)
await ecm.dms.update(
    doc,
    files=[JobRequestFileFromPath("/tmp/anhang.pdf")],
    replace_files=False,
)

8.4. Update erzwingen

Nützlich, wenn externe Logik eine Neuübermittlung erfordert, obwohl keine Felder geändert wurden:

  • Sync

  • Async

folder = ecm.dms.get(InvoiceFolder, 12345)
# keine Feldänderung — ohne force=True würde der Aufruf übersprungen
ecm.dms.update(folder, force=True)
folder = await ecm.dms.get(InvoiceFolder, 12345)
await ecm.dms.update(folder, force=True)

8.5. Pflichtfeldprüfung deaktivieren

  • Sync

  • Async

folder = ecm.dms.get(InvoiceFolder, 12345)
folder.Title = None  # Pflichtfeld leeren
# check_mandatory=False: kein Fehler, Pflichtfeldprüfung auch serverseitig deaktiviert
ecm.dms.update(folder, check_mandatory=False)
folder = await ecm.dms.get(InvoiceFolder, 12345)
folder.Title = None
await ecm.dms.update(folder, check_mandatory=False)

9. Siehe auch

  • update_and_get() — kombiniert update() und get() in einem Aufruf, wenn die aktualisierte Instanz benötigt wird

  • insert() — legt ein neues Objekt an

  • get() — lädt ein einzelnes Objekt vom Server