2
0
Эх сурвалжийг харах

Closes #22436: Rename jinja2_filters/get_jinja2_context/register_jinja2_filters to drop the '2' suffix

Follow-up to #22363: align the plugin hook names with the already-renamed
JINJA_FILTERS setting (#22288) and with the rest of the codebase's 'Jinja'
spelling convention.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Brian Tiemann 3 долоо хоног өмнө
parent
commit
ac513345b5

+ 13 - 13
docs/plugins/development/config-templates.md

@@ -9,11 +9,11 @@ NetBox uses [Jinja](https://jinja.palletsprojects.com/) to render [configuration
 
 ## Registering Jinja Filters
 
-### Via `jinja2_env.py` (auto-discovery)
+### Via `jinja_env.py` (auto-discovery)
 
-Create a file named `jinja2_env.py` in your plugin root and expose a dict called `filters`. NetBox will auto-discover and register it when the plugin loads.
+Create a file named `jinja_env.py` in your plugin root and expose a dict called `filters`. NetBox will auto-discover and register it when the plugin loads.
 
-```python title="my_plugin/jinja2_env.py"
+```python title="my_plugin/jinja_env.py"
 def prefix_list(device):
     """Return all prefixes assigned to a device's interfaces."""
     return [
@@ -35,7 +35,7 @@ The filter is then available in any config template:
 {% endfor %}
 ```
 
-### Via `register_jinja2_filters()`
+### Via `register_jinja_filters()`
 
 You can also register filters programmatically inside your plugin's `ready()` method:
 
@@ -48,12 +48,12 @@ class MyPluginConfig(PluginConfig):
 
     def ready(self):
         super().ready()
-        from netbox.plugins.registration import register_jinja2_filters
-        from .jinja2_env import filters
-        register_jinja2_filters(filters)
+        from netbox.plugins.registration import register_jinja_filters
+        from .jinja_env import filters
+        register_jinja_filters(filters)
 ```
 
-`register_jinja2_filters()` accepts a `dict` mapping filter names to callables. It raises `TypeError` if passed a non-dict or if any value is not callable.
+`register_jinja_filters()` accepts a `dict` mapping filter names to callables. It raises `TypeError` if passed a non-dict or if any value is not callable.
 
 ### Precedence
 
@@ -81,7 +81,7 @@ JINJA_FILTERS = {
 
 ## Injecting Context Variables
 
-Override `get_jinja2_context()` in your `PluginConfig` subclass to inject additional variables into every config template render context.
+Override `get_jinja_context()` in your `PluginConfig` subclass to inject additional variables into every config template render context.
 
 ```python title="my_plugin/__init__.py"
 from netbox.plugins import PluginConfig
@@ -90,7 +90,7 @@ class MyPluginConfig(PluginConfig):
     name = 'my_plugin'
     # ...
 
-    def get_jinja2_context(self):
+    def get_jinja_context(self):
         from .utils import MyNamespace
         return {
             'my_plugin': MyNamespace(),
@@ -104,12 +104,12 @@ The returned dict is merged into the template context, so `my_plugin` becomes av
 ```
 
 !!! warning "Startup cost"
-    `get_jinja2_context()` is called on **every** config template render, not once at startup. Keep it fast. Defer expensive lookups to the object you return rather than performing them in `get_jinja2_context()` itself.
+    `get_jinja_context()` is called on **every** config template render, not once at startup. Keep it fast. Defer expensive lookups to the object you return rather than performing them in `get_jinja_context()` itself.
 
 !!! note "Conflict avoidance"
     Choose context variable names that are unlikely to collide with NetBox's built-in template variables (`device`, `queryset`, etc.) or with those contributed by other plugins. Prefixing with your plugin name is strongly recommended.
 
-    In addition, avoid top-level app-label names (`dcim`, `ipam`, `virtualization`, etc.). The auto-populated template context maps each app label to a dict of its public model classes; returning a key like `'dcim'` from `get_jinja2_context()` will silently replace that entire namespace.
+    In addition, avoid top-level app-label names (`dcim`, `ipam`, `virtualization`, etc.). The auto-populated template context maps each app label to a dict of its public model classes; returning a key like `'dcim'` from `get_jinja_context()` will silently replace that entire namespace.
 
 !!! note "No per-render context"
-    `get_jinja2_context()` receives no arguments — it has no access to the object being rendered or the caller-supplied context. It is intended for plugin-global namespaces (e.g. a lazily-evaluated query helper). Per-object logic belongs in the template itself or in a custom filter.
+    `get_jinja_context()` receives no arguments — it has no access to the object being rendered or the caller-supplied context. It is intended for plugin-global namespaces (e.g. a lazily-evaluated query helper). Per-object logic belongs in the template itself or in a custom filter.

+ 1 - 1
docs/plugins/development/index.md

@@ -119,7 +119,7 @@ NetBox looks for the `config` variable within a plugin's `__init__.py` to load i
 | `search_indexes`      | The dotted path to the list of search index classes (default: `search.indexes`)                                                    |
 | `data_backends`       | The dotted path to the list of data source backend classes (default: `data_backends.backends`)                                     |
 | `template_extensions` | The dotted path to the list of template extension classes (default: `template_content.template_extensions`)                        |
-| `jinja2_filters`      | The dotted path to a dict of custom Jinja2 filter functions for use in config templates (default: `jinja2_env.filters`)            |
+| `jinja_filters`       | The dotted path to a dict of custom Jinja filter functions for use in config templates (default: `jinja_env.filters`)              |
 | `menu`                | The dotted path to a top-level navigation menu provided by the plugin (default: `navigation.menu`)                                 |
 | `menu_items`          | The dotted path to the list of menu items provided by the plugin (default: `navigation.menu_items`)                                |
 | `graphql_schema`      | The dotted path to the plugin's GraphQL schema class, if any (default: `graphql.schema`)                                           |

+ 2 - 2
netbox/extras/models/mixins.py

@@ -141,9 +141,9 @@ class RenderTemplateMixin(models.Model):
         for app_config in django_apps.get_app_configs():
             if isinstance(app_config, PluginConfig):
                 try:
-                    _context.update(app_config.get_jinja2_context())
+                    _context.update(app_config.get_jinja_context())
                 except Exception:
-                    logger.exception("Plugin %r raised an exception in get_jinja2_context()", app_config.name)
+                    logger.exception("Plugin %r raised an exception in get_jinja_context()", app_config.name)
 
         if context is not None:
             _context.update(context)

+ 8 - 8
netbox/netbox/plugins/__init__.py

@@ -20,7 +20,7 @@ from .utils import *
 registry['plugins'].update({
     'installed': [],
     'graphql_schemas': [],
-    'jinja2_filters': {},
+    'jinja_filters': {},
     'menus': [],
     'menu_items': {},
     'preferences': {},
@@ -31,7 +31,7 @@ DEFAULT_RESOURCE_PATHS = {
     'search_indexes': 'search.indexes',
     'data_backends': 'data_backends.backends',
     'graphql_schema': 'graphql.schema',
-    'jinja2_filters': 'jinja2_env.filters',
+    'jinja_filters': 'jinja_env.filters',
     'menu': 'navigation.menu',
     'menu_items': 'navigation.menu_items',
     'template_extensions': 'template_content.template_extensions',
@@ -80,7 +80,7 @@ class PluginConfig(AppConfig):
     search_indexes = None
     data_backends = None
     graphql_schema = None
-    jinja2_filters = None
+    jinja_filters = None
     menu = None
     menu_items = None
     serializer_resolver = None
@@ -88,9 +88,9 @@ class PluginConfig(AppConfig):
     user_preferences = None
     events_pipeline = []
 
-    def get_jinja2_context(self):
+    def get_jinja_context(self):
         """
-        Return a dict of additional variables to inject into the Jinja2 template context
+        Return a dict of additional variables to inject into the Jinja template context
         when rendering ConfigTemplates. Override this in a PluginConfig subclass to expose
         plugin-managed data to config templates without requiring template authors to know
         internal model names.
@@ -133,9 +133,9 @@ class PluginConfig(AppConfig):
         for backend in data_backends:
             register_data_backend()(backend)
 
-        # Register Jinja2 filters (if defined)
-        if jinja2_filters := self._load_resource('jinja2_filters'):
-            register_jinja2_filters(jinja2_filters)
+        # Register Jinja filters (if defined)
+        if jinja_filters := self._load_resource('jinja_filters'):
+            register_jinja_filters(jinja_filters)
 
         # Register template content (if defined)
         if template_extensions := self._load_resource('template_extensions'):

+ 8 - 8
netbox/netbox/plugins/registration.py

@@ -12,7 +12,7 @@ logger = logging.getLogger(__name__)
 
 __all__ = (
     'register_graphql_schema',
-    'register_jinja2_filters',
+    'register_jinja_filters',
     'register_menu',
     'register_menu_items',
     'register_serializer_resolver',
@@ -21,24 +21,24 @@ __all__ = (
 )
 
 
-def register_jinja2_filters(filters):
+def register_jinja_filters(filters):
     """
-    Register a dict of Jinja2 filter functions provided by a plugin. Each key is the
+    Register a dict of Jinja filter functions provided by a plugin. Each key is the
     filter name as it will appear in templates; the value is the callable implementing it.
     Plugin-registered filters have lower precedence than instance-level JINJA_FILTERS
     so that site admins can always override them in configuration.py.
     """
     if not isinstance(filters, dict):
-        raise TypeError(_("jinja2_filters must be a dict mapping filter names to callables"))
+        raise TypeError(_("jinja_filters must be a dict mapping filter names to callables"))
     for name, fn in filters.items():
         if not callable(fn):
-            raise TypeError(_("Jinja2 filter '{name}' must be callable").format(name=name))
-        if name in registry['plugins']['jinja2_filters']:
+            raise TypeError(_("Jinja filter '{name}' must be callable").format(name=name))
+        if name in registry['plugins']['jinja_filters']:
             logger.warning(
-                "Jinja2 filter '%s' registered by a plugin is being overridden by a later-loaded plugin",
+                "Jinja filter '%s' registered by a plugin is being overridden by a later-loaded plugin",
                 name,
             )
-    registry['plugins']['jinja2_filters'].update(filters)
+    registry['plugins']['jinja_filters'].update(filters)
 
 
 def register_template_extensions(class_list):

+ 1 - 1
netbox/netbox/tests/dummy_plugin/__init__.py

@@ -21,7 +21,7 @@ class DummyPluginConfig(PluginConfig):
         'netbox.tests.dummy_plugin.events.process_events_queue'
     ]
 
-    def get_jinja2_context(self):
+    def get_jinja_context(self):
         return {'dummy_plugin_var': 'hello_from_dummy'}
 
     def ready(self):

+ 0 - 0
netbox/netbox/tests/dummy_plugin/jinja2_env.py → netbox/netbox/tests/dummy_plugin/jinja_env.py


+ 27 - 27
netbox/netbox/tests/test_plugins.py

@@ -238,16 +238,16 @@ class PluginTestCase(TestCase):
         """
         self.assertIn(set_context, registry['webhook_callbacks'])
 
-    def test_jinja2_filters_registered(self):
+    def test_jinja_filters_registered(self):
         """
-        Check that Jinja2 filters exported by the dummy plugin are registered in
-        registry['plugins']['jinja2_filters'] after ready().
+        Check that Jinja filters exported by the dummy plugin are registered in
+        registry['plugins']['jinja_filters'] after ready().
         """
-        from netbox.tests.dummy_plugin.jinja2_env import dummy_upper
-        self.assertIn('dummy_upper', registry['plugins']['jinja2_filters'])
-        self.assertIs(registry['plugins']['jinja2_filters']['dummy_upper'], dummy_upper)
+        from netbox.tests.dummy_plugin.jinja_env import dummy_upper
+        self.assertIn('dummy_upper', registry['plugins']['jinja_filters'])
+        self.assertIs(registry['plugins']['jinja_filters']['dummy_upper'], dummy_upper)
 
-    def test_jinja2_filter_available_in_render(self):
+    def test_jinja_filter_available_in_render(self):
         """
         Filters registered by a plugin must be usable inside render_jinja2().
         """
@@ -255,31 +255,31 @@ class PluginTestCase(TestCase):
         result = render_jinja2("{{ 'hello' | dummy_upper }}", {})
         self.assertEqual(result, 'HELLO')
 
-    def test_get_jinja2_context_merged_into_render(self):
+    def test_get_jinja_context_merged_into_render(self):
         """
-        Variables returned by a plugin's get_jinja2_context() must appear in the
+        Variables returned by a plugin's get_jinja_context() must appear in the
         context produced by RenderTemplateMixin.get_context().
         """
         from extras.models import ConfigTemplate
-        ct = ConfigTemplate(name='jinja2-ctx-test', template_code='')
+        ct = ConfigTemplate(name='jinja-ctx-test', template_code='')
         ctx = ct.get_context()
         self.assertIn('dummy_plugin_var', ctx)
         self.assertEqual(ctx['dummy_plugin_var'], 'hello_from_dummy')
 
-    def test_get_jinja2_context_bad_return_is_silenced(self):
+    def test_get_jinja_context_bad_return_is_silenced(self):
         """
-        A non-dict return from get_jinja2_context() must not crash the render.
+        A non-dict return from get_jinja_context() must not crash the render.
         """
         from unittest.mock import patch
 
         from extras.models import ConfigTemplate
         from netbox.tests.dummy_plugin import DummyPluginConfig
         ct = ConfigTemplate(name='bad-ctx-test', template_code='')
-        with patch.object(DummyPluginConfig, 'get_jinja2_context', return_value='not_a_dict'):
+        with patch.object(DummyPluginConfig, 'get_jinja_context', return_value='not_a_dict'):
             ctx = ct.get_context()
         self.assertNotIn('dummy_plugin_var', ctx)
 
-    def test_instance_jinja2_filters_override_plugin_filters(self):
+    def test_instance_jinja_filters_override_plugin_filters(self):
         """
         Instance-level JINJA_FILTERS must take precedence over plugin-registered filters
         of the same name.
@@ -292,30 +292,30 @@ class PluginTestCase(TestCase):
 
 
 @skipIf('netbox.tests.dummy_plugin' not in settings.PLUGINS, "dummy_plugin not in settings.PLUGINS")
-class PluginJinja2RegistrationTest(TestCase):
+class PluginJinjaRegistrationTest(TestCase):
     """
-    Tests for the register_jinja2_filters() registration helper independent of
+    Tests for the register_jinja_filters() registration helper independent of
     the dummy plugin's startup path.
     """
 
-    def test_register_jinja2_filters_rejects_non_dict(self):
-        from netbox.plugins.registration import register_jinja2_filters
+    def test_register_jinja_filters_rejects_non_dict(self):
+        from netbox.plugins.registration import register_jinja_filters
         with self.assertRaises(TypeError):
-            register_jinja2_filters([('my_filter', lambda v: v)])
+            register_jinja_filters([('my_filter', lambda v: v)])
 
-    def test_register_jinja2_filters_rejects_non_callable_value(self):
-        from netbox.plugins.registration import register_jinja2_filters
+    def test_register_jinja_filters_rejects_non_callable_value(self):
+        from netbox.plugins.registration import register_jinja_filters
         with self.assertRaises(TypeError):
-            register_jinja2_filters({'my_filter': 'not_a_function'})
+            register_jinja_filters({'my_filter': 'not_a_function'})
 
-    def test_register_jinja2_filters_merges_into_registry(self):
-        from netbox.plugins.registration import register_jinja2_filters
+    def test_register_jinja_filters_merges_into_registry(self):
+        from netbox.plugins.registration import register_jinja_filters
         fn = lambda v: v  # noqa: E731
-        register_jinja2_filters({'_test_temp_filter': fn})
+        register_jinja_filters({'_test_temp_filter': fn})
         try:
-            self.assertIs(registry['plugins']['jinja2_filters']['_test_temp_filter'], fn)
+            self.assertIs(registry['plugins']['jinja_filters']['_test_temp_filter'], fn)
         finally:
-            del registry['plugins']['jinja2_filters']['_test_temp_filter']
+            del registry['plugins']['jinja_filters']['_test_temp_filter']
 
 
 class PluginNavigationTestCase(TestCase):

+ 1 - 1
netbox/utilities/jinja2.py

@@ -102,7 +102,7 @@ def render_jinja2(template_code, context, environment_params=None, data_file=Non
     # Instance-level config always wins so site admins can override anything.
     filters = {
         **DEFAULT_JINJA2_FILTERS,
-        **registry['plugins'].get('jinja2_filters', {}),
+        **registry['plugins'].get('jinja_filters', {}),
         **get_config().JINJA_FILTERS,
     }
     environment.filters.update(filters)