Django CMS · Schema

Django CMS Page Content

JSON Schema for a Django CMS page content object as returned by the djangocms-rest API

CMSContent ManagementDjangoPythonHeadless CMSREST APIOpen SourcePagesPluginsPlaceholders

Properties

Name Type Description
title string Page title
page_title string Page title used in the HTML tag; falls back to title</td> </tr> <tr> <td><span class="prop-name">menu_title</span></td> <td><span class="prop-type">string</span></td> <td>Title displayed in navigation menus; falls back to title</td> </tr> <tr> <td><span class="prop-name">meta_description</span></td> <td><span class="prop-type">string</span></td> <td>SEO meta description for the page</td> </tr> <tr> <td><span class="prop-name">redirect</span></td> <td><span class="prop-type">stringnull</span></td> <td>Redirect URL if configured; null when no redirect is set</td> </tr> <tr> <td><span class="prop-name">absolute_url</span></td> <td><span class="prop-type">string</span></td> <td>Absolute frontend URL for the page</td> </tr> <tr> <td><span class="prop-name">path</span></td> <td><span class="prop-type">string</span></td> <td>Relative URL path of the page (e.g. 'blog/my-post')</td> </tr> <tr> <td><span class="prop-name">details</span></td> <td><span class="prop-type">string</span></td> <td>API endpoint URL for this page</td> </tr> <tr> <td><span class="prop-name">is_home</span></td> <td><span class="prop-type">boolean</span></td> <td>Whether this is the home/root page</td> </tr> <tr> <td><span class="prop-name">login_required</span></td> <td><span class="prop-type">boolean</span></td> <td>Whether the page requires authentication to view</td> </tr> <tr> <td><span class="prop-name">in_navigation</span></td> <td><span class="prop-type">boolean</span></td> <td>Whether the page appears in navigation menus</td> </tr> <tr> <td><span class="prop-name">soft_root</span></td> <td><span class="prop-type">boolean</span></td> <td>Whether the page acts as a soft root for navigation</td> </tr> <tr> <td><span class="prop-name">template</span></td> <td><span class="prop-type">string</span></td> <td>Template file used to render this page</td> </tr> <tr> <td><span class="prop-name">xframe_options</span></td> <td><span class="prop-type">string</span></td> <td>X-Frame-Options header value for this page</td> </tr> <tr> <td><span class="prop-name">limit_visibility_in_menu</span></td> <td><span class="prop-type">booleannull</span></td> <td>Whether menu visibility is restricted by user auth state</td> </tr> <tr> <td><span class="prop-name">language</span></td> <td><span class="prop-type">string</span></td> <td>Language code for this page content (e.g. 'en', 'de')</td> </tr> <tr> <td><span class="prop-name">languages</span></td> <td><span class="prop-type">array</span></td> <td>All languages this page is available in</td> </tr> <tr> <td><span class="prop-name">is_preview</span></td> <td><span class="prop-type">boolean</span></td> <td>Whether this response was served in preview/draft mode</td> </tr> <tr> <td><span class="prop-name">application_namespace</span></td> <td><span class="prop-type">stringnull</span></td> <td>Application namespace if the page is an apphook; null otherwise</td> </tr> <tr> <td><span class="prop-name">creation_date</span></td> <td><span class="prop-type">string</span></td> <td>ISO 8601 timestamp when the page was first created</td> </tr> <tr> <td><span class="prop-name">changed_date</span></td> <td><span class="prop-type">string</span></td> <td>ISO 8601 timestamp when the page was last modified</td> </tr> <tr> <td><span class="prop-name">placeholders</span></td> <td><span class="prop-type">array</span></td> <td>Ordered list of placeholders defined by the page template</td> </tr> </tbody> </table> </div> <div style="margin-top: 2rem;"> <a href="https://github.com/api-evangelist/django-cms/blob/main/json-schema/djangocms-page-content-schema.json" target="_blank" rel="noopener" class="btn btn-outline-dark btn-sm">View JSON Schema on GitHub</a> </div> <link rel="stylesheet" href="/assets/prism/prism-coy.min.css"> <link rel="stylesheet" href="/assets/prism/prism-line-numbers.min.css"> <style> .src-widget { background: #fdfdfd; border: 1px solid #e3e7ee; border-radius: 10px; overflow: hidden; margin-top: 0.75rem; } .src-widget-head { display: flex; align-items: center; gap: 0.5rem; padding: 0.55rem 0.85rem; background: #f8fafc; border-bottom: 1px solid #e3e7ee; flex-wrap: wrap; } .src-label { color: #475569; font-weight: 600; font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace; font-size: 0.8rem; flex: 0 0 auto; } .src-picker { flex: 0 0 auto; max-width: 360px; padding: 0.3em 0.55em; border: 1px solid #cbd5e1; border-radius: 4px; background: #fff; font-size: 0.78rem; color: #1a1a2e; font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace; cursor: pointer; } .src-picker:focus { outline: none; border-color: #1e40af; box-shadow: 0 0 0 2px rgba(30, 64, 175, 0.15); } .src-loading { color: #94a3b8; font-style: italic; padding: 1rem; } .src-search { flex: 1 1 200px; min-width: 140px; } .src-search input { width: 100%; padding: 0.3em 0.6em; border: 1px solid #cbd5e1; border-radius: 4px; font-size: 0.8rem; background: #fff; color: #1a1a2e; } .src-search input:focus { outline: none; border-color: #1e40af; box-shadow: 0 0 0 2px rgba(30, 64, 175, 0.15); } .src-actions { display: flex; gap: 0.25rem; flex: 0 0 auto; } .src-actions a, .src-actions button { background: none; border: 1px solid transparent; padding: 0.25em 0.6em; color: #1e40af; font-size: 0.78rem; font-weight: 500; cursor: pointer; text-decoration: none; border-radius: 4px; line-height: 1.4; } .src-actions a:hover, .src-actions button:hover { background: #eef2f7; } .src-actions button.active { background: #dbeafe; border-color: #93c5fd; } .src-actions button:disabled { color: #94a3b8; cursor: not-allowed; } .src-actions button:disabled:hover { background: none; } .src-widget pre[class*="language-"] { margin: 0; padding: 1rem 0; background: #fdfdfd; box-shadow: none; border: none; max-height: 540px; overflow: auto; font-size: 0.82rem; line-height: 1.5; } .src-widget pre[class*="language-"] > code { font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace; padding-right: 1rem; } .src-widget pre[class*="language-"]::before, .src-widget pre[class*="language-"]::after { display: none; } .src-widget pre.line-numbers { padding-left: 3.5em; counter-reset: linenumber; position: relative; } .src-widget pre.line-numbers > code { position: relative; white-space: inherit; } .src-widget pre.line-numbers .line-numbers-rows { position: absolute; pointer-events: none; top: 1rem; font-size: 100%; left: -3em; width: 2.7em; letter-spacing: -1px; border-right: 1px solid #e3e7ee; user-select: none; } .src-widget pre.line-numbers .line-numbers-rows > span { display: block; counter-increment: linenumber; } .src-widget pre.line-numbers .line-numbers-rows > span:before { content: counter(linenumber); color: #cbd5e1; display: block; padding-right: 0.7em; text-align: right; font-size: 0.78rem; } .src-widget .token.key, .src-widget .token.property, .src-widget .token.atrule { color: #1e40af; font-weight: 600; } .src-widget .token.string { color: #047857; } .src-widget .token.number, .src-widget .token.boolean, .src-widget .token.null { color: #b45309; } .src-widget .token.comment { color: #94a3b8; font-style: italic; } .src-widget .token.punctuation, .src-widget .token.operator { color: #64748b; } .src-widget .token.tag { color: #7c3aed; } .src-widget .token.anchor, .src-widget .token.alias { color: #be185d; } .src-widget mark.src-hit { background: #fef08a; color: inherit; padding: 0; border-radius: 2px; } .src-widget mark.src-hit-active { background: #fb923c; color: #fff; } .src-copied { color: #047857 !important; } .src-empty { padding: 2rem 1rem; text-align: center; color: #94a3b8; font-size: 0.85rem; } </style> <h3 class="section-title" id="source">JSON Schema</h3> <div class="src-widget" data-src-widget data-original-lang="json" data-current-lang="json" data-original-filename="djangocms-page-content-schema.json"> <div class="src-widget-head"> <span class="src-label" data-src-filename>djangocms-page-content-schema.json</span> <span class="src-search"> <input type="search" data-src-search placeholder="Search in source… (n / N to navigate)" aria-label="Search source"> </span> <span class="src-actions"> <button type="button" data-src-toggle data-target="yaml" aria-label="View as YAML">YAML</button> <button type="button" data-src-toggle data-target="json" aria-label="View as JSON">JSON</button> <button type="button" data-src-download aria-label="Download source">Download</button> <a data-src-raw href="https://raw.githubusercontent.com/api-evangelist/django-cms/refs/heads/main/json-schema/djangocms-page-content-schema.json" target="_blank" rel="noopener">Raw ↑</a> <button type="button" data-src-copy>Copy</button> </span> </div> <pre class="line-numbers language-json"><code class="language-json" data-src-code>{ "$schema": "http://json-schema.org/draft-07/schema#", "$id": "https://raw.githubusercontent.com/api-evangelist/django-cms/main/json-schema/djangocms-page-content-schema.json", "title": "Django CMS Page Content", "description": "JSON Schema for a Django CMS page content object as returned by the djangocms-rest API", "type": "object", "properties": { "title": { "type": "string", "maxLength": 255, "description": "Page title" }, "page_title": { "type": "string", "maxLength": 255, "description": "Page title used in the HTML <title> tag; falls back to title" }, "menu_title": { "type": "string", "maxLength": 255, "description": "Title displayed in navigation menus; falls back to title" }, "meta_description": { "type": "string", "description": "SEO meta description for the page" }, "redirect": { "type": ["string", "null"], "maxLength": 2048, "description": "Redirect URL if configured; null when no redirect is set" }, "absolute_url": { "type": "string", "format": "uri", "description": "Absolute frontend URL for the page" }, "path": { "type": "string", "maxLength": 200, "description": "Relative URL path of the page (e.g. 'blog/my-post')" }, "details": { "type": "string", "format": "uri", "description": "API endpoint URL for this page" }, "is_home": { "type": "boolean", "description": "Whether this is the home/root page" }, "login_required": { "type": "boolean", "description": "Whether the page requires authentication to view" }, "in_navigation": { "type": "boolean", "description": "Whether the page appears in navigation menus" }, "soft_root": { "type": "boolean", "description": "Whether the page acts as a soft root for navigation" }, "template": { "type": "string", "maxLength": 100, "description": "Template file used to render this page" }, "xframe_options": { "type": "string", "maxLength": 50, "description": "X-Frame-Options header value for this page" }, "limit_visibility_in_menu": { "type": ["boolean", "null"], "default": false, "description": "Whether menu visibility is restricted by user auth state" }, "language": { "type": "string", "maxLength": 10, "description": "Language code for this page content (e.g. 'en', 'de')", "examples": ["en", "de", "fr", "zh-cn"] }, "languages": { "type": "array", "items": { "type": "string", "maxLength": 10 }, "description": "All languages this page is available in" }, "is_preview": { "type": "boolean", "default": false, "description": "Whether this response was served in preview/draft mode" }, "application_namespace": { "type": ["string", "null"], "maxLength": 200, "description": "Application namespace if the page is an apphook; null otherwise" }, "creation_date": { "type": "string", "format": "date-time", "description": "ISO 8601 timestamp when the page was first created" }, "changed_date": { "type": "string", "format": "date-time", "description": "ISO 8601 timestamp when the page was last modified" }, "placeholders": { "type": "array", "items": { "$ref": "#/definitions/Placeholder" }, "description": "Ordered list of placeholders defined by the page template" } }, "required": [ "title", "page_title", "menu_title", "meta_description", "absolute_url", "path", "details", "is_home", "login_required", "in_navigation", "soft_root", "template", "language", "languages", "creation_date", "changed_date" ], "definitions": { "Placeholder": { "type": "object", "title": "Placeholder", "description": "A CMS placeholder containing structured plugin content for a given slot", "properties": { "slot": { "type": "string", "description": "Template slot name identifying this placeholder" }, "label": { "type": "string", "description": "Human-readable label for the placeholder" }, "language": { "type": "string", "description": "Language code of the placeholder content" }, "content": { "type": "array", "items": { "$ref": "#/definitions/PluginInstance" }, "description": "Ordered list of serialized plugin data objects" }, "details": { "type": "string", "format": "uri", "description": "Direct API URL for this placeholder" }, "html": { "type": "string", "default": "", "description": "Server-rendered HTML for the placeholder (only present when ?html=1 is requested)" } }, "required": ["slot", "label", "language", "details"] }, "PluginInstance": { "type": "object", "title": "PluginInstance", "description": "A serialized CMS plugin instance within a placeholder", "properties": { "plugin_type": { "type": "string", "description": "Plugin class name (e.g. 'TextPlugin', 'PicturePlugin')" }, "pk": { "type": "integer", "description": "Primary key of the plugin instance" }, "parent_plugin_type": { "type": ["string", "null"], "description": "Parent plugin type if this is a child plugin; null for root plugins" } }, "required": ["plugin_type"], "additionalProperties": true } } } </code></pre> </div> <script src="/assets/prism/prism-core.min.js" defer></script> <script src="/assets/prism/prism-yaml.min.js" defer></script> <script src="/assets/prism/prism-json.min.js" defer></script> <script src="/assets/prism/prism-line-numbers.min.js" defer></script> <script src="/assets/prism/js-yaml.min.js" defer></script> <script> (function () { if (window.__srcWidgetInit) return; window.__srcWidgetInit = true; function initWidget(widget) { var codeEl = widget.querySelector('[data-src-code]'); var preEl = widget.querySelector('pre'); var search = widget.querySelector('[data-src-search]'); var toggles = widget.querySelectorAll('[data-src-toggle]'); var downloadBtn = widget.querySelector('[data-src-download]'); var copyBtn = widget.querySelector('[data-src-copy]'); var fileLabel = widget.querySelector('[data-src-filename]'); var picker = widget.querySelector('[data-src-picker]'); var rawLink = widget.querySelector('[data-src-raw]'); var headingEl = widget.previousElementSibling && widget.previousElementSibling.tagName === 'H3' ? widget.previousElementSibling : null; var fetchedCache = {}; // Original raw text — we always re-render from this so toggles + searches don't compound. var originalLang = widget.dataset.originalLang; var originalText = codeEl.textContent; var originalFilename = widget.dataset.originalFilename; var currentLang = originalLang; var currentText = originalText; var searchHits = []; var activeHit = -1; function highlight() { if (window.Prism) window.Prism.highlightElement(codeEl); } function setActiveToggle() { toggles.forEach(function (b) { if (b.dataset.target === currentLang) b.classList.add('active'); else b.classList.remove('active'); }); } function updateFilenameForLang() { if (!fileLabel) return; var base = (currentSourceFilename || originalFilename).replace(/\.(ya?ml|json)$/i, ''); fileLabel.textContent = base + '.' + currentLang; } var currentSourceFilename = originalFilename; var currentSourceUrl = null; if (rawLink) currentSourceUrl = rawLink.getAttribute('href'); function setLangContent(lang, text) { currentLang = lang; currentText = text; codeEl.textContent = text; codeEl.className = 'language-' + lang; preEl.className = 'line-numbers language-' + lang; highlight(); setActiveToggle(); updateFilenameForLang(); runSearch(); } function tryConvert(targetLang) { if (targetLang === currentLang) return; if (!window.jsyaml) return; try { var parsed; if (currentLang === 'yaml') parsed = window.jsyaml.load(currentText); else parsed = JSON.parse(currentText); var out; if (targetLang === 'yaml') { out = window.jsyaml.dump(parsed, { indent: 2, lineWidth: 100, noRefs: true }); } else { out = JSON.stringify(parsed, null, 2); } setLangContent(targetLang, out); } catch (err) { alert('Cannot convert: source is not valid ' + currentLang.toUpperCase() + '.\n' + err.message); } } // ---- Search highlighting (DOM walker over text nodes inside <code>) ---- function clearSearch() { widget.querySelectorAll('mark.src-hit').forEach(function (m) { var parent = m.parentNode; while (m.firstChild) parent.insertBefore(m.firstChild, m); parent.removeChild(m); parent.normalize(); }); searchHits = []; activeHit = -1; } function runSearch() { clearSearch(); var q = (search.value || '').trim(); if (q.length < 2) return; var qLower = q.toLowerCase(); var walker = document.createTreeWalker(codeEl, NodeFilter.SHOW_TEXT, null); var nodes = []; var n; while ((n = walker.nextNode())) nodes.push(n); nodes.forEach(function (node) { var text = node.nodeValue; var lower = text.toLowerCase(); var idx = lower.indexOf(qLower); if (idx === -1) return; var frag = document.createDocumentFragment(); var cursor = 0; while (idx !== -1) { if (idx > cursor) frag.appendChild(document.createTextNode(text.slice(cursor, idx))); var mark = document.createElement('mark'); mark.className = 'src-hit'; mark.textContent = text.slice(idx, idx + q.length); frag.appendChild(mark); searchHits.push(mark); cursor = idx + q.length; idx = lower.indexOf(qLower, cursor); } if (cursor < text.length) frag.appendChild(document.createTextNode(text.slice(cursor))); node.parentNode.replaceChild(frag, node); }); if (searchHits.length) jumpToHit(0); } function jumpToHit(i) { if (!searchHits.length) return; if (activeHit >= 0 && searchHits[activeHit]) searchHits[activeHit].classList.remove('src-hit-active'); activeHit = ((i % searchHits.length) + searchHits.length) % searchHits.length; var el = searchHits[activeHit]; el.classList.add('src-hit-active'); el.scrollIntoView({ block: 'center', behavior: 'smooth' }); } // ---- Wire up ---- var debounce; search.addEventListener('input', function () { clearTimeout(debounce); debounce = setTimeout(runSearch, 100); }); search.addEventListener('keydown', function (ev) { if (ev.key === 'Enter' || ev.key === 'n') { ev.preventDefault(); jumpToHit(activeHit + 1); } else if (ev.key === 'N' || (ev.key === 'p')) { ev.preventDefault(); jumpToHit(activeHit - 1); } else if (ev.key === 'Escape') { search.value = ''; runSearch(); } }); toggles.forEach(function (b) { b.addEventListener('click', function () { tryConvert(b.dataset.target); }); }); downloadBtn.addEventListener('click', function () { var blob = new Blob([codeEl.textContent], { type: currentLang === 'json' ? 'application/json' : 'application/yaml' }); var url = URL.createObjectURL(blob); var a = document.createElement('a'); a.href = url; a.download = fileLabel.textContent; document.body.appendChild(a); a.click(); document.body.removeChild(a); setTimeout(function () { URL.revokeObjectURL(url); }, 0); }); copyBtn.addEventListener('click', function () { navigator.clipboard.writeText(codeEl.textContent).then(function () { var orig = copyBtn.textContent; copyBtn.textContent = 'Copied'; copyBtn.classList.add('src-copied'); setTimeout(function () { copyBtn.textContent = orig; copyBtn.classList.remove('src-copied'); }, 1500); }); }); // ---- Picker (provider pages with multiple specs) ---- function applySource(text, lang, filename, url, heading) { currentSourceFilename = filename || originalFilename; currentSourceUrl = url || null; if (rawLink) { if (url) { rawLink.setAttribute('href', url); rawLink.style.display = ''; } else { rawLink.style.display = 'none'; } } if (headingEl && heading) headingEl.textContent = heading; setLangContent(lang, text); } if (picker) { picker.addEventListener('change', function () { var opt = picker.options[picker.selectedIndex]; var url = opt.dataset.url; var fmt = opt.dataset.format || 'yaml'; var fname = opt.dataset.filename || ''; var heading = opt.dataset.heading || ''; if (!url) { // Original embedded source applySource(originalText, originalLang, originalFilename, picker.options[0].dataset.url || null, picker.options[0].dataset.heading || ''); return; } if (fetchedCache[url]) { applySource(fetchedCache[url], fmt, fname, url, heading); return; } codeEl.textContent = 'Loading ' + fname + '…'; codeEl.className = ''; preEl.className = ''; fetch(url).then(function (r) { if (!r.ok) throw new Error('HTTP ' + r.status); return r.text(); }).then(function (text) { fetchedCache[url] = text; applySource(text, fmt, fname, url, heading); }).catch(function (err) { codeEl.textContent = 'Could not load: ' + err.message; }); }); // Stash the default option's url so we can restore "Raw" link when reset. if (rawLink && picker.options[0]) { picker.options[0].dataset.url = rawLink.getAttribute('href') || ''; picker.options[0].dataset.heading = (headingEl ? headingEl.textContent : '') || ''; } } // Auto-fetch when initial source is empty but a remote URL is available. // This happens when the build found an external OpenAPI URL but didn't // inline the content — the widget fetches it on page load instead. if (!originalText.trim() && currentSourceUrl) { var remoteUrl = currentSourceUrl; codeEl.textContent = 'Loading…'; codeEl.className = ''; preEl.className = 'line-numbers language-' + originalLang; fetch(remoteUrl) .then(function (r) { if (!r.ok) throw new Error('HTTP ' + r.status); return r.text(); }) .then(function (text) { var ext = remoteUrl.replace(/\?.*/, '').split('.').pop().toLowerCase(); var fmt = ext === 'json' ? 'json' : 'yaml'; originalText = text; originalLang = fmt; currentLang = fmt; widget.dataset.originalLang = fmt; applySource(text, fmt, originalFilename, remoteUrl, headingEl ? headingEl.textContent : ''); }) .catch(function (err) { codeEl.textContent = 'Could not load spec: ' + err.message; }); } // Initial state setActiveToggle(); // Wait for Prism to load before highlighting / line-numbers wiring. var tries = 0; var waitPrism = setInterval(function () { if (window.Prism && window.Prism.plugins) { clearInterval(waitPrism); highlight(); } else if (++tries > 80) { clearInterval(waitPrism); } }, 50); } function initAll() { document.querySelectorAll('[data-src-widget]').forEach(initWidget); } if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', initAll); } else { initAll(); } })(); </script> <script type="application/ld+json"> { "@context": "https://schema.org", "@type": "BreadcrumbList", "itemListElement": [ { "@type": "ListItem", "position": 1, "name": "Home", "item": "https://apis.io/" }, { "@type": "ListItem", "position": 2, "name": "Django CMS", "item": "https://providers.apis.io/providers/django-cms/" }, { "@type": "ListItem", "position": 3, "name": "Django CMS Page Content" } ] } </script> </main> <footer class="footer"> <div class="container"> <div class="d-flex justify-content-center gap-4 align-items-center" style="height: 60px;"> <a href="http://apisjson.org">APIs.json</a> <a href="http://apicommons.org">API Commons</a> <a href="https://developer.apis.io">Developers</a> <a href="https://github.com/api-search">GitHub</a> </div> </div> </footer> </body> </html>