Skip to content

Commit e0b6f88

Browse files
committed
Refs #910 - After Django introduced template based widget rendering, the number of context values which need prettifying has increased drastically, which can (given enough template contexts to render) cause pages using the template panel to become essentially unresponsive due to the sheer amount of data.
Instead, when first seeing a new context layer, keep a copy of it + the formatted version, and when subsequently seeing the exact same layer (by relying on the implicit __eq__ calls it is expected the lists's __contains__ will do) re-use the existing formatted version. This cuts the number of pformat calls at a minimum by half, and ultimately (far) more than that due to re-use of the same one where possible.
1 parent e02f132 commit e0b6f88

File tree

1 file changed

+69
-40
lines changed

1 file changed

+69
-40
lines changed

debug_toolbar/panels/templates/panel.py

Lines changed: 69 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,16 @@ class TemplatesPanel(Panel):
6969
def __init__(self, *args, **kwargs):
7070
super(TemplatesPanel, self).__init__(*args, **kwargs)
7171
self.templates = []
72+
# Refs GitHub issue #910
73+
# Hold a series of seen dictionaries within Contexts. A dictionary is
74+
# considered seen if it is `in` this list, requiring that the __eq__
75+
# for the dictionary matches. If *anything* in the dictionary is
76+
# different it is counted as a new layer.
77+
self.seen_layers = []
78+
# Holds all dictionaries which have been prettified for output.
79+
# This should align with the seen_layers such that an index here is
80+
# the same as the index there.
81+
self.pformat_layers = []
7282

7383
def _store_template_info(self, sender, **kwargs):
7484
template, context = kwargs['template'], kwargs['context']
@@ -80,47 +90,66 @@ def _store_template_info(self, sender, **kwargs):
8090

8191
context_list = []
8292
for context_layer in context.dicts:
83-
temp_layer = {}
84-
if hasattr(context_layer, 'items'):
85-
for key, value in context_layer.items():
86-
# Replace any request elements - they have a large
87-
# unicode representation and the request data is
88-
# already made available from the Request panel.
89-
if isinstance(value, http.HttpRequest):
90-
temp_layer[key] = '<<request>>'
91-
# Replace the debugging sql_queries element. The SQL
92-
# data is already made available from the SQL panel.
93-
elif key == 'sql_queries' and isinstance(value, list):
94-
temp_layer[key] = '<<sql_queries>>'
95-
# Replace LANGUAGES, which is available in i18n context processor
96-
elif key == 'LANGUAGES' and isinstance(value, tuple):
97-
temp_layer[key] = '<<languages>>'
98-
# QuerySet would trigger the database: user can run the query from SQL Panel
99-
elif isinstance(value, (QuerySet, RawQuerySet)):
100-
model_name = "%s.%s" % (
101-
value.model._meta.app_label, value.model.__name__)
102-
temp_layer[key] = '<<%s of %s>>' % (
103-
value.__class__.__name__.lower(), model_name)
104-
else:
105-
try:
106-
recording(False)
107-
pformat(value) # this MAY trigger a db query
108-
except SQLQueryTriggered:
109-
temp_layer[key] = '<<triggers database query>>'
110-
except UnicodeEncodeError:
111-
temp_layer[key] = '<<unicode encode error>>'
112-
except Exception:
113-
temp_layer[key] = '<<unhandled exception>>'
93+
if hasattr(context_layer, 'items') and context_layer:
94+
# Refs GitHub issue #910
95+
# If we can find this layer in our pseudo-cache then find the
96+
# matching prettified version in the associated list.
97+
key_values = sorted(context_layer.items())
98+
if key_values in self.seen_layers:
99+
index = self.seen_layers.index(key_values)
100+
pformatted = self.pformat_layers[index]
101+
context_list.append(pformatted)
102+
else:
103+
temp_layer = {}
104+
for key, value in context_layer.items():
105+
# Replace any request elements - they have a large
106+
# unicode representation and the request data is
107+
# already made available from the Request panel.
108+
if isinstance(value, http.HttpRequest):
109+
temp_layer[key] = '<<request>>'
110+
# Replace the debugging sql_queries element. The SQL
111+
# data is already made available from the SQL panel.
112+
elif key == 'sql_queries' and isinstance(value, list):
113+
temp_layer[key] = '<<sql_queries>>'
114+
# Replace LANGUAGES, which is available in i18n context processor
115+
elif key == 'LANGUAGES' and isinstance(value, tuple):
116+
temp_layer[key] = '<<languages>>'
117+
# QuerySet would trigger the database: user can run the query from SQL Panel
118+
elif isinstance(value, (QuerySet, RawQuerySet)):
119+
model_name = "%s.%s" % (
120+
value.model._meta.app_label, value.model.__name__)
121+
temp_layer[key] = '<<%s of %s>>' % (
122+
value.__class__.__name__.lower(), model_name)
114123
else:
115-
temp_layer[key] = value
116-
finally:
117-
recording(True)
118-
try:
119-
context_list.append(pformat(temp_layer))
120-
except UnicodeEncodeError:
121-
pass
122-
123-
kwargs['context'] = [force_text(item) for item in context_list]
124+
try:
125+
recording(False)
126+
force_text(value) # this MAY trigger a db query
127+
except SQLQueryTriggered:
128+
temp_layer[key] = '<<triggers database query>>'
129+
except UnicodeEncodeError:
130+
temp_layer[key] = '<<unicode encode error>>'
131+
except Exception:
132+
temp_layer[key] = '<<unhandled exception>>'
133+
else:
134+
temp_layer[key] = value
135+
finally:
136+
recording(True)
137+
# Refs GitHub issue #910
138+
# If we've not seen the layer before then we will add it
139+
# so that if we see it again we can skip formatting it.
140+
self.seen_layers.append(key_values)
141+
# Note: this *ought* to be len(...) - 1 but let's be safe.
142+
index = self.seen_layers.index(key_values)
143+
try:
144+
pformatted = force_text(pformat(temp_layer))
145+
except UnicodeEncodeError:
146+
pass
147+
else:
148+
# Note: this *ought* to be len(...) - 1 but let's be safe.
149+
self.pformat_layers.insert(index, pformatted)
150+
context_list.append(pformatted)
151+
152+
kwargs['context'] = context_list
124153
kwargs['context_processors'] = getattr(context, 'context_processors', None)
125154
self.templates.append(kwargs)
126155

0 commit comments

Comments
 (0)