cached.py 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687
  1. """
  2. Wrapper class that takes a list of template loaders as an argument and attempts
  3. to load templates from them in order, caching the result.
  4. """
  5. import hashlib
  6. from django.template.base import TemplateDoesNotExist
  7. from django.template.loader import BaseLoader, get_template_from_string, find_template_loader, make_origin
  8. from django.utils.encoding import force_bytes
  9. class Loader(BaseLoader):
  10. is_usable = True
  11. def __init__(self, loaders):
  12. self.template_cache = {}
  13. self.find_template_cache = {}
  14. self._loaders = loaders
  15. self._cached_loaders = []
  16. @property
  17. def loaders(self):
  18. # Resolve loaders on demand to avoid circular imports
  19. if not self._cached_loaders:
  20. # Set self._cached_loaders atomically. Otherwise, another thread
  21. # could see an incomplete list. See #17303.
  22. cached_loaders = []
  23. for loader in self._loaders:
  24. cached_loaders.append(find_template_loader(loader))
  25. self._cached_loaders = cached_loaders
  26. return self._cached_loaders
  27. def cache_key(self, template_name, template_dirs):
  28. if template_dirs:
  29. # If template directories were specified, use a hash to differentiate
  30. return '-'.join([template_name, hashlib.sha1(force_bytes('|'.join(template_dirs))).hexdigest()])
  31. else:
  32. return template_name
  33. def find_template(self, name, dirs=None):
  34. """
  35. Helper method. Lookup the template :param name: in all the configured loaders
  36. """
  37. key = self.cache_key(name, dirs)
  38. try:
  39. result = self.find_template_cache[key]
  40. except KeyError:
  41. result = None
  42. for loader in self.loaders:
  43. try:
  44. template, display_name = loader(name, dirs)
  45. except TemplateDoesNotExist:
  46. pass
  47. else:
  48. result = (template, make_origin(display_name, loader, name, dirs))
  49. break
  50. self.find_template_cache[key] = result
  51. if result:
  52. return result
  53. else:
  54. self.template_cache[key] = TemplateDoesNotExist
  55. raise TemplateDoesNotExist(name)
  56. def load_template(self, template_name, template_dirs=None):
  57. key = self.cache_key(template_name, template_dirs)
  58. template_tuple = self.template_cache.get(key)
  59. # A cached previous failure:
  60. if template_tuple is TemplateDoesNotExist:
  61. raise TemplateDoesNotExist
  62. elif template_tuple is None:
  63. template, origin = self.find_template(template_name, template_dirs)
  64. if not hasattr(template, 'render'):
  65. try:
  66. template = get_template_from_string(template, origin, template_name)
  67. except TemplateDoesNotExist:
  68. # If compiling the template we found raises TemplateDoesNotExist,
  69. # back off to returning the source and display name for the template
  70. # we were asked to load. This allows for correct identification (later)
  71. # of the actual template that does not exist.
  72. self.template_cache[key] = (template, origin)
  73. self.template_cache[key] = (template, None)
  74. return self.template_cache[key]
  75. def reset(self):
  76. "Empty the template cache."
  77. self.template_cache.clear()
  78. self.find_template_cache.clear()