update()

Updates an existing object on the server using dms.XMLUpdate. The passed model instance is not mutated.

ECM Model reference — for a full description of system.is_modified, system.modified_fields, system.modified_table_fields, system.has_removed_table_rows, system.requires_table_field_replacement, and how change tracking works.

If no fields have been changed and no files are provided, the server call is skipped by default. Pass force=True to send the request regardless.

If the fully populated instance is needed directly after the update, update_and_get() is the more compact alternative.

1. Signature

  • Sync

  • Async

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

2. Parameters

Name Type Description

model

ECMFolderModel | ECMRegisterModel | ECMDocumentModel

The model instance to update. Must have been loaded from the server so that model.system.id is set.

files

list[JobRequestFile] | None

Optional list of files for the document. Only allowed for ECMDocumentModel — raises TypeError for folder or register models. Default: None.

replace_files

bool

When True (default) and files are provided, the new files replace the existing document files (REPLACEFILES=1). When False, files are appended to the existing ones. Only relevant for ECMDocumentModel when files is provided.

force

bool

When True, the server call is always executed even when no changes are detected. Default: False.

check_mandatory

bool

When True (default), validates client-side that all mandatory=True ECMField descriptors have a value. When False, both client-side and server-side obligation checks are disabled (CHECKOBLIGATION=0).

3. Return value

None

4. Automatic skip

The server call is silently skipped when all of the following conditions are true:

  • force=False (default)

  • model.system.is_modified is False — no fields have been changed

  • files is None

The model tracks field changes automatically (is_modified, modified_fields, modified_table_fields). Only the fields you have actually touched are serialised — untouched fields are left alone and never re-sent to the server.

Table fields are handled separately:

  • When a loaded row was modified, removed, or replaced (i.e. system.requires_table_field_replacement is True), the entire current row set is sent and REPLACETABLEFIELDS=1 is added automatically so the server replaces the table contents.

  • When only new rows were appended, only those new rows are sent — the server’s default APPEND mode keeps the existing rows from being duplicated.

  • Table fields with no changes are omitted from the XML and remain untouched on the server.

5. Writable system fields

System fields (SystemFields enum) are stripped from the dms.XMLUpdate payload by default because the enaio server rejects almost all of them with ECMAccessDeniedException ("The specified system field cannot be changed by the user"). An allowlist WRITABLE_SYSTEM_FIELDS (in ecmind_blue_client.const) names the few that the server actually accepts:

Field Behaviour

OBJECT_FOREIGNID

String field. Insert + update. Holds the reference to the document in the external archive system; together with OBJECT_SYSTEMID it turns a document into a reference document (green arrow when system_id=0). Any string value is allowed — some archive configurations enforce a numeric column on the database side and will coerce non-numeric strings to '0'. Clearable via the field_function="NULL" attribute on the <Field> element.

OBJECT_SYSTEMID

Int field. Update only. On insert the server demands that OBJECT_FOREIGNID is also set (otherwise error -1013, "A system ID was specified but no foreign ID").

OBJECT_USERGUID

String field (owner). Insert + update. The value sent must be the target user’s username (e.g. "ROOT"), not the GUID hex. The server resolves the user and stores the GUID, which is what subsequent reads return. Sending a GUID hex directly fails with error -1019 ("The specified owner could not be determined").

Not available through <Field> but maintained as XML attributes on <Object>/<ObjectType> and controlled through other APIs (e.g. insert(), move()): OBJECT_MAIN (= maintype), OBJECT_CO (= cotype), OBJECT_ID (= object_id), SDSTA_ID/SDREG_ID/FOLDERID/REGISTERID/… (= folder_id/register_id/…). These also return ECMAccessDeniedException when sent as <Field>.

Further writable system fields discovered later can simply be added to WRITABLE_SYSTEM_FIELDS without any model changes.

from ecmind_blue_client import WRITABLE_SYSTEM_FIELDS
# Extend programmatically at run-time (not persisted):
WRITABLE_SYSTEM_FIELDS.add("OBJECT_AVID")

6. Exceptions

ValueError

model.system.id is None (object was not loaded from the server) or check_mandatory=True and a required field is not set, or a read-only field (ECMField(read_only=…)) was changed — see ECM model.

TypeError

files was provided for a folder or register model.

7. Examples

7.1. Simple field update

  • Sync

  • Async

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

7.2. Replace document file

  • Sync

  • Async

from ecmind_blue_client.rpc import JobRequestFileFromPath

doc = ecm.dms.get(InvoiceDocument, 42)
doc.Title = "Invoice No. 42 (corrected)"
ecm.dms.update(
    doc,
    files=[JobRequestFileFromPath("/tmp/invoice_v2.pdf")],
    replace_files=True,  # replace existing files (default)
)
from ecmind_blue_client.rpc import JobRequestFileFromPath

doc = await ecm.dms.get(InvoiceDocument, 42)
doc.Title = "Invoice No. 42 (corrected)"
await ecm.dms.update(
    doc,
    files=[JobRequestFileFromPath("/tmp/invoice_v2.pdf")],
    replace_files=True,
)

7.3. Append file instead of replacing

  • Sync

  • Async

from ecmind_blue_client.rpc import JobRequestFileFromPath

doc = ecm.dms.get(InvoiceDocument, 42)
ecm.dms.update(
    doc,
    files=[JobRequestFileFromPath("/tmp/attachment.pdf")],
    replace_files=False,  # add file, keep existing ones
)
from ecmind_blue_client.rpc import JobRequestFileFromPath

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

7.4. Forcing an update

Useful when external logic requires resubmission even though no fields have changed:

  • Sync

  • Async

folder = ecm.dms.get(InvoiceFolder, 12345)
# no field changes — without force=True the call would be skipped
ecm.dms.update(folder, force=True)
folder = await ecm.dms.get(InvoiceFolder, 12345)
await ecm.dms.update(folder, force=True)

7.5. Disabling mandatory field validation

  • Sync

  • Async

folder = ecm.dms.get(InvoiceFolder, 12345)
folder.Title = None  # clear a mandatory field
# check_mandatory=False: no error, obligation check also disabled server-side
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)

8. See also

  • update_and_get() — combines update() and get() in one call when the updated instance is needed

  • insert() — creates a new object

  • get() — loads a single object from the server