Simplify approach for registering plugin template content #3504

Closed
opened 2025-12-29 18:29:33 +01:00 by adam · 3 comments
Owner

Originally created by @jeremystretch on GitHub (Mar 25, 2020).

Similar to #4401, this issue proposes a new mechanism for plugins to register custom template content.

Current Approach

The current approach requires three components:

  1. Declare all PluginTemplateContent subclasses in a file.
  2. Create a function returning those classes and register it as receiver for the register_detail_page_content_classes signal in signals.py.
  3. Extend the PluginConfig subclass to import signals.py on ready() (which may already be in place).

Proposed Approach

This issue proposes extending the PluginTemplateContent class to automatically register its subclasses. This can be achieved using the __init_subclass__() method introduced in Python 3.6. Below is a theoretical example:

from extras.registry import registry

# Bit of hand-waving here; just want to call out that the registry store exists
registry['plugin_template_content_classes'] = collections.defaultdict(list)

class PluginTemplateContent:

    def __init_subclass__(cls, **kwargs):
        super().__init_subclass__(**kwargs)
        
        # Register the subclass
        registry['plugin_template_content_classes'][cls.model].append(cls)

This should automatically register each subclass as it is imported. The only missing component is the actual import, which we can handle in PluginConfig's ready() method, assuming we define a standard filename for template content.

With this approach, the following would be sufficient for a plugin to declare a piece of template content:

template_content.py

from extras.plugins import PluginTemplateContent

class TestButtonOne(PluginTemplateContent):
    model = 'dcim.site'

    def full_width_page(self):
        return self.render('my_custom_content.html')
    ...

Justification

  1. This approach requires a minimum of effort from plugin developers: simply define the necessary subclasses in the specified file, or ensure that they are otherwise imported by your plugin.
  2. Registration happens at initialization, rather than on-demand, so any errors are immediately raised.
  3. Validation is simplified, since we no longer have to check for the parent class.
Originally created by @jeremystretch on GitHub (Mar 25, 2020). Similar to #4401, this issue proposes a new mechanism for plugins to register custom template content. ### Current Approach The current approach requires three components: 1. Declare all PluginTemplateContent subclasses in a file. 2. Create a function returning those classes and register it as receiver for the `register_detail_page_content_classes` signal in `signals.py`. 3. Extend the PluginConfig subclass to import `signals.py` on `ready()` (which may already be in place). ### Proposed Approach This issue proposes extending the PluginTemplateContent class to automatically register its subclasses. This can be achieved using the [`__init_subclass__()` method](https://docs.python.org/3.6/whatsnew/3.6.html#pep-487-simpler-customization-of-class-creation) introduced in Python 3.6. Below is a theoretical example: ```python from extras.registry import registry # Bit of hand-waving here; just want to call out that the registry store exists registry['plugin_template_content_classes'] = collections.defaultdict(list) class PluginTemplateContent: def __init_subclass__(cls, **kwargs): super().__init_subclass__(**kwargs) # Register the subclass registry['plugin_template_content_classes'][cls.model].append(cls) ``` This should automatically register each subclass as it is imported. The only missing component is the actual import, which we can handle in PluginConfig's `ready()` method, assuming we define a standard filename for template content. With this approach, the following would be sufficient for a plugin to declare a piece of template content: **template_content.py** ```python from extras.plugins import PluginTemplateContent class TestButtonOne(PluginTemplateContent): model = 'dcim.site' def full_width_page(self): return self.render('my_custom_content.html') ... ``` ### Justification 1. This approach requires a minimum of effort from plugin developers: simply define the necessary subclasses in the specified file, or ensure that they are otherwise imported by your plugin. 2. Registration happens at initialization, rather than on-demand, so any errors are immediately raised. 3. Validation is simplified, since we no longer have to check for the parent class.
adam closed this issue 2025-12-29 18:29:33 +01:00
Author
Owner

@lampwins commented on GitHub (Mar 25, 2020):

I think we should use the same mechanism as #4401 for two reasons:

  1. It standardizes the interface pattern for this kind of thing. We have two such use cases today, but #4393 would likely take the same approach and should we decide to support custom scripts and reports through plugins.
  2. As brought up in #4401 using the list/tuple variable is an established Django pattern, but it also ensures deterministic ordering for the end-user without having to play with declaration order.
@lampwins commented on GitHub (Mar 25, 2020): I think we should use the same mechanism as #4401 for two reasons: 1. It standardizes the interface pattern for this kind of thing. We have two such use cases today, but #4393 would likely take the same approach and should we decide to support custom scripts and reports through plugins. 2. As brought up in #4401 using the list/tuple variable is an established Django pattern, but it also ensures deterministic ordering for the end-user without having to play with declaration order.
Author
Owner

@jeremystretch commented on GitHub (Mar 25, 2020):

I agree that we should establish a clear pattern in the way we integrate plugin components. I wasn't thinking about ordering with regard to the template content, but that's a good point: A plugin might introduce multiple PluginTemplateContent instances for the same view, and we should provide a mechanism for the author to manipulate the order in which they appear.

@jeremystretch commented on GitHub (Mar 25, 2020): I agree that we should establish a clear pattern in the way we integrate plugin components. I wasn't thinking about ordering with regard to the template content, but that's a good point: A plugin might introduce multiple PluginTemplateContent instances for the same view, and we should provide a mechanism for the author to manipulate the order in which they appear.
Author
Owner

@DanSheps commented on GitHub (Mar 25, 2020):

I have to agree, while that init_subclasses is nifty, I think sticking to the mechanism in #4401 is probably best. While plugins are a fairly advanced feature, anything we can do to maintain consistency I think would be desired.

@DanSheps commented on GitHub (Mar 25, 2020): I have to agree, while that init_subclasses is nifty, I think sticking to the mechanism in #4401 is probably best. While plugins are a fairly advanced feature, anything we can do to maintain consistency I think would be desired.
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: starred/netbox#3504