netbox/netbox/utilities/jinja2.py
Renato Almeida de Oliveira fbd6d8c7fc
Closes #17653: Add function to trim whitespaces in export templates via jinja environment settings (#19078)
* Create RenderMixin, and unify template_code rendering and exporting

* Join migrations

* Add DEFAULT_MIME_TE constant

* Move RenderMixin to extras.models.mixins, Rename RenderMixin to RenderTemplateMixin

* Add render_jinja2 to __all__

* Rename ConfigTemplateFilterForm rendering FieldSet

* ConfigTemplate lint

* Simplify ExportTemplate get_context

* Fix table order, and add fields for translations

* Update Serializers

* Update forms, tables, graphQL, API

* Add extra tests for ConfigTemplate and ExportTemplate

* Documentation update

* Fix typo

* Misc cleanup

* Clean up template layouts

---------

Co-authored-by: Jeremy Stretch <jstretch@netboxlabs.com>
2025-04-08 10:37:15 -04:00

60 lines
2.0 KiB
Python

from django.apps import apps
from jinja2 import BaseLoader, TemplateNotFound
from jinja2.meta import find_referenced_templates
from jinja2.sandbox import SandboxedEnvironment
from netbox.config import get_config
__all__ = (
'DataFileLoader',
'render_jinja2',
)
class DataFileLoader(BaseLoader):
"""
Custom Jinja2 loader to facilitate populating template content from DataFiles.
"""
def __init__(self, data_source):
self.data_source = data_source
self._template_cache = {}
def get_source(self, environment, template):
DataFile = apps.get_model('core', 'DataFile')
# Retrieve template content from cache
try:
template_source = self._template_cache[template]
except KeyError:
raise TemplateNotFound(template)
# Find and pre-fetch referenced templates
if referenced_templates := tuple(find_referenced_templates(environment.parse(template_source))):
related_files = DataFile.objects.filter(source=self.data_source)
# None indicates the use of dynamic resolution. If dependent files are statically
# defined, we can filter by path for optimization.
if None not in referenced_templates:
related_files = related_files.filter(path__in=referenced_templates)
self.cache_templates({
df.path: df.data_as_string for df in related_files
})
return template_source, template, lambda: True
def cache_templates(self, templates):
self._template_cache.update(templates)
#
# Utility functions
#
def render_jinja2(template_code, context, environment_params=None):
"""
Render a Jinja2 template with the provided context. Return the rendered content.
"""
environment_params = environment_params or {}
environment = SandboxedEnvironment(**environment_params)
environment.filters.update(get_config().JINJA2_FILTERS)
return environment.from_string(source=template_code).render(**context)