jinja2.py 2.8 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283
  1. from django.apps import apps
  2. from jinja2 import BaseLoader, TemplateNotFound
  3. from jinja2.meta import find_referenced_templates
  4. from jinja2.sandbox import SandboxedEnvironment
  5. from netbox.config import get_config
  6. __all__ = (
  7. 'DataFileLoader',
  8. 'render_jinja2',
  9. )
  10. class DataFileLoader(BaseLoader):
  11. """
  12. Custom Jinja2 loader to facilitate populating template content from DataFiles.
  13. """
  14. def __init__(self, data_source):
  15. self.data_source = data_source
  16. self._template_cache = {}
  17. def get_source(self, environment, template):
  18. DataFile = apps.get_model('core', 'DataFile')
  19. # Retrieve template content from cache
  20. try:
  21. template_source = self._template_cache[template]
  22. except KeyError:
  23. raise TemplateNotFound(template)
  24. # Find and pre-fetch referenced templates
  25. if referenced_templates := tuple(find_referenced_templates(environment.parse(template_source))):
  26. related_files = DataFile.objects.filter(source=self.data_source)
  27. # None indicates the use of dynamic resolution. If dependent files are statically
  28. # defined, we can filter by path for optimization.
  29. if None not in referenced_templates:
  30. related_files = related_files.filter(path__in=referenced_templates)
  31. self.cache_templates({
  32. df.path: df.data_as_string for df in related_files
  33. })
  34. return template_source, template, lambda: True
  35. def cache_templates(self, templates):
  36. self._template_cache.update(templates)
  37. #
  38. # Utility functions
  39. #
  40. def render_jinja2(template_code, context, environment_params=None, data_file=None, debug=False):
  41. """
  42. Render a Jinja2 template with the provided context. Return the rendered content.
  43. If debug is True, the Jinja2 debug extension is enabled to assist with template development.
  44. """
  45. environment_params = dict(environment_params or {})
  46. if debug:
  47. extensions = list(environment_params.get('extensions', []))
  48. if 'jinja2.ext.debug' not in extensions:
  49. extensions.append('jinja2.ext.debug')
  50. environment_params['extensions'] = extensions
  51. if 'loader' not in environment_params:
  52. if data_file:
  53. loader = DataFileLoader(data_file.source)
  54. loader.cache_templates({
  55. data_file.path: template_code
  56. })
  57. else:
  58. loader = BaseLoader()
  59. environment_params['loader'] = loader
  60. environment = SandboxedEnvironment(**environment_params)
  61. environment.filters.update(get_config().JINJA2_FILTERS)
  62. if data_file:
  63. template = environment.get_template(data_file.path)
  64. else:
  65. template = environment.from_string(source=template_code)
  66. return template.render(**context)