What this is about
An object rarely stands alone. A company has employees, an invoice has items, an employee belongs to a department. When you delete an object, CDMS walks through each of its relations and decides for every connected object:
- delete along if the relation has the flag
DELETE. The connected object is then a dependent child: it only lives together with its parent. - unlink if the relation has no
DELETE. The connected object stays, only the reference to the deleted object disappears.
A child that is deleted along is treated exactly like the object itself: its relations are walked through as well. This way a delete can reach across many levels. This chain reaction is called a cascade.
An example as a tree
A company modeled like this:
| Field | Type | Flag DELETE |
|---|---|---|
Company.employees | 1:n to employee | yes |
Employee.phones | 1:n to phone number | yes |
Company.logo | 1:1 to a file model | yes |
Company.mainAddress | 1:1 to address | no |
Employee.department | n:1 to department | no |
DELETE /company/delete/c4… works like this:
flowchart TB
C["Company CodamIC"]:::weg --> E1["Employee Anna"]:::weg
C --> E2["Employee Ben"]:::weg
C --> L["Logo (file)"]:::weg
C -.-> A["Address Wiesbaden"]:::bleibt
E1 --> P1["Phone 0611-1"]:::weg
E2 --> P2["Phone 0611-2"]:::weg
E2 --> P3["Phone 0170-3"]:::weg
E1 -.-> D["Department IT"]:::bleibt
E2 -.-> D
classDef weg fill:#fdecea,stroke:#c62828,color:#8e0000
classDef bleibt fill:#e8f5e9,stroke:#2e7d32,color:#1b5e20
Red is deleted, green stays. Solid lines are relations with the DELETE flag, dashed lines are relations without it.
- Anna, Ben and their phone numbers are deleted, across two levels.
- The logo is deleted, including the file content in storage. See Deleting file models.
- The address stays. It only loses its reference to the company.
- The IT department stays. Anna and Ben disappear from its list
employees.
Unlink or delete along, per relation type
| Relation on X | Flag DELETE | Result |
|---|---|---|
| 1:1 | yes | the child is deleted |
| 1:1 | no | the child stays, its reference to X is cleared |
| 1:n (list) | yes | all children in the list are deleted |
| 1:n (list) | no | all children stay, their reference to X is cleared |
| n:1 (X points to a parent) | no | the parent stays, X disappears from its list |
| n:1 (X points to a parent) | yes | the parent is deleted |
| n:m | no | the connections disappear, the partners stay |
| n:m | yes | as without the flag: the connections disappear, the partners stay |
With a join model (n:m with its own fields, such as Group2User) there are two 1:n relations. If User.groups has the DELETE flag, CDMS deletes the user’s join rows along with the user. The groups stay, because Group2User.group has no DELETE flag. See Many-to-many through a join table.
Unlinking in detail
Unlinking means: on the connected object, CDMS clears the reference back to the deleted object and saves it. The connected object itself stays, with all its other fields.
- the child disappears together with its own dependent children
- needs the delete role of the child model
- DELETE hooks of the child run
- file content of the child is removed
- the object stays, only the reference becomes empty
- no role of the connected model needed
- no DELETE hooks
- files stay untouched
Because unlinking is not a delete, you need no role on the connected model for it. You may delete an employee with employee-delete, even without any role on the department. The department stays and no longer has the employee in employees.
Roles in the cascade
CDMS checks the delete role for every row that disappears, against the model of that row. To delete a company together with its employees, you need company-delete and employee-delete.
When: company-delete and employee-delete
-
1CDMScompany:
company-deletepresent -
2CDMSevery employee:
employee-deletepresent -
3CDMS→Databasedeletes company and employees
Result: 200
When: only company-delete, the company has one employee
-
1CDMScompany:
company-deletepresent -
2CDMSemployee:
employee-deletemissing -
3CDMS→Client403
missing-permission|employee-delete, the transaction is rolled back
Result: Company and employee are still there, unchanged. There is no "half deleted".
When: The relation Vault.notes has its own delete role vault-notes-delete
-
1CDMSvault:
vault-deletepresent -
2CDMSevery note:
vault-notes-deleteornote-deletepresent? -
3CDMS→Databasedeletes vault and notes
Result: The field role allows deleting the notes only along this path. Deleting a note directly through /note/delete/{id} still requires note-delete. If both are missing, the error names the field role: 403 missing-permission|vault-notes-delete.
A field role applies to one level only. If a note has dependent children of its own, each of them again needs the role of its model or a field role on the note’s relation. See Permissions on relations (field roles).
Pitfalls
What comes next
- The steps of a DELETE: How a DELETE runs
- Children that drop out of a list through PUT or PATCH: Deleting by changing
- The flags themselves: Relation types and recursive flags