app_directories.py 2.2 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364
  1. """
  2. Wrapper for loading templates from "templates" directories in INSTALLED_APPS
  3. packages.
  4. """
  5. import os
  6. import sys
  7. from django.apps import apps
  8. from django.conf import settings
  9. from django.template.base import TemplateDoesNotExist
  10. from django.template.loader import BaseLoader
  11. from django.utils._os import safe_join
  12. from django.utils import six
  13. def calculate_app_template_dirs():
  14. if six.PY2:
  15. fs_encoding = sys.getfilesystemencoding() or sys.getdefaultencoding()
  16. app_template_dirs = []
  17. for app_config in apps.get_app_configs():
  18. if not app_config.path:
  19. continue
  20. template_dir = os.path.join(app_config.path, 'templates')
  21. if os.path.isdir(template_dir):
  22. if six.PY2:
  23. template_dir = template_dir.decode(fs_encoding)
  24. app_template_dirs.append(template_dir)
  25. return tuple(app_template_dirs)
  26. # At compile time, cache the directories to search.
  27. app_template_dirs = calculate_app_template_dirs()
  28. class Loader(BaseLoader):
  29. is_usable = True
  30. def get_template_sources(self, template_name, template_dirs=None):
  31. """
  32. Returns the absolute paths to "template_name", when appended to each
  33. directory in "template_dirs". Any paths that don't lie inside one of the
  34. template dirs are excluded from the result set, for security reasons.
  35. """
  36. if not template_dirs:
  37. template_dirs = app_template_dirs
  38. for template_dir in template_dirs:
  39. try:
  40. yield safe_join(template_dir, template_name)
  41. except UnicodeDecodeError:
  42. # The template dir name was a bytestring that wasn't valid UTF-8.
  43. raise
  44. except ValueError:
  45. # The joined path was located outside of template_dir.
  46. pass
  47. def load_template_source(self, template_name, template_dirs=None):
  48. for filepath in self.get_template_sources(template_name, template_dirs):
  49. try:
  50. with open(filepath, 'rb') as fp:
  51. return (fp.read().decode(settings.FILE_CHARSET), filepath)
  52. except IOError:
  53. pass
  54. raise TemplateDoesNotExist(template_name)