Skip to content

Conversation

@fsbraun
Copy link
Member

@fsbraun fsbraun commented Oct 24, 2025

This PR adds the Placeholder content directly to placeholder relation fields, e.g., for the page detail endpoint:

GET /api/en/pages/?html=true

HTTP 200 OK
Allow: GET, OPTIONS
Content-Type: application/json
Vary: Accept

{
    "title": "test page",
    ...,
    "placeholders": [
        {
            "slot": "Page Content",
            "label": "Page Content",
            "language": "en",
            "content": [
                {
                    "plugin_type": "TextPlugin",
                    "body": "<p>No need to fetch placeholder content in another request response cycle</p>",
                    "json": {
                        "type": "doc",
                        "content": [
                            {
                                "type": "paragraph",
                                "attrs": {
                                    "textAlign": null
                                },
                                "content": [
                                    {
                                        "type": "text",
                                        "text": "No need to fetch placeholder content in another request response cycle"
                                    }
                                ]
                            }
                        ]
                    },
                    "rte": "tiptap"
                }
            ],
            "details": "http://127.0.0.1:8000/api/en/placeholders/10/11/Page%20Content/?html=true",
            "html": "<p>No need to fetch placeholder content in another request response cycle</p>"
        }
    ]
}

Tests need to be adjusted.

Summary by Sourcery

Provide placeholder content, HTML, and details URL directly in page responses by consolidating placeholder serialization into a single serializer

New Features:

  • Include placeholder content and HTML directly in the page detail endpoint response

Enhancements:

  • Remove PlaceholderRelationSerializer and use PlaceholderSerializer for placeholders in pages
  • Move plugin rendering and HTML fetching into PlaceholderSerializer.to_representation
  • Add "details" URL field to PlaceholderSerializer and preserve original query parameters in its URL generation

Tests:

  • Update tests to expect direct placeholder content, HTML, and details fields

@sourcery-ai
Copy link
Contributor

sourcery-ai bot commented Oct 24, 2025

Reviewer's Guide

This PR removes the separate PlaceholderRelationSerializer, refactors PlaceholderSerializer to handle content, HTML, and details directly during representation, and updates PageContentSerializer to use the enhanced PlaceholderSerializer with simplified URL generation.

Class diagram for updated PlaceholderSerializer and removal of PlaceholderRelationSerializer

classDiagram
class PlaceholderSerializer {
  +label: CharField
  +language: CharField
  +content: ListSerializer(JSONField)
  +details: URLField
  +html: CharField
  +__init__(request, language, render_plugins)
  +to_representation(instance)
  +get_details(instance)
}
class PlaceholderRelationSerializer {
  -content_type_id: IntegerField
  -object_id: IntegerField
  -slot: CharField
  -details: URLField
  -__init__(language)
  -to_representation(instance)
  -get_details(instance)
}
PlaceholderRelationSerializer <|-- PlaceholderSerializer : "removed, replaced by"
Loading

Class diagram for updated PageContentSerializer placeholders field

classDiagram
class PageContentSerializer {
  +placeholders: PlaceholderSerializer[many=True]
  +__init__()
  +to_representation(page_content)
}
PageContentSerializer --> PlaceholderSerializer : uses
Loading

File-Level Changes

Change Details Files
Removed the separate relation serializer and integrated placeholder content directly.
  • Deleted PlaceholderRelationSerializer class.
  • Replaced PlaceholderRelationSerializer with PlaceholderSerializer in PageContentSerializer.
  • Removed intermediate placeholders_data assembly.
djangocms_rest/serializers/placeholders.py
djangocms_rest/serializers/pages.py
Refactored PlaceholderSerializer to initialize context in init and populate dynamic fields in to_representation.
  • Stored request, language, and render_plugins on self in init.
  • Removed legacy constructor logic that popped instance and modified fields in init.
  • Added to_representation to set label, language, details, content, and html.
djangocms_rest/serializers/placeholders.py
Streamlined detail URL construction using full querystring encoding.
  • Replaced manual preview-mode handling with appending request.GET.urlencode().
  • Renamed variables for consistency in URL generation.
djangocms_rest/serializers/placeholders.py
Updated plugin rendering calls to use self.request and instance consistently.
  • Switched from passing request variable to self.request in RESTRenderer.
  • Renamed placeholder references to instance for clear context.
  • Updated render_html invocation to use self.request and correct instance arguments.
djangocms_rest/serializers/placeholders.py

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

@fsbraun fsbraun marked this pull request as ready for review October 25, 2025 13:56
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 there - I've reviewed your changes and found some issues that need to be addressed.

  • Avoid mutating the placeholder model instance inside to_representation—build the serialized dict or use SerializerMethodFields so you don’t introduce side-effects on the original object.
  • get_details currently appends all request.GET params to the placeholder URL; consider whitelisting only relevant flags (e.g. html or preview) to avoid leaking unrelated query parameters.
  • The to_representation method is getting quite long—extracting plugin rendering and HTML assembly into small helper methods or a dedicated service will improve readability and maintainability.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- Avoid mutating the placeholder model instance inside to_representation—build the serialized dict or use SerializerMethodFields so you don’t introduce side-effects on the original object.
- get_details currently appends all request.GET params to the placeholder URL; consider whitelisting only relevant flags (e.g. html or preview) to avoid leaking unrelated query parameters.
- The to_representation method is getting quite long—extracting plugin rendering and HTML assembly into small helper methods or a dedicated service will improve readability and maintainability.

## Individual Comments

### Comment 1
<location> `djangocms_rest/serializers/placeholders.py:27-29` </location>
<code_context>
-        if placeholder and request and language:
-            if render_plugins:
+    def to_representation(self, instance):
+        instance.label = instance.get_label()
+        instance.language = self.language
+        instance.details = self.get_details(instance)
+        if instance and self.request and self.language:
+            if self.render_plugins:
</code_context>

<issue_to_address>
**suggestion (bug_risk):** Directly mutating the instance in 'to_representation' can have unintended side effects.

Using a local dictionary to build the representation avoids unintended changes to the input object and prevents side effects if the instance is reused elsewhere.
</issue_to_address>

### Comment 2
<location> `djangocms_rest/serializers/placeholders.py:62-63` </location>
<code_context>
-            else:
-                api_endpoint += "?preview=1"
-        return api_endpoint
+        if self.request.GET:
+            url += f"?{self.request.GET.urlencode()}"
+        return url
</code_context>

<issue_to_address>
**🚨 issue (security):** Appending all query parameters to the details URL may expose sensitive or irrelevant data.

Filter query parameters to include only those necessary for the details endpoint to avoid leaking sensitive or irrelevant information.
</issue_to_address>

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.

Comment on lines +27 to +29
instance.label = instance.get_label()
instance.language = self.language
instance.details = self.get_details(instance)
Copy link
Contributor

Choose a reason for hiding this comment

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

suggestion (bug_risk): Directly mutating the instance in 'to_representation' can have unintended side effects.

Using a local dictionary to build the representation avoids unintended changes to the input object and prevents side effects if the instance is reused elsewhere.

Copy link
Member Author

Choose a reason for hiding this comment

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

@metaforx What do you think about this? Maybe we change the pattern.

@fsbraun fsbraun requested a review from metaforx October 26, 2025 17:28
@codecov
Copy link

codecov bot commented Oct 26, 2025

Codecov Report

❌ Patch coverage is 94.11765% with 2 lines in your changes missing coverage. Please review.
✅ Project coverage is 91.23%. Comparing base (2a129da) to head (dcbbac9).

Files with missing lines Patch % Lines
djangocms_rest/serializers/placeholders.py 93.54% 0 Missing and 2 partials ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main      #73      +/-   ##
==========================================
+ Coverage   90.83%   91.23%   +0.39%     
==========================================
  Files          18       18              
  Lines         851      844       -7     
  Branches       98       97       -1     
==========================================
- Hits          773      770       -3     
+ Misses         48       46       -2     
+ Partials       30       28       -2     

☔ 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.

@fsbraun
Copy link
Member Author

fsbraun commented Oct 31, 2025

@metaforx This should also reduce the number of API calls: Content is already fetched with a page,.

@metaforx
Copy link
Collaborator

metaforx commented Nov 5, 2025

@fsbraun: Very nice 👍. I tested it locally and could fetch content as expected from root and subpage.

Should we still add the option to only fetch metadata? Or is there still very little JSON data, even when the page is large? I was just thinking about whether there is a case where we would want to fetch page metadata without also fetching the content.

One other issue we should fix, which was also present in earlier endpoints. content is missing from the placeholder schema but returned as a property of placeholders. We then either handle ts-error in the frontend or enhance the schema, which is doable, but fixing the schema would be the best way to address this issue.

  "placeholders": [
    {
      "slot": "string",
      "label": "string",
      "language": "string",
      "details": "string",   
      "html": ""
    }
  ]
  
"placeholders": [
  {
      "slot": "content",
      "label": "Content",
      "language": "en",
      "content": [ <- missing in schema
          {
          }
       ]
PLACEHOLDER_FIELD_TYPES = {
    "slot": str,
    "label": str,
    "language": str,
    "content": list,   <- but present here :/ 
    "html": str,
}

It looks set in code, but I do not get it when fetching the schema.
http://127.0.0.1:8080/api/schema/

@fsbraun
Copy link
Member Author

fsbraun commented Nov 6, 2025

@metaforx I fixed the schema issue and added test for the schema.

I do not think that generating the content is too traffic heavy: In the end it's a page worth of content.

Once we have added the path feature to the menus, would the menus provide enough meta info on the site structure and also on the pages? We also have pages-list and pages-tree for page-only meta info.

I'll leave it for you to decide if we add a set of page-meta endpoints wich would not contain the content. Let me know.

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.

3 participants