Skip to content

Add plain postgres models, and stop doubling % in constraint literals - #162

Merged
davegaeddert merged 1 commit into
masterfrom
postgres-models-command
Oct 8, 2026
Merged

davegaeddert merged 1 commit into
masterfrom
postgres-models-command

Conversation

@davegaeddert

Copy link
Copy Markdown
Member

What

plain postgres models describes the data model as the registry sees it, with no database connection: every field, every relation with its target and on_delete rule, the relations pointing back at each model with any declared reverse accessor, and constraints and indexes with their conditions compiled to SQL. --json carries the same facts as data; a target narrows it to a model (class name, label or table) or a package; --all adds the installed packages' models. The same facts are available from plain.postgres.describe.

unique and 1:1 mean effective uniqueness: an unconditional, single-field unique constraint. A partial unique is listed with its WHERE clause and never promoted, so "one open thread per bot" can't be mistaken for "one thread per bot".

quote_value no longer doubles % in string literals. The DDL it feeds runs with no parameters, so the doubled form was stored as written: CheckConstraint(check=~Q(name="100%")) was created as '100%%' and let the one value it names through.

Upgrade path. Existing databases hold the doubled form. Convergence now recognizes a stored definition that differs from the model only by that doubling and replaces the constraint under its own name without staging: a check in one DROP CONSTRAINT, ADD CONSTRAINT ... NOT VALID statement then validated; a unique rebuilt concurrently under a temporary name and swapped in inside one transaction. The table is never without the constraint. A failed replacement blocks sync. Plain indexes already rebuilt automatically.

Known limit: a model edited from a 100%% literal to 100% produces the same stored text and is replaced in place rather than staged under a new name. The validation still has to pass.

Also: Options.unique_field_names, PackageConfig.is_app replacing three prefix checks, a public reversed_field on reverse descriptors, and plain postgres schema sharing the same target matching (it now accepts a package label too).

Verified

  • plain-postgres suite: 1824 passed. plain core: 900 passed. Type checks clean.
  • Run against spur/cloud, HomeQuarter, Ranger, GKB, Plain public and the example app with the branch's plain-postgres, including with an unreachable database URL.
  • Three staged convergence tests reproduce the old DDL through the old quoting and verify check constraints and partial uniques are replaced, while a genuinely changed check still blocks.

…rals

`plain postgres models` describes the data model as the registry sees it,
with no database: every field, every relation with its target and
`on_delete` rule, the relations pointing back at each model with any
declared reverse accessor, and constraints and indexes with their
conditions compiled to SQL. `--json` carries the same facts as data, a
target narrows it to a model (by class name, label or table) or a package,
and `--all` adds the installed packages' models. `unique` and `1:1` mean
effective uniqueness: an unconditional single-field unique constraint,
never a partial one. The same facts are `plain.postgres.describe`.

`quote_value` no longer doubles `%` in a string literal. The DDL it feeds
runs with no parameters, so the doubled form was stored as written: a
`CheckConstraint(check=~Q(name="100%"))` was created as `'100%%'` and let
the one value it names through. Convergence recognizes a stored definition
that only differs by the doubled literal and replaces the constraint under
its own name — a check in one DROP/ADD statement then validated, a unique
rebuilt concurrently under a temporary name and swapped in — instead of
reporting a definition change you have to stage. A model edited from a
`100%%` literal to `100%` looks the same to it and is replaced in place;
the validation still has to pass.

Also: `Options.unique_field_names`, `PackageConfig.is_app` in place of
three prefix checks, `reversed_field` on reverse descriptors, and
`plain postgres schema` sharing the same target matching.
@pullapprove5

pullapprove5 Bot commented Oct 8, 2026

Copy link
Copy Markdown
Contributor
PASS: 1 review scope passed
Scope Progress
✅ code 1/1

View in PullApprove

Next steps:

@davegaeddert
davegaeddert enabled auto-merge (squash) October 8, 2026 20:42
@davegaeddert
davegaeddert merged commit eaca32b into master Oct 8, 2026
8 checks passed
@davegaeddert
davegaeddert deleted the postgres-models-command branch October 8, 2026 20:46
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant