Skip to content

Commit 6004f59

Browse files
Instrument the Django Jinja2 template backend.
- Add jinja2 template to example app. - Switch to the render function to include context. It instruments the single template render, but not the inherited templates and I'm guessing not the included templates either. I suspect we're going to have to patch jinja templates more robustly than relying on the django jinja backend template class. Co-Authored-By: Tim Schilling <[email protected]>
1 parent c79f249 commit 6004f59

File tree

12 files changed

+74
-5
lines changed

12 files changed

+74
-5
lines changed
+23
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
import functools
2+
3+
from django.template.backends.jinja2 import Template as JinjaTemplate
4+
from django.template.context import make_context
5+
from django.test.signals import template_rendered
6+
7+
8+
def patch_jinja_render():
9+
orig_render = JinjaTemplate.render
10+
11+
@functools.wraps(orig_render)
12+
def wrapped_render(self, context=None, request=None):
13+
# This patching of render only instruments the rendering
14+
# of the immediate template. It won't include the parent template(s).
15+
self.name = self.template.name
16+
template_rendered.send(
17+
sender=self, template=self, context=make_context(context, request)
18+
)
19+
return orig_render(self, context, request)
20+
21+
if JinjaTemplate.render != wrapped_render:
22+
JinjaTemplate.original_render = JinjaTemplate.render
23+
JinjaTemplate.render = wrapped_render

debug_toolbar/panels/templates/panel.py

+2
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
from debug_toolbar.panels import Panel
1515
from debug_toolbar.panels.sql.tracking import SQLQueryTriggered, allow_sql
1616
from debug_toolbar.panels.templates import views
17+
from debug_toolbar.panels.templates.jinja2 import patch_jinja_render
1718

1819
# Monkey-patch to enable the template_rendered signal. The receiver returns
1920
# immediately when the panel is disabled to keep the overhead small.
@@ -25,6 +26,7 @@
2526
Template.original_render = Template._render
2627
Template._render = instrumented_test_render
2728

29+
patch_jinja_render()
2830

2931
# Monkey-patch to store items added by template context processors. The
3032
# overhead is sufficiently small to justify enabling it unconditionally.

docs/changes.rst

+4
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,10 @@ Change log
44
Pending
55
-------
66

7+
* Instrument the Django Jinja2 template backend. This only instruments
8+
the immediate template that's rendered. It will not provide stats on
9+
any parent templates.
10+
711
4.4.3 (2024-07-04)
812
------------------
913

example/settings.py

+7-1
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,12 @@
4141
STATIC_URL = "/static/"
4242

4343
TEMPLATES = [
44+
{
45+
"NAME": "jinja2",
46+
"BACKEND": "django.template.backends.jinja2.Jinja2",
47+
"APP_DIRS": True,
48+
"DIRS": [os.path.join(BASE_DIR, "example", "templates", "jinja2")],
49+
},
4450
{
4551
"BACKEND": "django.template.backends.django.DjangoTemplates",
4652
"APP_DIRS": True,
@@ -54,7 +60,7 @@
5460
"django.contrib.messages.context_processors.messages",
5561
],
5662
},
57-
}
63+
},
5864
]
5965

6066
USE_TZ = True

example/templates/index.html

+1
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
<h1>Index of Tests</h1>
1010
{% cache 10 index_cache %}
1111
<ul>
12+
<li><a href="{% url 'jinja' %}">Jinja2</a></li>
1213
<li><a href="/jquery/">jQuery 3.3.1</a></li>
1314
<li><a href="/mootools/">MooTools 1.6.0</a></li>
1415
<li><a href="/prototype/">Prototype 1.7.3.0</a></li>

example/templates/jinja2/index.jinja

+12
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
<!DOCTYPE html>
2+
<html>
3+
<head>
4+
<meta http-equiv="content-type" content="text/html; charset=utf-8">
5+
<title>jinja Test</title>
6+
</head>
7+
<body>
8+
<h1>jinja Test</h1>
9+
{{ foo }}
10+
{% for i in range(10) %}{{ i }}{% endfor %} {# Jinja2 supports range(), Django templates do not #}
11+
</body>
12+
</html>

example/urls.py

+2-1
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
from django.views.generic import TemplateView
44

55
from debug_toolbar.toolbar import debug_toolbar_urls
6-
from example.views import increment
6+
from example.views import increment, jinja2_view
77

88
urlpatterns = [
99
path("", TemplateView.as_view(template_name="index.html"), name="home"),
@@ -12,6 +12,7 @@
1212
TemplateView.as_view(template_name="bad_form.html"),
1313
name="bad_form",
1414
),
15+
path("jinja/", jinja2_view, name="jinja"),
1516
path("jquery/", TemplateView.as_view(template_name="jquery/index.html")),
1617
path("mootools/", TemplateView.as_view(template_name="mootools/index.html")),
1718
path("prototype/", TemplateView.as_view(template_name="prototype/index.html")),

example/views.py

+5
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
from django.http import JsonResponse
2+
from django.shortcuts import render
23

34

45
def increment(request):
@@ -8,3 +9,7 @@ def increment(request):
89
value = 1
910
request.session["value"] = value
1011
return JsonResponse({"value": value})
12+
13+
14+
def jinja2_view(request):
15+
return render(request, "index.jinja", {"foo": "bar"}, using="jinja2")

tests/panels/test_template.py

+4-1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
from unittest import expectedFailure
2+
13
import django
24
from django.contrib.auth.models import User
35
from django.template import Context, RequestContext, Template
@@ -135,11 +137,12 @@ def test_lazyobject_eval(self):
135137
DEBUG=True, DEBUG_TOOLBAR_PANELS=["debug_toolbar.panels.templates.TemplatesPanel"]
136138
)
137139
class JinjaTemplateTestCase(IntegrationTestCase):
140+
@expectedFailure
138141
def test_django_jinja2(self):
139142
r = self.client.get("/regular_jinja/foobar/")
140143
self.assertContains(r, "Test for foobar (Jinja)")
141144
self.assertContains(r, "<h3>Templates (2 rendered)</h3>")
142-
self.assertContains(r, "<small>jinja2/basic.jinja</small>")
145+
self.assertContains(r, "<small>basic.jinja</small>")
143146

144147

145148
def context_processor(request):

tests/templates/jinja2/base.html

+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
<!DOCTYPE html>
2+
<html>
3+
<head>
4+
<title>{{ title }}</title>
5+
</head>
6+
<body>
7+
{% block content %}{% endblock %}
8+
</body>
9+
</html>

tests/templates/jinja2/basic.jinja

+4-1
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,5 @@
11
{% extends 'base.html' %}
2-
{% block content %}Test for {{ title }} (Jinja){% endblock %}
2+
{% block content %}
3+
Test for {{ title }} (Jinja)
4+
{% for i in range(10) %}{{ i }}{% endfor %} {# Jinja2 supports range(), Django templates do not #}
5+
{% endblock %}

tests/views.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ def json_view(request):
4848

4949

5050
def regular_jinjia_view(request, title):
51-
return render(request, "jinja2/basic.jinja", {"title": title})
51+
return render(request, "basic.jinja", {"title": title}, using="jinja2")
5252

5353

5454
def listcomp_view(request):

0 commit comments

Comments
 (0)