CMS Data Model & Rendering¶
This site documents the CMS template / page schema and the P1
data-driven rendering model for the custom CMS (epic AISD-36). It is the
companion reference to the SQLAlchemy models in hq/sql_models/ -- the docs
live next to the code they describe so the two stay in step.
It explains two things that work together:
- The data model -- the database tables that store a template, its blocks, its per-page descriptors, and the CSS that styles it.
- The rendering model -- how a single generic worker turns a row in the
page_typestable into a fully rendered web page, with no page-specific code.
Who this is for¶
- Engineers working on the CMS schema, the cms-api service, or the rendering worker -- the Model API reference is generated straight from the SQLAlchemy docstrings, so it never drifts from the code.
- Editors, product, and anyone non-technical who needs to understand how a page is described in data. The sources / bind / call / params explained page is written in plain English and walks real, seeded examples line by line.
The single source of truth: the models¶
There is no Alembic, no hand-written CREATE TABLE. The SQLAlchemy models
in hq/sql_models/ are the single source of truth for the schema. The
bootstrap builds the tables with Base.metadata.create_all(), so every column,
foreign key, index, and unique constraint is declared in Python (template.py,
core.py, lookup.py, ...) and emitted from that metadata.
The .sql files under hq/sql_models/ddl/ carry only the post-create steps
that create_all cannot express -- guarded ALTERs for existing databases,
the AFTER UPDATE history triggers, and the seed rows. Those seed rows are the
real, shipped data this site quotes verbatim (the celebrity-v1 template's
page descriptors live in 0015_page_types_and_css_version.sql).
What changed in P1¶
Before P1, the rendering worker carried hardcoded routing (router.ts) and one
orchestrator file per page kind (pages/*.ts). P1 moves all of that into the
database: a row in page_types now describes a page -- its routes, its
layout, and the API calls that supply its data -- and the worker runs one
generic loop over those descriptors:
match route → fetch sources → build → wrap layout
Adding or changing a page is now a data edit, not a code deploy.
Task lineage¶
- AISD-36 -- the CMS epic (the SQL-model track that introduced these tables).
- AISD-131 -- the per-repository documentation-portal epic. Phase 1 is content + a local strict build only; hosting and CI are Phase 2.
- AISD-133 -- this documentation package (Phase 1 of AISD-131).
- AISD-85 -- the template table group (
templates,template_blocks,site_block_overrides). - AISD-126 -- the edit-tracking columns (
edited_by/creator_type) and the history-mirror trigger pattern. - AISD P1.a / P1.b -- the
templates.css/css_versioncache-bust pointer and thepage_typesdata-driven descriptors.
Where to go next¶
| Page | What it covers |
|---|---|
| Data model overview | The four live tables, their history mirrors, the shared CMSBase columns, the cms_db schema, and the foreign-key relationships. |
| Templates & CSS in the DB | The templates table, and why the full stylesheet lives in templates.css with a css_version cache-bust pointer. |
| Template blocks & overrides | template_blocks and site_block_overrides, the element_type string key, and the HTML / settings resolution order. |
| Page types (data-driven pages) | Every page_types column, the (template_id, type, variant) unique key, and the real seeded celebrity-v1 descriptors. |
| sources / bind / call / params explained | The plain-English core: the generic render loop and a line-by-line walk of three real sources descriptors. |
| Model API reference | Auto-generated class + column reference for hq.sql_models.template. |