Skip to content

Instantly share code, notes, and snippets.

@corentinbettiol
Last active August 12, 2022 14:53
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save corentinbettiol/518b01e7f269f62bfe4196f78d291df1 to your computer and use it in GitHub Desktop.
Save corentinbettiol/518b01e7f269f62bfe4196f78d291df1 to your computer and use it in GitHub Desktop.
Display djangocms cmsplugin instance (and childs) from another page.

Do you ever wanted to display a cms plugin content on any page, without having to copy the plugin? Display multiple plugins too?

Then listenread this

  1. Create a new model:
# my_plugin/models.py
from cms.models.pluginmodel import CMSPlugin

class MyPlugin():
    ...
    # the existing cms plugin model that we want to display

class DisplayOneOrMultipleMyPlugins(CMSPlugin):
    my_plugins = models.ManyToManyField(to=MyPlugin, limit_choices_to={"placeholder__page__publisher_is_draft": False},)

    def copy_relations(self, oldinstance):
        self.my_plugins.add(*oldinstance.my_plugins.all())
  1. Makemigrations & migrate.
  2. Create a new CMSPlugin (I'll explain the FakePlugin thing later):
# my_plugin/cms_plugins.py
from cms.plugin_base import CMSPluginBase
from cms.plugin_pool import plugin_pool
from apps.my_plugin.models import DisplayOneOrMultipleMyPlugins

# There should be your class "MyPluginPublished()" here.

@plugin_pool.register_plugin
class DisplayOneOrMultipleMyPluginsPluginRender(CMSPluginBase):
    module = _("My Plugin")
    name = _("Display one My Plugin or more.")
    model = DisplayOneOrMultipleMyPlugins
    render_template = "my_plugin/my_plugins.html"

    class FakePlugin():
        plugin = None
        def __init__(self, plugin): self.plugin = plugin  # oneliner rulez!!!!1!

    def render(self, context, instance, placeholder):
        instances = []
        for my_plugin_instance in instance.my_plugins.all():
            fake_instance = self.FakePlugin(my_plugin_instance)
            instances.append(fake_instance)
        context.update({"instances": instances})
        return context
  1. Use this dark magic template to render your plugin:
{% load cms_alias_tags %}
{% for instance in instances %}
  {% render_alias_plugin instance %}
{% endfor %}
  1. That's all folks!

What's the thing with FakePlugin?

Explanations

I've struggled to understand how DjangoCMS works for hours before a colleague pointed me to the right direction. This direction, friends, is this templatetag.

render_alias_plugin is a function used in the Aliases feature of DjangoCMS (create an "alias" (a copy) of a plugin and display it on multiple pages). It takes an instance (???), a request, then searches for the plugin attribute in the instance object, and call all the required complicated functions in the right order with the right arguments in order to render the plugin (idk I don't understand the DjangoCMS code no matter how hard I try).

So in order to make the templatetag work I just created a fake class in DisplayOneOrMultipleMyPluginsPluginRender that has a plugin attribute, assigned the instance of the plugin we want to display in it, and tried.

Worked first time, after like 5 hours of struggle. 10/10 can recommand.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment