Incubator (feincms3.incubator)

Warning

The feincms3.incubator module contains code which is subject to change and/or removal without any prior warning.

Subrenderers

Subrenderers allow rendering sections inside regions using different renderers. The same plugin may be rendered differently in different contexts. Sometimes just differentiating on the plugin itself or inserting additional elements as described in Rendering some plugins differently is sufficient, but you might want to try a more powerful tool.

Let’s make an example. Assume that we want to show a list of boxes, where a box might either be a teaser for a different page in the CMS or it might lead directly to a file download. Files can be integrated everywhere on the site, but they are generally rendered in a less obtrusive way, except inside our boxes list.

Defining a subrenderer

A boxes renderer definition and instantiation follows:

from . import models
from feincms3.incubator import subrenderer
from feincms3.renderer import TemplatePluginRenderer

class BoxRenderer(subrenderer.Subrenderer):
    # .enter() and .exit() are not required, but are very useful to add
    # wrapping elements to sections rendered by the subrenderer
    def enter(self, **kwargs):
        return '<div class="boxes">'

    def exit(self, **kwargs):
        return "</div>"

box_renderer = BoxRenderer()
box_renderer.register_template_renderer(
    models.PageTeaser,
    "boxes/page-teaser.html",
)
box_renderer.register_template_renderer(
    models.File,
    "boxes/file.html",
)

Making the subrenderer known to the main renderer

Next, we need a Regions class instantiated by the main renderer, but which also knows about subrenderers:

class Regions(subrenderer.SubrendererRegions):
    subrenderers = {"boxes": box_renderer}  # The exact key is important!

renderer = TemplatePluginRenderer(regions_class=Regions)

The plugins have to be registered with the main renderer even if the main renderer does not render some plugins – the renderer provides the list of plugins to be loaded from the database:

# When rendered by the main renderer page teasers do not produce any output
renderer.register_string_renderer(
    models.PageTeaser,
    "",
)
renderer.register_template_renderer(
    models.File,
    "plugins/file.html",  # Different template than above!
)

Entering the subrenderer

During rendering a subrenderer is activated when the SubrendererRegions encounters a plugin with a "subrenderer" attribute. In our case, two attribute values are allowed:

  1. "boxes": Activates the box_renderer for the current plugin.
  2. A falsy value for exiting the subrenderer again.

Probably the best way to achieve 1. in our example would be to add the attribute to the page teaser in the class definition:

# ...
class PageTeaser(PagePlugin):
    subrenderer = "boxes"

    # ...

Of course making the “subrenderer” attribute a model field or a property would work too.

Exiting the subrenderer

The subrenderer is deactivated if any of the following conditions occurs:

  • SubrendererRegions encounters a plugin with a "subrenderer" attribute which does not correspond to the current subrenderer.
  • The current subrenderer does not accept a plugin. By default this happens when the plugin has not been registered with the subrenderer.
  • Rendering finishes (no more contents).

Code documentation

class feincms3.incubator.subrenderer.Subrenderer(regions_class=<class 'feincms3.renderer.Regions'>)[source]

TemplatePluginRenderer subclass with hooks for adding wrapping elements and for rejecting plugin types

accepts(plugin, context=None)[source]

Returns True if plugin is of a handleable type

Returning False exits the subrenderer.

enter(**kwargs)[source]

Hook for opening a wrapping element

exit(**kwargs)[source]

Hook for closing a wrapping element

class feincms3.incubator.subrenderer.SubrendererRegions(item, contents, renderer)[source]

Regions subclass containing the logic enter and exit subrenderers depending on plugins

activate(subrenderer, **kwargs)[source]

Sets the passed subrenderer instance as current subrenderer, or exits a subrenderer if passed None.

Handles eventually required subrenderer enter and exit hooks calls.

current = None

Holds current subrenderer or None if using main renderer at the moment

render(self, region, context=None, *, timeout=None)[source]

Render a single region using the context passed

If timeout is None caching is disabled.

subrenderers = {}

Map of subrenderer keys to subrenderer instances