1. import pickle
    
  2. import time
    
  3. from datetime import datetime
    
  4. 
    
  5. from django.template import engines
    
  6. from django.template.response import (
    
  7.     ContentNotRenderedError,
    
  8.     SimpleTemplateResponse,
    
  9.     TemplateResponse,
    
  10. )
    
  11. from django.test import (
    
  12.     RequestFactory,
    
  13.     SimpleTestCase,
    
  14.     modify_settings,
    
  15.     override_settings,
    
  16. )
    
  17. from django.test.utils import require_jinja2
    
  18. 
    
  19. from .utils import TEMPLATE_DIR
    
  20. 
    
  21. 
    
  22. def test_processor(request):
    
  23.     return {"processors": "yes"}
    
  24. 
    
  25. 
    
  26. test_processor_name = "template_tests.test_response.test_processor"
    
  27. 
    
  28. 
    
  29. # A test middleware that installs a temporary URLConf
    
  30. def custom_urlconf_middleware(get_response):
    
  31.     def middleware(request):
    
  32.         request.urlconf = "template_tests.alternate_urls"
    
  33.         return get_response(request)
    
  34. 
    
  35.     return middleware
    
  36. 
    
  37. 
    
  38. class SimpleTemplateResponseTest(SimpleTestCase):
    
  39.     def _response(self, template="foo", *args, **kwargs):
    
  40.         template = engines["django"].from_string(template)
    
  41.         return SimpleTemplateResponse(template, *args, **kwargs)
    
  42. 
    
  43.     def test_template_resolving(self):
    
  44.         response = SimpleTemplateResponse("first/test.html")
    
  45.         response.render()
    
  46.         self.assertEqual(response.content, b"First template\n")
    
  47. 
    
  48.         templates = ["foo.html", "second/test.html", "first/test.html"]
    
  49.         response = SimpleTemplateResponse(templates)
    
  50.         response.render()
    
  51.         self.assertEqual(response.content, b"Second template\n")
    
  52. 
    
  53.         response = self._response()
    
  54.         response.render()
    
  55.         self.assertEqual(response.content, b"foo")
    
  56. 
    
  57.     def test_explicit_baking(self):
    
  58.         # explicit baking
    
  59.         response = self._response()
    
  60.         self.assertFalse(response.is_rendered)
    
  61.         response.render()
    
  62.         self.assertTrue(response.is_rendered)
    
  63. 
    
  64.     def test_render(self):
    
  65.         # response is not re-rendered without the render call
    
  66.         response = self._response().render()
    
  67.         self.assertEqual(response.content, b"foo")
    
  68. 
    
  69.         # rebaking doesn't change the rendered content
    
  70.         template = engines["django"].from_string("bar{{ baz }}")
    
  71.         response.template_name = template
    
  72.         response.render()
    
  73.         self.assertEqual(response.content, b"foo")
    
  74. 
    
  75.         # but rendered content can be overridden by manually
    
  76.         # setting content
    
  77.         response.content = "bar"
    
  78.         self.assertEqual(response.content, b"bar")
    
  79. 
    
  80.     def test_iteration_unrendered(self):
    
  81.         # unrendered response raises an exception on iteration
    
  82.         response = self._response()
    
  83.         self.assertFalse(response.is_rendered)
    
  84. 
    
  85.         def iteration():
    
  86.             list(response)
    
  87. 
    
  88.         msg = "The response content must be rendered before it can be iterated over."
    
  89.         with self.assertRaisesMessage(ContentNotRenderedError, msg):
    
  90.             iteration()
    
  91.         self.assertFalse(response.is_rendered)
    
  92. 
    
  93.     def test_iteration_rendered(self):
    
  94.         # iteration works for rendered responses
    
  95.         response = self._response().render()
    
  96.         self.assertEqual(list(response), [b"foo"])
    
  97. 
    
  98.     def test_content_access_unrendered(self):
    
  99.         # unrendered response raises an exception when content is accessed
    
  100.         response = self._response()
    
  101.         self.assertFalse(response.is_rendered)
    
  102.         with self.assertRaises(ContentNotRenderedError):
    
  103.             response.content
    
  104.         self.assertFalse(response.is_rendered)
    
  105. 
    
  106.     def test_content_access_rendered(self):
    
  107.         # rendered response content can be accessed
    
  108.         response = self._response().render()
    
  109.         self.assertEqual(response.content, b"foo")
    
  110. 
    
  111.     def test_set_content(self):
    
  112.         # content can be overridden
    
  113.         response = self._response()
    
  114.         self.assertFalse(response.is_rendered)
    
  115.         response.content = "spam"
    
  116.         self.assertTrue(response.is_rendered)
    
  117.         self.assertEqual(response.content, b"spam")
    
  118.         response.content = "baz"
    
  119.         self.assertEqual(response.content, b"baz")
    
  120. 
    
  121.     def test_dict_context(self):
    
  122.         response = self._response("{{ foo }}{{ processors }}", {"foo": "bar"})
    
  123.         self.assertEqual(response.context_data, {"foo": "bar"})
    
  124.         response.render()
    
  125.         self.assertEqual(response.content, b"bar")
    
  126. 
    
  127.     def test_kwargs(self):
    
  128.         response = self._response(
    
  129.             content_type="application/json", status=504, charset="ascii"
    
  130.         )
    
  131.         self.assertEqual(response.headers["content-type"], "application/json")
    
  132.         self.assertEqual(response.status_code, 504)
    
  133.         self.assertEqual(response.charset, "ascii")
    
  134. 
    
  135.     def test_args(self):
    
  136.         response = SimpleTemplateResponse("", {}, "application/json", 504)
    
  137.         self.assertEqual(response.headers["content-type"], "application/json")
    
  138.         self.assertEqual(response.status_code, 504)
    
  139. 
    
  140.     @require_jinja2
    
  141.     def test_using(self):
    
  142.         response = SimpleTemplateResponse("template_tests/using.html").render()
    
  143.         self.assertEqual(response.content, b"DTL\n")
    
  144.         response = SimpleTemplateResponse(
    
  145.             "template_tests/using.html", using="django"
    
  146.         ).render()
    
  147.         self.assertEqual(response.content, b"DTL\n")
    
  148.         response = SimpleTemplateResponse(
    
  149.             "template_tests/using.html", using="jinja2"
    
  150.         ).render()
    
  151.         self.assertEqual(response.content, b"Jinja2\n")
    
  152. 
    
  153.     def test_post_callbacks(self):
    
  154.         "Rendering a template response triggers the post-render callbacks"
    
  155.         post = []
    
  156. 
    
  157.         def post1(obj):
    
  158.             post.append("post1")
    
  159. 
    
  160.         def post2(obj):
    
  161.             post.append("post2")
    
  162. 
    
  163.         response = SimpleTemplateResponse("first/test.html", {})
    
  164.         response.add_post_render_callback(post1)
    
  165.         response.add_post_render_callback(post2)
    
  166. 
    
  167.         # When the content is rendered, all the callbacks are invoked, too.
    
  168.         response.render()
    
  169.         self.assertEqual(response.content, b"First template\n")
    
  170.         self.assertEqual(post, ["post1", "post2"])
    
  171. 
    
  172.     def test_pickling(self):
    
  173.         # Create a template response. The context is
    
  174.         # known to be unpicklable (e.g., a function).
    
  175.         response = SimpleTemplateResponse(
    
  176.             "first/test.html",
    
  177.             {
    
  178.                 "value": 123,
    
  179.                 "fn": datetime.now,
    
  180.             },
    
  181.         )
    
  182.         with self.assertRaises(ContentNotRenderedError):
    
  183.             pickle.dumps(response)
    
  184. 
    
  185.         # But if we render the response, we can pickle it.
    
  186.         response.render()
    
  187.         pickled_response = pickle.dumps(response)
    
  188.         unpickled_response = pickle.loads(pickled_response)
    
  189. 
    
  190.         self.assertEqual(unpickled_response.content, response.content)
    
  191.         self.assertEqual(
    
  192.             unpickled_response.headers["content-type"], response.headers["content-type"]
    
  193.         )
    
  194.         self.assertEqual(unpickled_response.status_code, response.status_code)
    
  195. 
    
  196.         # ...and the unpickled response doesn't have the
    
  197.         # template-related attributes, so it can't be re-rendered
    
  198.         template_attrs = ("template_name", "context_data", "_post_render_callbacks")
    
  199.         for attr in template_attrs:
    
  200.             self.assertFalse(hasattr(unpickled_response, attr))
    
  201. 
    
  202.         # ...and requesting any of those attributes raises an exception
    
  203.         for attr in template_attrs:
    
  204.             with self.assertRaises(AttributeError):
    
  205.                 getattr(unpickled_response, attr)
    
  206. 
    
  207.     def test_repickling(self):
    
  208.         response = SimpleTemplateResponse(
    
  209.             "first/test.html",
    
  210.             {
    
  211.                 "value": 123,
    
  212.                 "fn": datetime.now,
    
  213.             },
    
  214.         )
    
  215.         with self.assertRaises(ContentNotRenderedError):
    
  216.             pickle.dumps(response)
    
  217. 
    
  218.         response.render()
    
  219.         pickled_response = pickle.dumps(response)
    
  220.         unpickled_response = pickle.loads(pickled_response)
    
  221.         pickle.dumps(unpickled_response)
    
  222. 
    
  223.     def test_pickling_cookie(self):
    
  224.         response = SimpleTemplateResponse(
    
  225.             "first/test.html",
    
  226.             {
    
  227.                 "value": 123,
    
  228.                 "fn": datetime.now,
    
  229.             },
    
  230.         )
    
  231. 
    
  232.         response.cookies["key"] = "value"
    
  233. 
    
  234.         response.render()
    
  235.         pickled_response = pickle.dumps(response, pickle.HIGHEST_PROTOCOL)
    
  236.         unpickled_response = pickle.loads(pickled_response)
    
  237. 
    
  238.         self.assertEqual(unpickled_response.cookies["key"].value, "value")
    
  239. 
    
  240.     def test_headers(self):
    
  241.         response = SimpleTemplateResponse(
    
  242.             "first/test.html",
    
  243.             {"value": 123, "fn": datetime.now},
    
  244.             headers={"X-Foo": "foo"},
    
  245.         )
    
  246.         self.assertEqual(response.headers["X-Foo"], "foo")
    
  247. 
    
  248. 
    
  249. @override_settings(
    
  250.     TEMPLATES=[
    
  251.         {
    
  252.             "BACKEND": "django.template.backends.django.DjangoTemplates",
    
  253.             "DIRS": [TEMPLATE_DIR],
    
  254.             "OPTIONS": {
    
  255.                 "context_processors": [test_processor_name],
    
  256.             },
    
  257.         }
    
  258.     ]
    
  259. )
    
  260. class TemplateResponseTest(SimpleTestCase):
    
  261.     factory = RequestFactory()
    
  262. 
    
  263.     def _response(self, template="foo", *args, **kwargs):
    
  264.         self._request = self.factory.get("/")
    
  265.         template = engines["django"].from_string(template)
    
  266.         return TemplateResponse(self._request, template, *args, **kwargs)
    
  267. 
    
  268.     def test_render(self):
    
  269.         response = self._response("{{ foo }}{{ processors }}").render()
    
  270.         self.assertEqual(response.content, b"yes")
    
  271. 
    
  272.     def test_render_with_requestcontext(self):
    
  273.         response = self._response("{{ foo }}{{ processors }}", {"foo": "bar"}).render()
    
  274.         self.assertEqual(response.content, b"baryes")
    
  275. 
    
  276.     def test_context_processor_priority(self):
    
  277.         # context processors should be overridden by passed-in context
    
  278.         response = self._response(
    
  279.             "{{ foo }}{{ processors }}", {"processors": "no"}
    
  280.         ).render()
    
  281.         self.assertEqual(response.content, b"no")
    
  282. 
    
  283.     def test_kwargs(self):
    
  284.         response = self._response(content_type="application/json", status=504)
    
  285.         self.assertEqual(response.headers["content-type"], "application/json")
    
  286.         self.assertEqual(response.status_code, 504)
    
  287. 
    
  288.     def test_args(self):
    
  289.         response = TemplateResponse(
    
  290.             self.factory.get("/"), "", {}, "application/json", 504
    
  291.         )
    
  292.         self.assertEqual(response.headers["content-type"], "application/json")
    
  293.         self.assertEqual(response.status_code, 504)
    
  294. 
    
  295.     @require_jinja2
    
  296.     def test_using(self):
    
  297.         request = self.factory.get("/")
    
  298.         response = TemplateResponse(request, "template_tests/using.html").render()
    
  299.         self.assertEqual(response.content, b"DTL\n")
    
  300.         response = TemplateResponse(
    
  301.             request, "template_tests/using.html", using="django"
    
  302.         ).render()
    
  303.         self.assertEqual(response.content, b"DTL\n")
    
  304.         response = TemplateResponse(
    
  305.             request, "template_tests/using.html", using="jinja2"
    
  306.         ).render()
    
  307.         self.assertEqual(response.content, b"Jinja2\n")
    
  308. 
    
  309.     def test_pickling(self):
    
  310.         # Create a template response. The context is
    
  311.         # known to be unpicklable (e.g., a function).
    
  312.         response = TemplateResponse(
    
  313.             self.factory.get("/"),
    
  314.             "first/test.html",
    
  315.             {
    
  316.                 "value": 123,
    
  317.                 "fn": datetime.now,
    
  318.             },
    
  319.         )
    
  320.         with self.assertRaises(ContentNotRenderedError):
    
  321.             pickle.dumps(response)
    
  322. 
    
  323.         # But if we render the response, we can pickle it.
    
  324.         response.render()
    
  325.         pickled_response = pickle.dumps(response)
    
  326.         unpickled_response = pickle.loads(pickled_response)
    
  327. 
    
  328.         self.assertEqual(unpickled_response.content, response.content)
    
  329.         self.assertEqual(
    
  330.             unpickled_response.headers["content-type"], response.headers["content-type"]
    
  331.         )
    
  332.         self.assertEqual(unpickled_response.status_code, response.status_code)
    
  333. 
    
  334.         # ...and the unpickled response doesn't have the
    
  335.         # template-related attributes, so it can't be re-rendered
    
  336.         template_attrs = (
    
  337.             "template_name",
    
  338.             "context_data",
    
  339.             "_post_render_callbacks",
    
  340.             "_request",
    
  341.         )
    
  342.         for attr in template_attrs:
    
  343.             self.assertFalse(hasattr(unpickled_response, attr))
    
  344. 
    
  345.         # ...and requesting any of those attributes raises an exception
    
  346.         for attr in template_attrs:
    
  347.             with self.assertRaises(AttributeError):
    
  348.                 getattr(unpickled_response, attr)
    
  349. 
    
  350.     def test_repickling(self):
    
  351.         response = SimpleTemplateResponse(
    
  352.             "first/test.html",
    
  353.             {
    
  354.                 "value": 123,
    
  355.                 "fn": datetime.now,
    
  356.             },
    
  357.         )
    
  358.         with self.assertRaises(ContentNotRenderedError):
    
  359.             pickle.dumps(response)
    
  360. 
    
  361.         response.render()
    
  362.         pickled_response = pickle.dumps(response)
    
  363.         unpickled_response = pickle.loads(pickled_response)
    
  364.         pickle.dumps(unpickled_response)
    
  365. 
    
  366.     def test_headers(self):
    
  367.         response = TemplateResponse(
    
  368.             self.factory.get("/"),
    
  369.             "first/test.html",
    
  370.             {"value": 123, "fn": datetime.now},
    
  371.             headers={"X-Foo": "foo"},
    
  372.         )
    
  373.         self.assertEqual(response.headers["X-Foo"], "foo")
    
  374. 
    
  375. 
    
  376. @modify_settings(
    
  377.     MIDDLEWARE={"append": ["template_tests.test_response.custom_urlconf_middleware"]}
    
  378. )
    
  379. @override_settings(ROOT_URLCONF="template_tests.urls")
    
  380. class CustomURLConfTest(SimpleTestCase):
    
  381.     def test_custom_urlconf(self):
    
  382.         response = self.client.get("/template_response_view/")
    
  383.         self.assertContains(response, "This is where you can find the snark: /snark/")
    
  384. 
    
  385. 
    
  386. @modify_settings(
    
  387.     MIDDLEWARE={
    
  388.         "append": [
    
  389.             "django.middleware.cache.FetchFromCacheMiddleware",
    
  390.             "django.middleware.cache.UpdateCacheMiddleware",
    
  391.         ],
    
  392.     },
    
  393. )
    
  394. @override_settings(
    
  395.     CACHE_MIDDLEWARE_SECONDS=2.0, ROOT_URLCONF="template_tests.alternate_urls"
    
  396. )
    
  397. class CacheMiddlewareTest(SimpleTestCase):
    
  398.     def test_middleware_caching(self):
    
  399.         response = self.client.get("/template_response_view/")
    
  400.         self.assertEqual(response.status_code, 200)
    
  401. 
    
  402.         time.sleep(1.0)
    
  403. 
    
  404.         response2 = self.client.get("/template_response_view/")
    
  405.         self.assertEqual(response2.status_code, 200)
    
  406. 
    
  407.         self.assertEqual(response.content, response2.content)
    
  408. 
    
  409.         time.sleep(2.0)
    
  410. 
    
  411.         # Let the cache expire and test again
    
  412.         response2 = self.client.get("/template_response_view/")
    
  413.         self.assertEqual(response2.status_code, 200)
    
  414. 
    
  415.         self.assertNotEqual(response.content, response2.content)