jinja2.py 1.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657
  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. )
  9. class DataFileLoader(BaseLoader):
  10. """
  11. Custom Jinja2 loader to facilitate populating template content from DataFiles.
  12. """
  13. def __init__(self, data_source):
  14. self.data_source = data_source
  15. self._template_cache = {}
  16. def get_source(self, environment, template):
  17. DataFile = apps.get_model('core', 'DataFile')
  18. # Retrieve template content from cache
  19. try:
  20. template_source = self._template_cache[template]
  21. except KeyError:
  22. raise TemplateNotFound(template)
  23. # Find and pre-fetch referenced templates
  24. if referenced_templates := tuple(find_referenced_templates(environment.parse(template_source))):
  25. related_files = DataFile.objects.filter(source=self.data_source)
  26. # None indicates the use of dynamic resolution. If dependent files are statically
  27. # defined, we can filter by path for optimization.
  28. if None not in referenced_templates:
  29. related_files = related_files.filter(path__in=referenced_templates)
  30. self.cache_templates({
  31. df.path: df.data_as_string for df in related_files
  32. })
  33. return template_source, template, lambda: True
  34. def cache_templates(self, templates):
  35. self._template_cache.update(templates)
  36. #
  37. # Utility functions
  38. #
  39. def render_jinja2(template_code, context):
  40. """
  41. Render a Jinja2 template with the provided context. Return the rendered content.
  42. """
  43. environment = SandboxedEnvironment()
  44. environment.filters.update(get_config().JINJA2_FILTERS)
  45. return environment.from_string(source=template_code).render(**context)