You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
docs: update for Diagram.cascade() classmethod and prune() restrictions
- cascade() is now a classmethod: Diagram.cascade(table_expr)
builds the full cascade diagram in one call, including all
descendants across loaded schemas.
- restrict() is an instance method on schema Diagrams.
- prune() only works on restrict/unrestricted Diagrams, not cascade.
- Remove dry_run references; document safemode workflow instead.
- Update all code examples to new API.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Copy file name to clipboardExpand all lines: src/about/whats-new-22.md
+12-15Lines changed: 12 additions & 15 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -217,15 +217,11 @@ In 2.2, `Table.delete()` and `Table.drop()` use `dj.Diagram` internally to compu
217
217
218
218
### The Preview-Then-Execute Pattern
219
219
220
-
The key benefit of the diagram-level API is the ability to build a cascade explicitly, inspect it, and then execute via `Table.delete()`:
220
+
`Diagram.cascade()` is a class method that builds a complete cascade diagram from a table expression — including all descendants across all loaded schemas — in a single call:
221
221
222
222
```python
223
-
# Build the dependency graph and inspect the cascade
# Execute via Table.delete() after reviewing the blast radius
@@ -238,17 +234,17 @@ This is valuable when working with unfamiliar pipelines, large datasets, or mult
238
234
239
235
The diagram supports two restriction propagation modes designed for fundamentally different tasks.
240
236
241
-
**`cascade()` prepares a delete.** It takes a single restricted table expression, propagates the restriction downstream through all descendants, and **trims the diagram** to the resulting subgraph — ancestors and unrelated tables are removed entirely. Convergence uses OR: a descendant row is marked for deletion if *any* ancestor path reaches it, because if any reason exists to remove a row, it should be removed. `cascade()` is one-shot and is always followed by `counts()` or `delete()`.
237
+
**`Diagram.cascade(table_expr)`** is a class method that creates a cascade diagram for delete. It takes a (possibly restricted) table expression, includes all descendants across loaded schemas, propagates the restriction downstream, and **trims the diagram** to the resulting subgraph — ancestors and unrelated tables are removed entirely. Convergence uses OR: a descendant row is marked for deletion if *any* ancestor path reaches it, because if any reason exists to remove a row, it should be removed.
242
238
243
239
When the cascade encounters a part table whose master is not yet included in the cascade, the behavior depends on the `part_integrity` setting. With `"enforce"` (the default), `delete()` raises an error if part rows would be deleted without their master — preventing orphaned master rows. With `"cascade"`, the restriction propagates *upward* from the part to its master: the restricted part rows identify which master rows are affected, those masters receive a restriction, and that restriction then propagates back downstream to all sibling parts — deleting the entire compositional unit, not just the originally matched part rows.
244
240
245
-
**`restrict()`selects a data subset.** It propagates a restriction downstream but **preserves the full diagram**, allowing `restrict()` to be called again from a different seed table. This makes it possible to build up multi-condition subsets incrementally — for example, restricting by species from one table and by date from another. Convergence uses AND: a descendant row is included only if *all* restricted ancestors match, because an export should contain only rows satisfying every condition. After chaining restrictions, use `prune()` to remove empty tables and `counts()` to inspect the result.
241
+
**`diagram.restrict(table_expr)`** is an instance method that selects a data subset. It propagates a restriction downstream but **preserves the full diagram**, allowing `restrict()` to be called again from a different seed table. This makes it possible to build up multi-condition subsets incrementally — for example, restricting by species from one table and by date from another. Convergence uses AND: a descendant row is included only if *all* restricted ancestors match, because an export should contain only rows satisfying every condition. After chaining restrictions, use `prune()` to remove empty tables and `counts()` to inspect the result.
246
242
247
-
The two modes are mutually exclusive on the same diagram — DataJoint raises an error if you attempt to mix `cascade()` and `restrict()`, or if you call `cascade()` more than once. This prevents accidental mixing of incompatible semantics: a delete diagram should never be reused for subsetting, and vice versa.
243
+
The two modes are mutually exclusive — `restrict()` raises an error if called on a Diagram produced by `cascade()`. This prevents accidental mixing of incompatible semantics: a delete diagram should never be reused for subsetting.
248
244
249
245
### Pruning Empty Tables
250
246
251
-
After applying restrictions, some tables in the diagram may have zero matching rows. The `prune()` method removes these tables from the diagram, leaving only the subgraph with actual data:
247
+
After applying restrictions with `restrict()`, some tables in the diagram may have zero matching rows. The `prune()` method removes these tables from the diagram, leaving only the subgraph with actual data:
252
248
253
249
```python
254
250
export = (dj.Diagram(schema)
@@ -262,6 +258,8 @@ export # visualize the export subgraph
262
258
263
259
Without prior restrictions, `prune()` removes physically empty tables. This is useful for understanding which parts of a pipeline are populated.
264
260
261
+
`prune()` cannot be used on cascade Diagrams — cascade retains all descendant tables to handle concurrent inserts safely (a table empty at cascade time could have rows by the time `delete()` executes).
262
+
265
263
### Restriction Propagation Rules
266
264
267
265
When `cascade()` or `restrict()` propagates a restriction from a parent to a child, one of three rules applies depending on the foreign key relationship:
@@ -287,11 +285,10 @@ With `safemode=True` (the default), `delete()` provides a built-in preview-and-c
287
285
288
286
This is safer than a pre-transaction preview because it reflects the actual database state at delete time, including triggers and concurrent changes.
289
287
290
-
For programmatic preview without executing, use `Diagram` directly:
288
+
For programmatic preview without executing, use `Diagram.cascade()`:
@@ -319,7 +316,7 @@ Each yielded `FreeTable` carries any cascade or restrict conditions that have be
319
316
320
317
### Architecture
321
318
322
-
`Table.delete()`constructs a `Diagram` internally, calls `cascade()` to compute the affected subgraph, then iterates `reversed(diagram)` to delete leaves first. The Diagram is purely a graph computation and inspection tool — it computes the cascade and provides `counts()` and iteration, but all mutation logic (transactions, SQL execution, prompts) lives in `Table.delete()` and `Table.drop()`.
319
+
`Table.delete()`uses `Diagram.cascade(self)`internally to compute the affected subgraph, then iterates `reversed(diagram)` to delete leaves first. `Table.drop()` builds a Diagram with all descendants and drops in the same order. The Diagram is purely a graph computation and inspection tool — it computes the cascade and provides `counts()` and iteration, but all mutation logic (transactions, SQL execution, prompts) lives in `Table.delete()` and `Table.drop()`.
With `safemode=True` (the default), `delete()` provides a built-in safety workflow: it executes the cascade inside a transaction, shows all affected tables and row counts, and asks **"Commit deletes?"** before committing. Declining rolls back all changes.
198
198
199
-
For programmatic preview without executing, or for complex scenarios — working across schemas, chaining multiple restrictions, or visualizing the dependency graph — use `dj.Diagram` to build and inspect the cascade explicitly:
199
+
For programmatic preview without executing, or for complex scenarios — working across schemasor visualizing the dependency graph — use `Diagram.cascade()` to inspect the cascade:
200
200
201
201
```python
202
202
import datajoint as dj
203
203
204
-
# 1. Build the dependency graph and apply cascade restriction
0 commit comments