v0.145.0
 1from __future__ import annotations
 2
 3
 4class OnDelete:
 5    """Sentinel marking an on_delete action.
 6
 7    Each valid action is a single `OnDelete` instance exported from
 8    ``plain.postgres`` (CASCADE, SET_NULL, RESTRICT, NO_ACTION). Cascading
 9    is enforced entirely by Postgres via the corresponding ``ON DELETE``
10    clause — there is no application-level traversal.
11    """
12
13    __slots__ = ("name", "sql_clause", "confdeltype")
14
15    def __init__(self, name: str, sql_clause: str, confdeltype: str) -> None:
16        self.name = name
17        self.sql_clause = sql_clause
18        self.confdeltype = confdeltype
19
20    def __repr__(self) -> str:
21        return f"<plain.postgres.{self.name}>"
22
23
24#: Child rows are deleted by Postgres when the parent is deleted.
25CASCADE = OnDelete("CASCADE", " ON DELETE CASCADE", "c")
26
27#: Child FK columns are set to NULL when the parent is deleted.
28#: Requires ``allow_null=True`` on the field.
29SET_NULL = OnDelete("SET_NULL", " ON DELETE SET NULL", "n")
30
31#: Deleting the parent fails immediately with IntegrityError if children exist.
32#: The check is always immediate — it is not affected by DEFERRABLE.
33RESTRICT = OnDelete("RESTRICT", " ON DELETE RESTRICT", "r")
34
35#: Deleting the parent fails at transaction commit if children exist.
36#: Respects DEFERRABLE INITIALLY DEFERRED, so constraint violations inside a
37#: transaction can be resolved before commit.
38NO_ACTION = OnDelete("NO_ACTION", "", "a")
39
40
41def sql_on_delete(on_delete: OnDelete) -> tuple[str, str]:
42    """Return ``(sql_clause, pg_confdeltype_code)`` for an on_delete value."""
43    if not isinstance(on_delete, OnDelete):
44        raise TypeError(
45            "on_delete must be one of plain.postgres.CASCADE, SET_NULL, "
46            f"RESTRICT, or NO_ACTION; got {on_delete!r}"
47        )
48    return (on_delete.sql_clause, on_delete.confdeltype)