Template blocks & overrides¶
A template is not one big HTML file. It is assembled from blocks -- small,
named pieces of markup plus settings. This page covers the two tables that hold
them: template_blocks (a template's own blocks) and site_block_overrides (a
site's per-element tweaks on top of those blocks), and the precise order in
which the renderer resolves the final HTML and settings for an element.
template_blocks¶
A template_blocks row (model
TemplateBlock) is a block
of markup + settings within a template, keyed by element_type. Besides the
shared CMSBase columns:
| Column | Type | Notes |
|---|---|---|
template_id |
INT FK → templates.id, NOT NULL, indexed |
The owning template. |
element_type |
VARCHAR(128) NOT NULL, indexed | String reference to template_elements.name (not a FK). Which element this block fills. |
html_content |
LONGTEXT, nullable | The block's markup. LONGTEXT because rendered markup can exceed TEXT's 64 KB limit. |
settings |
JSON, nullable | The block's settings. |
description |
TEXT, nullable | Free-text description. |
edited_by |
INT FK → users.id, nullable, indexed |
Edit tracking (AISD-126). |
creator_type |
VARCHAR(64), nullable | String reference to creator_types.name. |
Relationship: template (the owning Template).
site_block_overrides¶
A site_block_overrides row (model
SiteBlockOverride) is
a site-specific override of a template block, keyed by the same
element_type string. It lets one site tweak a shared template without forking
it. Besides CMSBase:
| Column | Type | Notes |
|---|---|---|
site_id |
INT FK → sites.id, NOT NULL, indexed |
The owning site. |
element_type |
VARCHAR(128) NOT NULL, indexed | String reference to template_elements.name -- the element this override applies to. |
html_content |
LONGTEXT, nullable | Override markup (null = inherit). |
settings |
JSON, nullable | Override settings (merged over the block's). |
edited_by |
INT FK → users.id, nullable, indexed |
Edit tracking (AISD-126). |
creator_type |
VARCHAR(64), nullable | String reference to creator_types.name. |
Relationship: site (the owning Site).
The element_type string key¶
Both tables key on element_type, a string that names a row in
template_elements (e.g. header, footer, sidebar, article_card). It is
deliberately not a foreign key, and it is indexed on both tables for the
render-time lookup.
The names come from the template_elements lookup, seeded in
0001_seed_lookups.sql -- page elements (homepage, article, category,
author, 404, the static pages) and component elements (layout, header,
footer, sidebar, article_card, breadcrumb, pagination,
author_card).
Why a string key, not a FK to template_blocks.id? Because an override is
attached to the element name, a site's overrides survive a template
switch. If a site moves from celebrity-v1 to some celebrity-v2, its
override for the footer element still applies, because both templates have a
footer element. A foreign key to a specific block id would point at a row that
belongs to the old template and would dangle the moment the site switched. The
string key follows the schema-wide "string values, lookup tables for admin
validation" decision: admin tooling validates the name against
template_elements, but the database does not enforce it as a FK.
Resolution order (most specific wins)¶
When the renderer needs the final HTML and settings for an element on a given site, it layers three sources, from least to most specific.
HTML¶
template_blocks.html_content -> site_block_overrides.html_content
(template default) (site override, wins)
The template's block markup is the default; a site override for the same
element_type replaces it. Most specific wins.
Settings¶
template_elements.default_settings -> template_blocks.settings -> site_block_overrides.settings
(global element default) (template layer) (site override, wins)
Settings layer in three steps, each overriding the previous:
template_elements.default_settings-- the global default for that element kind.template_blocks.settings-- the template's values for its block.site_block_overrides.settings-- the site's overrides, the most specific layer.
Mental model
Read the chains left to right as "general → specific". The site override is always the last word; where it is absent, the value falls back to the template layer, then to the global element default.
History¶
Both tables have a flat history mirror populated by an AFTER UPDATE trigger:
template_block_history (via trg_template_blocks_history) and
site_block_override_history (via trg_site_block_overrides_history). They
follow the same decoupled-mirror pattern as every other history table -- see
Data model overview.