Serialize plugins (default and custom)¶
Every django CMS plugin is serialized to JSON automatically — no configuration required. This guide shows the default behaviour, then how to take control with a custom serializer when you need a specific shape or fully typed output.
For why it works this way (foreign-key resolution, rich-text JSON, the plugin definitions endpoint), see Plugin serialization.
Default: automatic serialization¶
Create plugins exactly as you would in any django CMS project. Their model fields are serialized for you. A plugin with no extra fields still serializes its type:
# cms_plugins.py
from cms.models.pluginmodel import CMSPlugin
from cms.plugin_base import CMSPluginBase
from cms.plugin_pool import plugin_pool
@plugin_pool.register_plugin
class HelloPlugin(CMSPluginBase):
model = CMSPlugin
name = "Hello"
render_template = "hello_world.html"
The template is still useful in headless mode: it lets editors see the plugin in the
django CMS structure board and powers the optional ?html=1 rendering.
Add the plugin to a page, then fetch the placeholder content (note that the specific
content type and object primary keys likely are different for your setup, run
curl http://localhost:8080/api/en/pages/ to find out what’s correct for you):
curl "http://localhost:8080/api/en/placeholders/18/1/content/"
{
"slot": "content",
"content": [
{
"id": 1,
"parent_plugin_type": null,
"plugin_type": "HelloPlugin"
}
],
"html": ""
}
Every serialized plugin carries id, parent_plugin_type (the type of its parent
plugin, or null at the top level) and plugin_type. A plugin with model fields adds
each field by name:
# models.py
from cms.models import CMSPlugin
from cms.models.fields import PageField
from django.db import models
class HeroPluginModel(CMSPlugin):
title = models.CharField(max_length=200)
description = models.TextField()
image = models.ImageField(upload_to="hero_images")
link = PageField(blank=True, null=True)
A new model needs migrations — create and apply them before using the plugin:
python manage.py makemigrations <your_app>
python manage.py migrate
Its serialized form:
{
"id": 4,
"parent_plugin_type": null,
"plugin_type": "HeroPlugin",
"title": "A custom page hero",
"description": "Important teaser content.",
"image": "http://localhost:8080/media/hero_images/demo.png",
"link": "http://localhost:8080/api/en/pages/about/"
}
Foreign keys are resolved to API endpoints where possible — note how link (a
PageField) becomes the target page’s API URL rather than a raw id. This matches the
real behaviour of, for example, a link plugin whose page field resolves to
"http://localhost:8080/api/en/pages/". See Plugin serialization
for the resolution rules.
Nested plugins¶
Plugins nested in the structure board (e.g. columns inside a grid) are serialized
recursively. Child plugins appear under a children array on the parent, so the JSON
mirrors the plugin tree.
Custom: define a serializer_class¶
Set a serializer_class on the plugin to control exactly which fields appear and how
they are typed. Use this to add computed fields, hide internal ones, or shape data for the
frontend. The serializer is a standard DRF ModelSerializer:
# serializers.py
from rest_framework import serializers
from .models import HeroPluginModel
class HeroPluginSerializer(serializers.ModelSerializer):
cta_label = serializers.SerializerMethodField()
class Meta:
model = HeroPluginModel
fields = ["title", "description", "image", "link", "cta_label"]
def get_cta_label(self, obj) -> str:
return obj.link_text or "Read more"
# cms_plugins.py
from cms.plugin_base import CMSPluginBase
from cms.plugin_pool import plugin_pool
from .models import HeroPluginModel
from .serializers import HeroPluginSerializer
@plugin_pool.register_plugin
class HeroPlugin(CMSPluginBase):
model = HeroPluginModel
name = "Hero"
render_template = "hero.html"
serializer_class = HeroPluginSerializer
The same serializer_class drives both the runtime content payload and the typed
description returned by the /api/plugins/ endpoint — so a custom
serializer also produces richer, fully typed client code.
Verify the type definition¶
curl http://localhost:8080/api/plugins/
The plugin now reports its precise properties (types, formats, help text), which client generators turn into accurate frontend types.
See also
Plugin serialization — resolution rules, rich text, and limits.
Reference — the endpoint catalogue and live schema.