upsert()
upsert() returns a fluent builder that wraps dms.XMLImport.
The server first searches for an existing object using the conditions configured via .search() and
then performs the configured action based on the number of results.
1. Signature
-
Sync
-
Async
ecm.dms.upsert(
model: T,
folder_id: int | None = None,
register_id: int | None = None,
*,
check_mandatory: bool = True,
) -> ECMModelUpsertSync[T]
ecm.dms.upsert(
model: T,
folder_id: int | None = None,
register_id: int | None = None,
*,
check_mandatory: bool = True,
) -> ECMModelUpsertAsync[T]
2. Parameters
| Parameter | Default | Description |
|---|---|---|
|
— |
Populated model instance (subclass of |
|
|
ID of the parent folder. Only relevant for |
|
|
ID of the parent register. For |
|
|
When |
3. Builder methods
3.1. .search(*conditions)
Defines the search conditions for the server-side duplicate check.
|
Only the |
.search(InvoiceFolder.Title == "Invoice 2024", InvoiceFolder.Year == 2024)
3.2. .action0(value), .action1(value), .action_multiple(value)
Configures the action depending on the number of search results:
| Method | Result count | Default | Valid values |
|---|---|---|---|
|
0 results |
|
|
|
exactly 1 result |
|
|
|
more than 1 result |
|
|
Meaning of action values:
"INSERT"-
Create a new object.
"UPDATE"-
Update the found object with the field values from
model. "NONE"-
Perform no action.
object_idandobject_type_idare-1. "ERROR"-
Abort with an error.
3.3. .files(value, replace=True)
Attaches files to the upsert operation. Only valid for ECMDocumentModel.
| Parameter | Default | Description |
|---|---|---|
|
— |
|
|
|
When |
|
Calling |
3.4. .replace_table_fields(value=True)
Controls how table-field rows are written when the upsert performs an update. By default the server appends the supplied table-field rows to the rows already stored. Call .replace_table_fields(True) to request REPLACETABLEFIELDS=1 instead, so the supplied rows replace the existing ones.
| Parameter | Default | Description |
|---|---|---|
|
|
|
|
The option only takes effect in the update branch of the upsert (the search matches exactly one object and |
3.5. .execute()
Executes the upsert and returns a 4-tuple:
| Element | Type | Description |
|---|---|---|
|
|
ID of the inserted or updated object. |
|
|
Object type ID. |
|
|
Number of objects found by the server-side search. |
|
|
Action actually performed: |
3.6. .execute_and_get(…)
Executes the upsert and then fetches the resulting object in full from the server — the builder
equivalent of insert_and_get() and
update_and_get(). Returns the typed model instance instead of the
(id, type_id, hits, action) tuple. hits and action are discarded; if you need them, use
.execute() and fetch with dms.get() yourself.
.execute_and_get(
*,
rights: bool = False,
base_params: bool = False,
file_properties: bool = False,
variants: bool = False,
use_result_list: bool = False,
fields: list[str | ECMField] | None = None,
) -> T
The parameters are forwarded to dms.get() (see there for details).
|
If the upsert does not yield a usable object ( |
4. Exceptions
ValueError-
A mandatory field has no value and
check_mandatory=True, or a read-only field (ECMField(read_only=…)) is written — see ECM model. TypeError-
.files()was called for a non-ECMDocumentModel. ECMException-
.execute_and_get()was called but the upsert did not yield a usable object (object_id < 0).
5. Examples
5.1. Basic folder upsert
Inserts a folder or updates it if it already exists:
-
Sync
-
Async
object_id, type_id, hits, action = (
ecm.dms.upsert(InvoiceFolder(Title="Invoice 2024", Year=2024))
.search(InvoiceFolder.Title == "Invoice 2024")
.execute()
)
print(action) # "INSERT" or "UPDATE"
object_id, type_id, hits, action = await (
ecm.dms.upsert(InvoiceFolder(Title="Invoice 2024", Year=2024))
.search(InvoiceFolder.Title == "Invoice 2024")
.execute()
)
print(action) # "INSERT" or "UPDATE"
5.2. Insert only, never update
Using action1("NONE") skips the action when an existing object is found:
-
Sync
-
Async
object_id, type_id, hits, action = (
ecm.dms.upsert(InvoiceFolder(Title="Invoice 2024", Year=2024))
.search(InvoiceFolder.Title == "Invoice 2024")
.action1("NONE")
.execute()
)
object_id, type_id, hits, action = await (
ecm.dms.upsert(InvoiceFolder(Title="Invoice 2024", Year=2024))
.search(InvoiceFolder.Title == "Invoice 2024")
.action1("NONE")
.execute()
)
5.3. Register with location
-
Sync
-
Async
object_id, type_id, hits, action = (
ecm.dms.upsert(
InvoiceRegister(Name="2024"),
folder_id=42,
)
.search(InvoiceRegister.Name == "2024")
.execute()
)
object_id, type_id, hits, action = await (
ecm.dms.upsert(
InvoiceRegister(Name="2024"),
folder_id=42,
)
.search(InvoiceRegister.Name == "2024")
.execute()
)
5.4. Document with file attachment
-
Sync
-
Async
from datetime import date
from ecmind_blue_client.rpc import JobRequestFile
object_id, type_id, hits, action = (
ecm.dms.upsert(
InvoiceDocument(Title="Invoice 2024", InvoiceDate=date(2024, 3, 1)),
folder_id=42,
)
.search(InvoiceDocument.Title == "Invoice 2024")
.files([JobRequestFile("invoice.pdf", pdf_bytes)], replace=True)
.execute()
)
from datetime import date
from ecmind_blue_client.rpc import JobRequestFile
object_id, type_id, hits, action = await (
ecm.dms.upsert(
InvoiceDocument(Title="Invoice 2024", InvoiceDate=date(2024, 3, 1)),
folder_id=42,
)
.search(InvoiceDocument.Title == "Invoice 2024")
.files([JobRequestFile("invoice.pdf", pdf_bytes)], replace=True)
.execute()
)
5.5. Disable mandatory field check
-
Sync
-
Async
object_id, type_id, hits, action = (
ecm.dms.upsert(
InvoiceFolder(Year=2024),
check_mandatory=False,
)
.search(InvoiceFolder.Year == 2024)
.execute()
)
object_id, type_id, hits, action = await (
ecm.dms.upsert(
InvoiceFolder(Year=2024),
check_mandatory=False,
)
.search(InvoiceFolder.Year == 2024)
.execute()
)
5.6. Upsert and get the object back directly
Instead of .execute() (tuple), .execute_and_get() returns the fully populated model instance:
-
Sync
-
Async
folder = (
ecm.dms.upsert(InvoiceFolder(Title="Invoice 2024", Year=2024))
.search(InvoiceFolder.Title == "Invoice 2024")
.execute_and_get()
)
print(folder.id, folder.Title)
folder = await (
ecm.dms.upsert(InvoiceFolder(Title="Invoice 2024", Year=2024))
.search(InvoiceFolder.Title == "Invoice 2024")
.execute_and_get()
)
print(folder.id, folder.Title)