Skip to content

Commit 14e431e

Browse files
committed
Adding Update on ajax feature
This adds a feature to allow the toolbar to watch for ajax requests and automatically update debug information to show ajax details
1 parent e951347 commit 14e431e

File tree

8 files changed

+94
-25
lines changed

8 files changed

+94
-25
lines changed

debug_toolbar/panels/history/panel.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,18 @@ class HistoryPanel(Panel):
2020
nav_title = _("History")
2121
template = "debug_toolbar/panels/history.html"
2222

23+
def process_request(self, request):
24+
response = super().process_request(request)
25+
if not self.toolbar.should_render_panels():
26+
self.toolbar.store()
27+
store_id = self.toolbar.store_id
28+
sig = SignedDataForm(
29+
initial=HistoryStoreForm(initial={"store_id": store_id}).initial
30+
).initial.get("signed")
31+
response["DJ-TOOLBAR-STORE-ID"] = store_id
32+
response["DJ-TOOLBAR-STORE-ID-SIGNATURE"] = sig
33+
return response
34+
2335
@property
2436
def enabled(self):
2537
# Do not show the history panel if the panels are rendered on request

debug_toolbar/panels/history/views.py

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,6 @@ def history_sidebar(request):
2020
# RESULTS_CACHE_SIZE
2121
return JsonResponse(context)
2222
for panel in toolbar.panels:
23-
if not panel.is_historical:
24-
continue
2523
panel_context = {"panel": panel}
2624
context[panel.panel_id] = {
2725
"button": render_to_string(

debug_toolbar/settings.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@
3737
"SHOW_TEMPLATE_CONTEXT": True,
3838
"SKIP_TEMPLATE_PREFIXES": ("django/forms/widgets/", "admin/widgets/"),
3939
"SQL_WARNING_THRESHOLD": 500, # milliseconds
40+
"UPDATE_ON_AJAX": False,
4041
}
4142

4243

debug_toolbar/static/debug_toolbar/js/history.js

Lines changed: 16 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,37 +1,35 @@
1-
import { $$, ajaxForm } from "./utils.js";
1+
import { $$, ajaxForm, replaceToolbarState } from "./utils.js";
22

33
const djDebug = document.getElementById("djDebug");
44

5-
$$.on(djDebug, "click", ".switchHistory", function (event) {
6-
event.preventDefault();
7-
const newStoreId = this.dataset.storeId;
8-
const tbody = this.closest("tbody");
5+
function switchHistory(newStoreId) {
6+
const formTarget = djDebug.querySelector(
7+
".switchHistory[data-store-id='" + newStoreId + "']"
8+
);
9+
const tbody = formTarget.closest("tbody");
910

1011
const highlighted = tbody.querySelector(".djdt-highlighted");
1112
if (highlighted) {
1213
highlighted.classList.remove("djdt-highlighted");
1314
}
14-
this.closest("tr").classList.add("djdt-highlighted");
15+
formTarget.closest("tr").classList.add("djdt-highlighted");
1516

16-
ajaxForm(this).then(function (data) {
17-
djDebug.setAttribute("data-store-id", newStoreId);
18-
// Check if response is empty, it could be due to an expired store_id.
17+
ajaxForm(formTarget).then(function (data) {
1918
if (Object.keys(data).length === 0) {
2019
const container = document.getElementById("djdtHistoryRequests");
2120
container.querySelector(
2221
'button[data-store-id="' + newStoreId + '"]'
2322
).innerHTML = "Switch [EXPIRED]";
24-
} else {
25-
Object.keys(data).forEach(function (panelId) {
26-
const panel = document.getElementById(panelId);
27-
if (panel) {
28-
panel.outerHTML = data[panelId].content;
29-
document.getElementById("djdt-" + panelId).outerHTML =
30-
data[panelId].button;
31-
}
32-
});
3323
}
24+
//we're already in history panel, so handle locally vs replacing active html
25+
delete data.HistoryPanel;
26+
replaceToolbarState(newStoreId, data);
3427
});
28+
}
29+
30+
$$.on(djDebug, "click", ".switchHistory", function (event) {
31+
event.preventDefault();
32+
switchHistory(this.dataset.storeId);
3533
});
3634

3735
$$.on(djDebug, "click", ".refreshHistory", function (event) {

debug_toolbar/static/debug_toolbar/js/toolbar.js

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { $$, ajax } from "./utils.js";
1+
import { $$, ajax, replaceToolbarState } from "./utils.js";
22

33
function onKeyDown(event) {
44
if (event.keyCode === 27) {
@@ -200,6 +200,9 @@ const djdt = {
200200
} else {
201201
djdt.hide_toolbar();
202202
}
203+
if (djDebug.dataset.sidebarUrl !== undefined) {
204+
djdt.update_on_ajax();
205+
}
203206
},
204207
hide_panels() {
205208
const djDebug = document.getElementById("djDebug");
@@ -253,6 +256,35 @@ const djdt = {
253256
localStorage.setItem("djdt.show", "true");
254257
window.removeEventListener("resize", djdt.ensure_handle_visibility);
255258
},
259+
update_on_ajax() {
260+
const sidebar_url =
261+
document.getElementById("djDebug").dataset.sidebarUrl;
262+
263+
const origOpen = XMLHttpRequest.prototype.open;
264+
XMLHttpRequest.prototype.open = function () {
265+
this.addEventListener("load", function () {
266+
if (
267+
this.responseURL !== "" &&
268+
this.responseURL.indexOf("__debug__") === -1
269+
) {
270+
let signed = this.getResponseHeader(
271+
"dj-toolbar-store-id-signature"
272+
);
273+
const store_id = this.getResponseHeader(
274+
"dj-toolbar-store-id"
275+
);
276+
if (signed !== null) {
277+
signed = encodeURIComponent(signed);
278+
const dest = `${sidebar_url}?signed=${signed}`;
279+
ajax(dest).then(function (data) {
280+
replaceToolbarState(store_id, data);
281+
});
282+
}
283+
}
284+
});
285+
origOpen.apply(this, arguments);
286+
};
287+
},
256288
cookie: {
257289
get(key) {
258290
if (!document.cookie.includes(key)) {

debug_toolbar/static/debug_toolbar/js/utils.js

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -104,4 +104,18 @@ function ajaxForm(element) {
104104
return ajax(url, ajaxData);
105105
}
106106

107-
export { $$, ajax, ajaxForm };
107+
function replaceToolbarState(newStoreId, data) {
108+
const djDebug = document.getElementById("djDebug");
109+
djDebug.setAttribute("data-store-id", newStoreId);
110+
// Check if response is empty, it could be due to an expired store_id.
111+
Object.keys(data).forEach(function (panelId) {
112+
const panel = document.getElementById(panelId);
113+
if (panel) {
114+
panel.outerHTML = data[panelId].content;
115+
document.getElementById("djdt-" + panelId).outerHTML =
116+
data[panelId].button;
117+
}
118+
});
119+
}
120+
121+
export { $$, ajax, ajaxForm, replaceToolbarState };

debug_toolbar/templates/debug_toolbar/base.html

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,9 @@
1111
data-store-id="{{ toolbar.store_id }}"
1212
data-render-panel-url="{% url 'djdt:render_panel' %}"
1313
{% endif %}
14+
{% if toolbar.should_update_on_ajax %}
15+
data-sidebar-url="{% url 'djdt:history_sidebar' %}"
16+
{% endif %}
1417
data-default-show="{% if toolbar.config.SHOW_COLLAPSED %}false{% else %}true{% endif %}"
1518
{{ toolbar.config.ROOT_TAG_EXTRA_ATTRS|safe }}>
1619
<div class="djdt-hidden" id="djDebugToolbar">

debug_toolbar/toolbar.py

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,8 @@
99
from django.core.exceptions import ImproperlyConfigured
1010
from django.template import TemplateSyntaxError
1111
from django.template.loader import render_to_string
12-
from django.urls import path, resolve
13-
from django.urls.exceptions import Resolver404
12+
from django.urls import path, resolve, reverse
13+
from django.urls.exceptions import NoReverseMatch, Resolver404
1414
from django.utils.module_loading import import_string
1515

1616
from debug_toolbar import settings as dt_settings
@@ -88,6 +88,17 @@ def should_render_panels(self):
8888
render_panels = self.request.META["wsgi.multiprocess"]
8989
return render_panels
9090

91+
def should_update_on_ajax(self):
92+
"""Determine whether the toolbar should consider ajax requests as updates or not"""
93+
has_endpoint_registered = False
94+
try:
95+
reverse("djdt:history_sidebar")
96+
has_endpoint_registered = True
97+
except NoReverseMatch:
98+
pass
99+
should_watch = self.config["UPDATE_ON_AJAX"]
100+
return should_watch and has_endpoint_registered
101+
91102
# Handle storing toolbars in memory and fetching them later on
92103

93104
_store = OrderedDict()
@@ -130,7 +141,7 @@ def get_urls(cls):
130141
# Load URLs in a temporary variable for thread safety.
131142
# Global URLs
132143
urlpatterns = [
133-
path("render_panel/", views.render_panel, name="render_panel")
144+
path("render_panel/", views.render_panel, name="render_panel"),
134145
]
135146
# Per-panel URLs
136147
for panel_class in cls.get_panel_classes():

0 commit comments

Comments
 (0)