Repository: sanic-org/sanic-ext
Pull request #194 (2023-05-14 14:47:42)

Adds current request to default template context
As discussed on Discord, this adds the current `request` to the default template context in Jinja.


ahopkins: Thanks for adding this. Can you also create a unit test for each of four possible renderings? https://sanic.dev/en//sanic-ext/templating.html#rendering-a-template-from-a-file
lllama: Added. Let me know if you want the commits squashed or similar. 
ahopkins: @lllama I do squash commits into `main` so it does not matter.
ahopkins: Thanks @lllama 

When done, take it out of "draft" and I will get it merged in.

Patch:
diff --git a/sanic_ext/extensions/templating/engine.py b/sanic_ext/extensions/templating/engine.py
index c3ee578..3c00466 100644
--- a/sanic_ext/extensions/templating/engine.py
+++ b/sanic_ext/extensions/templating/engine.py
@@ -6,6 +6,7 @@ from typing import TYPE_CHECKING, Dict, Optional, Union
 
 from jinja2 import Environment
 from sanic.compat import Header
+from sanic.request import Request
 from sanic.response import HTTPResponse
 
 from sanic_ext.extensions.templating.render import (
@@ -63,6 +64,8 @@ class Templating:
                             params[attr] = value
                     context = context.context
 
+                context["request"] = Request.get_current()
+
                 content = render(**context)
                 if isawaitable(content):
                     content = await content
diff --git a/sanic_ext/extensions/templating/render.py b/sanic_ext/extensions/templating/render.py
index 6fa998f..3718f03 100644
--- a/sanic_ext/extensions/templating/render.py
+++ b/sanic_ext/extensions/templating/render.py
@@ -6,6 +6,7 @@ from typing import TYPE_CHECKING, Any, Dict, Optional, Union
 from sanic import Sanic
 from sanic.compat import Header
 from sanic.exceptions import SanicException
+from sanic.request import Request
 from sanic.response import HTTPResponse
 
 from sanic_ext.exceptions import ExtensionNotFound
@@ -76,6 +77,9 @@ async def render(
             )
 
     kwargs = context if context else {}
+
+    kwargs["request"] = Request.get_current()
+
     if template_name or template_source:
         template = (
             environment.get_template(template_name)


Test patch:
diff --git a/tests/extensions/templating/templates/request_test.html b/tests/extensions/templating/templates/request_test.html
new file mode 100644
index 0000000..74ff683
--- /dev/null
+++ b/tests/extensions/templating/templates/request_test.html
@@ -0,0 +1,1 @@
+{{ request.args.get("test") }}
diff --git a/tests/extensions/templating/test_templating.py b/tests/extensions/templating/test_templating.py
index 9c9bb68..7954cb0 100644
--- a/tests/extensions/templating/test_templating.py
+++ b/tests/extensions/templating/test_templating.py
@@ -110,3 +110,44 @@ def test_url_for():
 
     _, response = app.test_client.get("/one/two/three")
     assert response.text == "url: /one/two/three"
+
+
+def test_default_context():
+    app = Sanic("templating-from-string")
+    app.extend(
+        config={
+            "templating_path_to_templates": Path(__file__).parent / "templates"
+        }
+    )
+
+    template = r"{{ request.args.get('test') }}"
+
+    @app.get("/1")
+    async def handler1(_):
+        return await render(template_source=template)
+
+    @app.get("/2")
+    @app.ext.template("request_test.html")
+    async def handler2(_):
+        return {}
+
+    @app.get("/3")
+    async def handler3(_):
+        return await render("request_test.html", context={}, app=app)
+
+    @app.get("/4")
+    @app.ext.template("request_test.html")
+    async def handler4(_):
+        return await render(context={}, app=app)
+
+    _, response = app.test_client.get("/1?test=passing")
+    assert response.text == "passing"
+
+    _, response = app.test_client.get("/2?test=passing")
+    assert response.text == "passing"
+
+    _, response = app.test_client.get("/3?test=passing")
+    assert response.text == "passing"
+
+    _, response = app.test_client.get("/4?test=passing")
+    assert response.text == "passing"