Skip to content

feat: add AliasInlineSerializer#94

Open
metaforx wants to merge 4 commits intodjango-cms:mainfrom
metaforx:feat/djangocms-alias-serializer
Open

feat: add AliasInlineSerializer#94
metaforx wants to merge 4 commits intodjango-cms:mainfrom
metaforx:feat/djangocms-alias-serializer

Conversation

@metaforx
Copy link
Collaborator

@metaforx metaforx commented Mar 11, 2026

fixes #5

  • Adds an alias serializer if the Alias plugin (or user) has not set its own serializer
  • Uses existing plugin rendering chain and preview handler
  • Small refactoring to avoid inline and circular imports

I opted for a more integrated approach instead of overriding the serializer via the app init function in the apps.py file because I consider it to be cleaner. @fsbraun: Do you agree?

{
  "content": [
    {
      "plugin_type": "TextPlugin",
      "body": "<p>Ich bin ein Alias</p>",
      "json": {
        "type": "doc",
        "content": [
          {
            "type": "paragraph",
            "attrs": {
              "textAlign": null
            },
            "content": [
              {
                "text": "Ich bin ein Alias",
                "type": "text"
              }
            ]
          }
        ]
      },
      "rte": "tiptap"
    }
  ],
  "plugin_type": "Alias",
  "template": "default",
  "alias": "djangocms_alias.alias:1"
}

Summary by Sourcery

Add a default serializer for Alias plugins by integrating an alias-specific inline serializer into the generic plugin serialization pipeline and centralizing plugin serializer resolution.

New Features:

  • Introduce an Alias inline serializer that exposes nested alias content via the existing REST rendering pipeline when no custom serializer is provided by the Alias plugin.

Enhancements:

  • Centralize plugin serializer resolution with support for built-in overrides and shared auto-generated model serializers.
  • Refine CMS plugin serialization and rendering paths to reuse the same serializer resolution logic and reduce inline or circular imports.

@metaforx metaforx requested a review from fsbraun March 11, 2026 21:00
@sourcery-ai
Copy link
Contributor

sourcery-ai bot commented Mar 11, 2026

Reviewer's Guide

Introduces a centralized mechanism for resolving plugin serializers, adding a built‑in default serializer for djangocms_alias.AliasPlugin, reusing the existing RESTRenderer/plugin serialization pipeline, and refactoring generic auto‑serializer creation and minor formatting.

Sequence diagram for AliasPlugin serialization via RESTRenderer

sequenceDiagram
    actor Client
    participant RESTRenderer
    participant Request
    participant CMSPlugin as CMSPluginInstance
    participant AliasPlugin as AliasPluginInstance
    participant Resolver as resolve_plugin_serializer
    participant AutoSer as get_auto_model_serializer
    participant Serializer as AliasInlineSerializer
    participant RendererAlias as RESTRendererAliasContent

    Client->>RESTRenderer: serialize_plugins(placeholder, language, context)
    RESTRenderer->>Request: attach request to context
    loop For each plugin
        RESTRenderer->>CMSPlugin: serialize_cms_plugin(instance, context)
        CMSPlugin-->>RESTRenderer: plugin_instance, plugin
        RESTRenderer->>Resolver: resolve_plugin_serializer(plugin, AliasPlugin)
        alt plugin.serializer_class is set
            Resolver-->>RESTRenderer: plugin.serializer_class
        else plugin.serializer_class not set
            Resolver-->>RESTRenderer: AliasInlineSerializer (from overrides)
        end
        RESTRenderer->>Serializer: __init__(AliasPluginInstance, context)
        RESTRenderer->>Serializer: data
        Serializer->>Serializer: get_content(instance)
        Serializer->>Request: read _rest_alias_stack
        alt alias_id already in _rest_alias_stack
            Serializer-->>RESTRenderer: content = []
        else alias_id not in stack
            Serializer->>Request: push alias_id to _rest_alias_stack
            Serializer->>AliasPlugin: alias.get_placeholder(language, draft_flag)
            alt placeholder exists
                Serializer->>RendererAlias: RESTRenderer(request).serialize_plugins(placeholder, language, context)
                RendererAlias-->>Serializer: serialized children
                Serializer-->>RESTRenderer: content with nested plugins
            else no placeholder
                Serializer-->>RESTRenderer: content = []
            end
            Serializer->>Request: pop alias_id from _rest_alias_stack
        end
        RESTRenderer-->>Client: serialized plugin data (including alias content)
    end
Loading

Updated class diagram for plugin serializers and AliasInlineSerializer

classDiagram
    class GenericPluginSerializer {
    }

    class AliasPlugin {
    }

    class AliasInlineSerializer {
        +content SerializerMethodField
        +get_content(instance AliasPlugin) list~dict~
    }

    GenericPluginSerializer <|-- AliasInlineSerializer
    AliasInlineSerializer ..> AliasPlugin

    class RESTRenderer {
        +serialize_plugins(placeholder, language, context) list
        +serialize_placeholder(placeholder, context, language, use_cache)
    }

    RESTRenderer ..> AliasInlineSerializer : uses for alias content

    class PluginSerializerResolution {
        +get_plugin_serializer_overrides() dict~str, type~
        +resolve_plugin_serializer(plugin, model_class) type
        +get_auto_model_serializer(model_class) type
    }

    PluginSerializerResolution ..> GenericPluginSerializer
    PluginSerializerResolution ..> AliasInlineSerializer
    RESTRenderer ..> PluginSerializerResolution : uses for serializer resolution
    AliasInlineSerializer ..> RESTRenderer : invokes for nested plugins
Loading

File-Level Changes

Change Details Files
Centralize plugin serializer resolution and reuse generic auto serializer logic in plugin serializers and rendering.
  • Added get_plugin_serializer_overrides to return built‑in serializer mappings, currently wiring AliasPlugin to a new AliasInlineSerializer when djangocms_alias is installed.
  • Introduced resolve_plugin_serializer to first honor a plugin.serializer_class and then fall back to centralized overrides based on model label.
  • Moved and generalized get_auto_model_serializer into serializers.plugins so both plugin definition generation and runtime rendering can share the same auto ModelSerializer generation logic.
  • Updated generate_plugin_definitions to use resolve_plugin_serializer plus get_auto_model_serializer instead of building ad‑hoc DynamicModelSerializer classes.
  • Refactored serialize_cms_plugin in plugin_rendering to rely on resolve_plugin_serializer/get_auto_model_serializer without mutating plugin.class.serializer_class.
  • Performed several line‑wrapping/formatting tweaks for readability without changing behavior.
djangocms_rest/serializers/plugins.py
djangocms_rest/plugin_rendering.py
Add a default inline serializer for AliasPlugin that inlines rendered alias content via the existing REST rendering pipeline while preventing recursion.
  • Created AliasInlineSerializer that subclasses GenericPluginSerializer, excludes base CMS bookkeeping fields, and exposes a computed content field.
  • Implemented get_content to resolve the current request and language, track an alias stack on the request to avoid infinite recursion, and short‑circuit when missing prerequisites or on recursive use.
  • Within get_content, resolved the alias placeholder for the proper language and preview mode, and used RESTRenderer.serialize_plugins to serialize the placeholder’s plugins with the existing plugin rendering chain and context.
  • Ensured the alias stack is correctly pushed/popped in a try/finally block to handle nested aliases safely.
djangocms_rest/serializers/alias.py

Assessment against linked issues

Issue Objective Addressed Explanation
#5 Expose AliasPlugin content via the existing REST API by providing appropriate serialization when the djangocms_alias app is installed.

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

Comment on lines +15 to +18
from djangocms_rest.serializers.plugins import (
get_auto_model_serializer,
resolve_plugin_serializer,
)

Check notice

Code scanning / CodeQL

Cyclic import Note

Import of module
djangocms_rest.serializers.plugins
begins an import cycle.
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@fsbraun can we ignore this?

from djangocms_alias.models import AliasPlugin
from rest_framework import serializers

from djangocms_rest.serializers.plugins import GenericPluginSerializer, base_exclude

Check notice

Code scanning / CodeQL

Cyclic import Note

Import of module
djangocms_rest.serializers.plugins
begins an import cycle.
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@fsbraun can we ignore this?

if not placeholder:
return []

from djangocms_rest.plugin_rendering import RESTRenderer

Check notice

Code scanning / CodeQL

Cyclic import Note

Import of module
djangocms_rest.plugin_rendering
begins an import cycle.
Copy link
Collaborator Author

@metaforx metaforx Mar 11, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@fsbraun exist to break circular dependencies

"""Return built-in serializer overrides keyed by model label."""
overrides: dict[str, type] = {}
if apps.is_installed("djangocms_alias"):
from djangocms_rest.serializers.alias import AliasInlineSerializer

Check notice

Code scanning / CodeQL

Cyclic import Note

Import of module
djangocms_rest.serializers.alias
begins an import cycle.
Copy link
Collaborator Author

@metaforx metaforx Mar 11, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@fsbraun exist to break circular dependencies

Copy link
Contributor

@sourcery-ai sourcery-ai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey - I've left some high level feedback:

  • Consider caching the result of get_plugin_serializer_overrides() (e.g. at module level or via lru_cache) so it isn’t recomputed on every serializer resolution when djangocms_alias is installed.
  • In resolve_plugin_serializer, you’ve removed the previous behavior of caching the resolved serializer on plugin.__class__.serializer_class; if that was relied upon for performance, you might want to explicitly document or reintroduce a lightweight caching strategy for resolved serializers.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- Consider caching the result of `get_plugin_serializer_overrides()` (e.g. at module level or via `lru_cache`) so it isn’t recomputed on every serializer resolution when `djangocms_alias` is installed.
- In `resolve_plugin_serializer`, you’ve removed the previous behavior of caching the resolved serializer on `plugin.__class__.serializer_class`; if that was relied upon for performance, you might want to explicitly document or reintroduce a lightweight caching strategy for resolved serializers.

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

@codecov
Copy link

codecov bot commented Mar 11, 2026

Codecov Report

❌ Patch coverage is 98.21429% with 1 line in your changes missing coverage. Please review.
✅ Project coverage is 92.40%. Comparing base (7f76592) to head (300c83a).

Files with missing lines Patch % Lines
djangocms_rest/plugin_rendering.py 91.66% 1 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main      #94      +/-   ##
==========================================
+ Coverage   91.98%   92.40%   +0.42%     
==========================================
  Files          19       20       +1     
  Lines         886      909      +23     
  Branches      100      100              
==========================================
+ Hits          815      840      +25     
+ Misses         44       43       -1     
+ Partials       27       26       -1     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@metaforx metaforx changed the title feat: add alias default serializer feat: add AliasInlineSerializer Mar 12, 2026
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.

expose aliases in the REST API

1 participant