test_include.py 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291
  1. from django.template import (
  2. Context, Engine, TemplateDoesNotExist, TemplateSyntaxError, loader,
  3. )
  4. from django.test import SimpleTestCase
  5. from ..utils import setup
  6. from .test_basic import basic_templates
  7. include_fail_templates = {
  8. 'include-fail1': '{% load bad_tag %}{% badtag %}',
  9. 'include-fail2': '{% load broken_tag %}',
  10. }
  11. class IncludeTagTests(SimpleTestCase):
  12. libraries = {'bad_tag': 'template_tests.templatetags.bad_tag'}
  13. @setup({'include01': '{% include "basic-syntax01" %}'}, basic_templates)
  14. def test_include01(self):
  15. output = self.engine.render_to_string('include01')
  16. self.assertEqual(output, 'something cool')
  17. @setup({'include02': '{% include "basic-syntax02" %}'}, basic_templates)
  18. def test_include02(self):
  19. output = self.engine.render_to_string('include02', {'headline': 'Included'})
  20. self.assertEqual(output, 'Included')
  21. @setup({'include03': '{% include template_name %}'}, basic_templates)
  22. def test_include03(self):
  23. output = self.engine.render_to_string(
  24. 'include03',
  25. {'template_name': 'basic-syntax02', 'headline': 'Included'},
  26. )
  27. self.assertEqual(output, 'Included')
  28. @setup({'include04': 'a{% include "nonexistent" %}b'})
  29. def test_include04(self):
  30. template = self.engine.get_template('include04')
  31. with self.assertRaises(TemplateDoesNotExist):
  32. template.render(Context({}))
  33. @setup({
  34. 'include 05': 'template with a space',
  35. 'include06': '{% include "include 05"%}',
  36. })
  37. def test_include06(self):
  38. output = self.engine.render_to_string('include06')
  39. self.assertEqual(output, "template with a space")
  40. @setup({'include07': '{% include "basic-syntax02" with headline="Inline" %}'}, basic_templates)
  41. def test_include07(self):
  42. output = self.engine.render_to_string('include07', {'headline': 'Included'})
  43. self.assertEqual(output, 'Inline')
  44. @setup({'include08': '{% include headline with headline="Dynamic" %}'}, basic_templates)
  45. def test_include08(self):
  46. output = self.engine.render_to_string('include08', {'headline': 'basic-syntax02'})
  47. self.assertEqual(output, 'Dynamic')
  48. @setup(
  49. {'include09': '{{ first }}--'
  50. '{% include "basic-syntax03" with first=second|lower|upper second=first|upper %}'
  51. '--{{ second }}'},
  52. basic_templates,
  53. )
  54. def test_include09(self):
  55. output = self.engine.render_to_string('include09', {'first': 'Ul', 'second': 'lU'})
  56. self.assertEqual(output, 'Ul--LU --- UL--lU')
  57. @setup({'include10': '{% include "basic-syntax03" only %}'}, basic_templates)
  58. def test_include10(self):
  59. output = self.engine.render_to_string('include10', {'first': '1'})
  60. if self.engine.string_if_invalid:
  61. self.assertEqual(output, 'INVALID --- INVALID')
  62. else:
  63. self.assertEqual(output, ' --- ')
  64. @setup({'include11': '{% include "basic-syntax03" only with second=2 %}'}, basic_templates)
  65. def test_include11(self):
  66. output = self.engine.render_to_string('include11', {'first': '1'})
  67. if self.engine.string_if_invalid:
  68. self.assertEqual(output, 'INVALID --- 2')
  69. else:
  70. self.assertEqual(output, ' --- 2')
  71. @setup({'include12': '{% include "basic-syntax03" with first=1 only %}'}, basic_templates)
  72. def test_include12(self):
  73. output = self.engine.render_to_string('include12', {'second': '2'})
  74. if self.engine.string_if_invalid:
  75. self.assertEqual(output, '1 --- INVALID')
  76. else:
  77. self.assertEqual(output, '1 --- ')
  78. @setup(
  79. {'include13': '{% autoescape off %}{% include "basic-syntax03" %}{% endautoescape %}'},
  80. basic_templates,
  81. )
  82. def test_include13(self):
  83. output = self.engine.render_to_string('include13', {'first': '&'})
  84. if self.engine.string_if_invalid:
  85. self.assertEqual(output, '& --- INVALID')
  86. else:
  87. self.assertEqual(output, '& --- ')
  88. @setup(
  89. {'include14': '{% autoescape off %}'
  90. '{% include "basic-syntax03" with first=var1 only %}'
  91. '{% endautoescape %}'},
  92. basic_templates,
  93. )
  94. def test_include14(self):
  95. output = self.engine.render_to_string('include14', {'var1': '&'})
  96. if self.engine.string_if_invalid:
  97. self.assertEqual(output, '& --- INVALID')
  98. else:
  99. self.assertEqual(output, '& --- ')
  100. # Include syntax errors
  101. @setup({'include-error01': '{% include "basic-syntax01" with %}'})
  102. def test_include_error01(self):
  103. with self.assertRaises(TemplateSyntaxError):
  104. self.engine.get_template('include-error01')
  105. @setup({'include-error02': '{% include "basic-syntax01" with "no key" %}'})
  106. def test_include_error02(self):
  107. with self.assertRaises(TemplateSyntaxError):
  108. self.engine.get_template('include-error02')
  109. @setup({'include-error03': '{% include "basic-syntax01" with dotted.arg="error" %}'})
  110. def test_include_error03(self):
  111. with self.assertRaises(TemplateSyntaxError):
  112. self.engine.get_template('include-error03')
  113. @setup({'include-error04': '{% include "basic-syntax01" something_random %}'})
  114. def test_include_error04(self):
  115. with self.assertRaises(TemplateSyntaxError):
  116. self.engine.get_template('include-error04')
  117. @setup({'include-error05': '{% include "basic-syntax01" foo="duplicate" foo="key" %}'})
  118. def test_include_error05(self):
  119. with self.assertRaises(TemplateSyntaxError):
  120. self.engine.get_template('include-error05')
  121. @setup({'include-error06': '{% include "basic-syntax01" only only %}'})
  122. def test_include_error06(self):
  123. with self.assertRaises(TemplateSyntaxError):
  124. self.engine.get_template('include-error06')
  125. @setup(include_fail_templates)
  126. def test_include_fail1(self):
  127. with self.assertRaises(RuntimeError):
  128. self.engine.get_template('include-fail1')
  129. @setup(include_fail_templates)
  130. def test_include_fail2(self):
  131. with self.assertRaises(TemplateSyntaxError):
  132. self.engine.get_template('include-fail2')
  133. @setup({'include-error07': '{% include "include-fail1" %}'}, include_fail_templates)
  134. def test_include_error07(self):
  135. template = self.engine.get_template('include-error07')
  136. with self.assertRaises(RuntimeError):
  137. template.render(Context())
  138. @setup({'include-error08': '{% include "include-fail2" %}'}, include_fail_templates)
  139. def test_include_error08(self):
  140. template = self.engine.get_template('include-error08')
  141. with self.assertRaises(TemplateSyntaxError):
  142. template.render(Context())
  143. @setup({'include-error09': '{% include failed_include %}'}, include_fail_templates)
  144. def test_include_error09(self):
  145. context = Context({'failed_include': 'include-fail1'})
  146. template = self.engine.get_template('include-error09')
  147. with self.assertRaises(RuntimeError):
  148. template.render(context)
  149. @setup({'include-error10': '{% include failed_include %}'}, include_fail_templates)
  150. def test_include_error10(self):
  151. context = Context({'failed_include': 'include-fail2'})
  152. template = self.engine.get_template('include-error10')
  153. with self.assertRaises(TemplateSyntaxError):
  154. template.render(context)
  155. class IncludeTests(SimpleTestCase):
  156. def test_include_missing_template(self):
  157. """
  158. The correct template is identified as not existing
  159. when {% include %} specifies a template that does not exist.
  160. """
  161. engine = Engine(app_dirs=True, debug=True)
  162. template = engine.get_template('test_include_error.html')
  163. with self.assertRaises(TemplateDoesNotExist) as e:
  164. template.render(Context())
  165. self.assertEqual(e.exception.args[0], 'missing.html')
  166. def test_extends_include_missing_baseloader(self):
  167. """
  168. #12787 -- The correct template is identified as not existing
  169. when {% extends %} specifies a template that does exist, but that
  170. template has an {% include %} of something that does not exist.
  171. """
  172. engine = Engine(app_dirs=True, debug=True)
  173. template = engine.get_template('test_extends_error.html')
  174. with self.assertRaises(TemplateDoesNotExist) as e:
  175. template.render(Context())
  176. self.assertEqual(e.exception.args[0], 'missing.html')
  177. def test_extends_include_missing_cachedloader(self):
  178. engine = Engine(debug=True, loaders=[
  179. ('django.template.loaders.cached.Loader', [
  180. 'django.template.loaders.app_directories.Loader',
  181. ]),
  182. ])
  183. template = engine.get_template('test_extends_error.html')
  184. with self.assertRaises(TemplateDoesNotExist) as e:
  185. template.render(Context())
  186. self.assertEqual(e.exception.args[0], 'missing.html')
  187. # Repeat to ensure it still works when loading from the cache
  188. template = engine.get_template('test_extends_error.html')
  189. with self.assertRaises(TemplateDoesNotExist) as e:
  190. template.render(Context())
  191. self.assertEqual(e.exception.args[0], 'missing.html')
  192. def test_include_template_argument(self):
  193. """
  194. Support any render() supporting object
  195. """
  196. engine = Engine()
  197. ctx = Context({
  198. 'tmpl': engine.from_string('This worked!'),
  199. })
  200. outer_tmpl = engine.from_string('{% include tmpl %}')
  201. output = outer_tmpl.render(ctx)
  202. self.assertEqual(output, 'This worked!')
  203. def test_include_from_loader_get_template(self):
  204. tmpl = loader.get_template('include_tpl.html') # {% include tmpl %}
  205. output = tmpl.render({'tmpl': loader.get_template('index.html')})
  206. self.assertEqual(output, 'index\n\n')
  207. def test_include_immediate_missing(self):
  208. """
  209. #16417 -- Include tags pointing to missing templates should not raise
  210. an error at parsing time.
  211. """
  212. Engine(debug=True).from_string('{% include "this_does_not_exist.html" %}')
  213. def test_include_recursive(self):
  214. comments = [
  215. {
  216. 'comment': 'A1',
  217. 'children': [
  218. {'comment': 'B1', 'children': []},
  219. {'comment': 'B2', 'children': []},
  220. {'comment': 'B3', 'children': [
  221. {'comment': 'C1', 'children': []}
  222. ]},
  223. ]
  224. }
  225. ]
  226. engine = Engine(app_dirs=True)
  227. t = engine.get_template('recursive_include.html')
  228. self.assertEqual(
  229. "Recursion! A1 Recursion! B1 B2 B3 Recursion! C1",
  230. t.render(Context({'comments': comments})).replace(' ', '').replace('\n', ' ').strip(),
  231. )
  232. def test_include_cache(self):
  233. """
  234. {% include %} keeps resolved templates constant (#27974). The
  235. CounterNode object in the {% counter %} template tag is created once
  236. if caching works properly. Each iteration increases the counter instead
  237. of restarting it.
  238. This works as a regression test only if the cached loader
  239. isn't used, so the @setup decorator isn't used.
  240. """
  241. engine = Engine(loaders=[
  242. ('django.template.loaders.locmem.Loader', {
  243. 'template': '{% for x in vars %}{% include "include" %}{% endfor %}',
  244. 'include': '{% include "next" %}',
  245. 'next': '{% load custom %}{% counter %}'
  246. }),
  247. ], libraries={'custom': 'template_tests.templatetags.custom'})
  248. output = engine.render_to_string('template', {'vars': range(9)})
  249. self.assertEqual(output, '012345678')