diff --git a/debug_toolbar/panels/templates/panel.py b/debug_toolbar/panels/templates/panel.py index b288f020e..1f96e97ee 100644 --- a/debug_toolbar/panels/templates/panel.py +++ b/debug_toolbar/panels/templates/panel.py @@ -69,6 +69,16 @@ class TemplatesPanel(Panel): def __init__(self, *args, **kwargs): super(TemplatesPanel, self).__init__(*args, **kwargs) self.templates = [] + # Refs GitHub issue #910 + # Hold a series of seen dictionaries within Contexts. A dictionary is + # considered seen if it is `in` this list, requiring that the __eq__ + # for the dictionary matches. If *anything* in the dictionary is + # different it is counted as a new layer. + self.seen_layers = [] + # Holds all dictionaries which have been prettified for output. + # This should align with the seen_layers such that an index here is + # the same as the index there. + self.pformat_layers = [] def _store_template_info(self, sender, **kwargs): template, context = kwargs['template'], kwargs['context'] @@ -80,47 +90,66 @@ def _store_template_info(self, sender, **kwargs): context_list = [] for context_layer in context.dicts: - temp_layer = {} - if hasattr(context_layer, 'items'): - for key, value in context_layer.items(): - # Replace any request elements - they have a large - # unicode representation and the request data is - # already made available from the Request panel. - if isinstance(value, http.HttpRequest): - temp_layer[key] = '<>' - # Replace the debugging sql_queries element. The SQL - # data is already made available from the SQL panel. - elif key == 'sql_queries' and isinstance(value, list): - temp_layer[key] = '<>' - # Replace LANGUAGES, which is available in i18n context processor - elif key == 'LANGUAGES' and isinstance(value, tuple): - temp_layer[key] = '<>' - # QuerySet would trigger the database: user can run the query from SQL Panel - elif isinstance(value, (QuerySet, RawQuerySet)): - model_name = "%s.%s" % ( - value.model._meta.app_label, value.model.__name__) - temp_layer[key] = '<<%s of %s>>' % ( - value.__class__.__name__.lower(), model_name) - else: - try: - recording(False) - pformat(value) # this MAY trigger a db query - except SQLQueryTriggered: - temp_layer[key] = '<>' - except UnicodeEncodeError: - temp_layer[key] = '<>' - except Exception: - temp_layer[key] = '<>' + if hasattr(context_layer, 'items') and context_layer: + # Refs GitHub issue #910 + # If we can find this layer in our pseudo-cache then find the + # matching prettified version in the associated list. + key_values = sorted(context_layer.items()) + if key_values in self.seen_layers: + index = self.seen_layers.index(key_values) + pformatted = self.pformat_layers[index] + context_list.append(pformatted) + else: + temp_layer = {} + for key, value in context_layer.items(): + # Replace any request elements - they have a large + # unicode representation and the request data is + # already made available from the Request panel. + if isinstance(value, http.HttpRequest): + temp_layer[key] = '<>' + # Replace the debugging sql_queries element. The SQL + # data is already made available from the SQL panel. + elif key == 'sql_queries' and isinstance(value, list): + temp_layer[key] = '<>' + # Replace LANGUAGES, which is available in i18n context processor + elif key == 'LANGUAGES' and isinstance(value, tuple): + temp_layer[key] = '<>' + # QuerySet would trigger the database: user can run the query from SQL Panel + elif isinstance(value, (QuerySet, RawQuerySet)): + model_name = "%s.%s" % ( + value.model._meta.app_label, value.model.__name__) + temp_layer[key] = '<<%s of %s>>' % ( + value.__class__.__name__.lower(), model_name) else: - temp_layer[key] = value - finally: - recording(True) - try: - context_list.append(pformat(temp_layer)) - except UnicodeEncodeError: - pass - - kwargs['context'] = [force_text(item) for item in context_list] + try: + recording(False) + force_text(value) # this MAY trigger a db query + except SQLQueryTriggered: + temp_layer[key] = '<>' + except UnicodeEncodeError: + temp_layer[key] = '<>' + except Exception: + temp_layer[key] = '<>' + else: + temp_layer[key] = value + finally: + recording(True) + # Refs GitHub issue #910 + # If we've not seen the layer before then we will add it + # so that if we see it again we can skip formatting it. + self.seen_layers.append(key_values) + # Note: this *ought* to be len(...) - 1 but let's be safe. + index = self.seen_layers.index(key_values) + try: + pformatted = force_text(pformat(temp_layer)) + except UnicodeEncodeError: + pass + else: + # Note: this *ought* to be len(...) - 1 but let's be safe. + self.pformat_layers.insert(index, pformatted) + context_list.append(pformatted) + + kwargs['context'] = context_list kwargs['context_processors'] = getattr(context, 'context_processors', None) self.templates.append(kwargs)