Resolving Directed Acyclic Entity Dependencies in High-Speed Database Seeding

In complex enterprise applications, creating a single target entity (e.g., an InvoiceItem) requires an intricate tree of prerequisite database records: an Organization, an AccountOwner, a Customer, an Invoice, and a ProductCatalogItem.

Attempting to insert records out of order instantly triggers foreign-key constraint violations (PG::ForeignKeyViolation).

Modeling Relational Schemas as Directed Graphs

We can represent database tables as nodes $V$ and foreign-key relationships as directed edges $E$ in a graph $G = (V, E)$:

[ Organization ] ──> [ AccountOwner ] ──> [ Customer ] ──> [ Invoice ] ──> [ InvoiceItem ]

An edge $A \to B$ signifies that Table $B$ holds a foreign key referencing Table $A$. Therefore, Table $A$ must be created before Table $B$.

Topological Sorting via Kahn’s Algorithm

To discover the exact linear creation order without manual hardcoding, execute Kahn’s algorithm on the schema graph:

from collections import deque

def compute_insertion_order(nodes: set, edges: dict) -> list:
    # 1. Calculate in-degree (number of incoming foreign keys) for each node
    in_degree = {node: 0 for node in nodes}
    for u in edges:
        for v in edges[u]:
            in_degree[v] += 1

    # 2. Collect nodes with no dependencies (in-degree 0)
    queue = deque([node for node in nodes if in_degree[node] == 0])
    insertion_order = []

    # 3. Process graph topologically
    while queue:
        curr = queue.popleft()
        insertion_order.append(curr)

        for neighbor in edges.get(curr, []):
            in_degree[neighbor] -= 1
            if in_degree[neighbor] == 0:
                queue.append(neighbor)

    if len(insertion_order) != len(nodes):
        raise ValueError("Circular dependency detected in relational schema!")

    return insertion_order
Calculating insertion orders dynamically at runtime allows test suites to automatically adapt to database migrations, schema additions, and foreign-key refactoring without breaking test setup code.
--