From 4cadf088d8b4c48fc056222d46d4d9e7e25bcae9 Mon Sep 17 00:00:00 2001
From: Tim Schilling
Date: Fri, 22 Jan 2021 12:09:18 -0600
Subject: [PATCH 01/28] Correct RENDER_PANELS functionality and when enabled
disable HistoryPanel.
The render panels setting used to control whether the toolbar would keep
the contents of the panels in the background or render the toolbar on
every page. The history panel relies on loading panels in the background.
If panels should be rendered on the request, then the history panel is
can't/shouldn't work.
---
debug_toolbar/panels/history/panel.py | 6 ++++
.../templates/debug_toolbar/base.html | 5 ++-
.../debug_toolbar/includes/panel_content.html | 6 ++--
debug_toolbar/toolbar.py | 4 +++
docs/changes.rst | 4 +++
docs/configuration.rst | 2 ++
docs/installation.rst | 8 +++++
docs/panels.rst | 9 +++--
tests/test_integration.py | 35 ++++++++++++++++++-
9 files changed, 72 insertions(+), 7 deletions(-)
diff --git a/debug_toolbar/panels/history/panel.py b/debug_toolbar/panels/history/panel.py
index e80d8c93a..393d91d4f 100644
--- a/debug_toolbar/panels/history/panel.py
+++ b/debug_toolbar/panels/history/panel.py
@@ -20,6 +20,12 @@ class HistoryPanel(Panel):
nav_title = _("History")
template = "debug_toolbar/panels/history.html"
+ @property
+ def enabled(self):
+ # Do not show the history panel if the panels are rendered on request
+ # rather than loaded via ajax.
+ return super().enabled and not self.toolbar.should_render_panels()
+
@property
def is_historical(self):
"""The HistoryPanel should not be included in the historical panels."""
diff --git a/debug_toolbar/templates/debug_toolbar/base.html b/debug_toolbar/templates/debug_toolbar/base.html
index 78b9b7fe2..7abc5476f 100644
--- a/debug_toolbar/templates/debug_toolbar/base.html
+++ b/debug_toolbar/templates/debug_toolbar/base.html
@@ -7,7 +7,10 @@
{% endblock %}
diff --git a/debug_toolbar/templates/debug_toolbar/includes/panel_content.html b/debug_toolbar/templates/debug_toolbar/includes/panel_content.html
index 2c1a1b195..8c2e446b1 100644
--- a/debug_toolbar/templates/debug_toolbar/includes/panel_content.html
+++ b/debug_toolbar/templates/debug_toolbar/includes/panel_content.html
@@ -7,11 +7,11 @@
{{ panel.title }}
- {% if toolbar.store_id %}
+ {% if toolbar.should_render_panels %}
+
{{ panel.content }}
+ {% else %}
- {% else %}
-
{{ panel.content }}
{% endif %}
diff --git a/debug_toolbar/toolbar.py b/debug_toolbar/toolbar.py
index 9638ba1f8..3d8937126 100644
--- a/debug_toolbar/toolbar.py
+++ b/debug_toolbar/toolbar.py
@@ -78,6 +78,10 @@ def render_toolbar(self):
raise
def should_render_panels(self):
+ """Determine whether the panels should be rendered during the request
+
+ If False, the panels will be loaded via Ajax.
+ """
render_panels = self.config["RENDER_PANELS"]
if render_panels is None:
render_panels = self.request.META["wsgi.multiprocess"]
diff --git a/docs/changes.rst b/docs/changes.rst
index 6207b6797..04c7cf2dc 100644
--- a/docs/changes.rst
+++ b/docs/changes.rst
@@ -9,6 +9,10 @@ Next version
* Added ``PRETTIFY_SQL`` configuration option to support controlling
SQL token grouping. By default it's set to True. When set to False,
a performance improvement can be seen by the SQL panel.
+* ``HistoryPanel`` will be disabled when ``RENDER_PANELS`` is ``True``
+ or if it's not set, but the server is running with multiple processes.
+* Fixes ``RENDER_PANELS`` functionality so that when ``True`` panels are
+ rendered during the request and not loaded asynchronously.
3.2 (2020-12-03)
diff --git a/docs/configuration.rst b/docs/configuration.rst
index 92b493000..01d5b8436 100644
--- a/docs/configuration.rst
+++ b/docs/configuration.rst
@@ -66,6 +66,8 @@ Toolbar options
The toolbar searches for this string in the HTML and inserts itself just
before.
+.. _RENDER_PANELS:
+
* ``RENDER_PANELS``
Default: ``None``
diff --git a/docs/installation.rst b/docs/installation.rst
index 87b964e12..eb60c1277 100644
--- a/docs/installation.rst
+++ b/docs/installation.rst
@@ -147,3 +147,11 @@ And for Apache:
.. _JavaScript module: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Modules
.. _CORS errors: https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS/Errors/CORSMissingAllowOrigin
.. _Access-Control-Allow-Origin header: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Allow-Origin
+
+Django Channels & Async
+^^^^^^^^^^^^^^^^^^^^^^^
+
+The Debug Toolbar currently doesn't support Django Channels or async projects.
+If you are using Django channels are having issues getting panels to load,
+please review the documentation for the configuration option
+:ref:`RENDER_PANELS `.
diff --git a/docs/panels.rst b/docs/panels.rst
index c21e90801..f83c0770d 100644
--- a/docs/panels.rst
+++ b/docs/panels.rst
@@ -17,6 +17,11 @@ History
This panel shows the history of requests made and allows switching to a past
snapshot of the toolbar to view that request's stats.
+.. caution::
+ If :ref:`RENDER_PANELS ` configuration option is set to
+ ``True`` or if the server runs with multiple processes, the History Panel
+ will be disabled.
+
Version
~~~~~~~
@@ -184,9 +189,9 @@ URL: https://github.com/danyi1212/django-windowsauth
Path: ``windows_auth.panels.LDAPPanel``
-LDAP Operations performed during the request, including timing, request and response messages,
+LDAP Operations performed during the request, including timing, request and response messages,
the entries received, write changes list, stack-tracing and error debugging.
-This panel also shows connection usage metrics when it is collected.
+This panel also shows connection usage metrics when it is collected.
`Check out the docs `_.
Line Profiler
diff --git a/tests/test_integration.py b/tests/test_integration.py
index 8c28f6c6e..f1f1cb1bb 100644
--- a/tests/test_integration.py
+++ b/tests/test_integration.py
@@ -289,11 +289,44 @@ def test_sql_profile_checks_show_toolbar(self):
self.assertEqual(response.status_code, 404)
@override_settings(DEBUG_TOOLBAR_CONFIG={"RENDER_PANELS": True})
- def test_data_store_id_not_rendered_when_none(self):
+ def test_render_panels_in_request(self):
+ """
+ Test that panels are are rendered during the request with
+ RENDER_PANELS=TRUE
+ """
url = "/regular/basic/"
response = self.client.get(url)
self.assertIn(b'id="djDebug"', response.content)
+ # Verify the store id is not included.
self.assertNotIn(b"data-store-id", response.content)
+ # Verify the history panel was disabled
+ self.assertIn(
+ b' ',
+ response.content,
+ )
+ # Verify the a panel was rendered
+ self.assertIn(b"Response headers", response.content)
+
+ @override_settings(DEBUG_TOOLBAR_CONFIG={"RENDER_PANELS": False})
+ def test_load_panels(self):
+ """
+ Test that panels are not rendered during the request with
+ RENDER_PANELS=False
+ """
+ url = "/execute_sql/"
+ response = self.client.get(url)
+ self.assertIn(b'id="djDebug"', response.content)
+ # Verify the store id is included.
+ self.assertIn(b"data-store-id", response.content)
+ # Verify the history panel was not disabled
+ self.assertNotIn(
+ b' ',
+ response.content,
+ )
+ # Verify the a panel was not rendered
+ self.assertNotIn(b"Response headers", response.content)
def test_view_returns_template_response(self):
response = self.client.get("/template_response/basic/")
From 765fbd14523390e859b77c3fb1c093544c7a39ce Mon Sep 17 00:00:00 2001
From: Tim Schilling
Date: Fri, 12 Feb 2021 08:05:47 -0600
Subject: [PATCH 02/28] Improved RENDER_PANELS documentation and change log.
---
docs/changes.rst | 7 ++++---
docs/configuration.rst | 9 ++++++---
2 files changed, 10 insertions(+), 6 deletions(-)
diff --git a/docs/changes.rst b/docs/changes.rst
index 04c7cf2dc..c59a59fd2 100644
--- a/docs/changes.rst
+++ b/docs/changes.rst
@@ -9,9 +9,10 @@ Next version
* Added ``PRETTIFY_SQL`` configuration option to support controlling
SQL token grouping. By default it's set to True. When set to False,
a performance improvement can be seen by the SQL panel.
-* ``HistoryPanel`` will be disabled when ``RENDER_PANELS`` is ``True``
- or if it's not set, but the server is running with multiple processes.
-* Fixes ``RENDER_PANELS`` functionality so that when ``True`` panels are
+* Disabled ``HistoryPanel`` when ``RENDER_PANELS`` is ``True``
+ or if ``RENDER_PANELS`` is ``None`` and the WSGI container is
+ running with multiple processes.
+* Fixed ``RENDER_PANELS`` functionality so that when ``True`` panels are
rendered during the request and not loaded asynchronously.
diff --git a/docs/configuration.rst b/docs/configuration.rst
index 01d5b8436..0d7cd87c4 100644
--- a/docs/configuration.rst
+++ b/docs/configuration.rst
@@ -73,14 +73,17 @@ Toolbar options
Default: ``None``
If set to ``False``, the debug toolbar will keep the contents of panels in
- memory on the server and load them on demand. If set to ``True``, it will
- render panels inside every page. This may slow down page rendering but it's
+ memory on the server and load them on demand.
+
+ If set to ``True``, it will disable ``HistoryPanel`` and render panels
+ inside every page. This may slow down page rendering but it's
required on multi-process servers, for example if you deploy the toolbar in
production (which isn't recommended).
The default value of ``None`` tells the toolbar to automatically do the
right thing depending on whether the WSGI container runs multiple processes.
- This setting allows you to force a different behavior if needed.
+ This setting allows you to force a different behavior if needed. If the
+ WSGI container runs multiple processes, it will disable ``HistoryPanel``.
* ``RESULTS_CACHE_SIZE``
From dc3f80be5cacfa469f8817caeacd9175c7f94b80 Mon Sep 17 00:00:00 2001
From: Tim Schilling
Date: Fri, 12 Feb 2021 09:04:45 -0600
Subject: [PATCH 03/28] Add async to the allowed spellings.
---
docs/spelling_wordlist.txt | 1 +
1 file changed, 1 insertion(+)
diff --git a/docs/spelling_wordlist.txt b/docs/spelling_wordlist.txt
index ede7915a1..7250ed750 100644
--- a/docs/spelling_wordlist.txt
+++ b/docs/spelling_wordlist.txt
@@ -37,3 +37,4 @@ unhashable
uWSGI
validator
Werkzeug
+async
From 43f4304789a0c62ee1437e7e7b8b601be8e2001b Mon Sep 17 00:00:00 2001
From: Hasan Ramezani
Date: Wed, 7 Apr 2021 14:19:42 +0200
Subject: [PATCH 04/28] Drop support for Django 3.0.
---
setup.cfg | 1 -
tox.ini | 13 ++++++-------
2 files changed, 6 insertions(+), 8 deletions(-)
diff --git a/setup.cfg b/setup.cfg
index 93a297b31..6a52c962b 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -14,7 +14,6 @@ classifiers =
Environment :: Web Environment
Framework :: Django
Framework :: Django :: 2.2
- Framework :: Django :: 3.0
Framework :: Django :: 3.1
Intended Audience :: Developers
License :: OSI Approved :: BSD License
diff --git a/tox.ini b/tox.ini
index 192e51f67..882e19eb4 100644
--- a/tox.ini
+++ b/tox.ini
@@ -3,14 +3,13 @@ envlist =
docs
style
readme
- py{36,37}-dj{22,30,31,32}-sqlite
- py{38,39}-dj{22,30,31,32,main}-sqlite
- py{36,37,38,39}-dj{22,30,31,32}-{postgresql,mysql}
+ py{36,37}-dj{22,31,32}-sqlite
+ py{38,39}-dj{22,31,32,main}-sqlite
+ py{36,37,38,39}-dj{22,31,32}-{postgresql,mysql}
[testenv]
deps =
dj22: Django==2.2.*
- dj30: Django==3.0.*
dj31: Django==3.1.*
dj32: Django>=3.2a1,<4.0
sqlite: mock
@@ -43,19 +42,19 @@ whitelist_externals = make
pip_pre = True
commands = make coverage TEST_ARGS='{posargs:tests}'
-[testenv:py{36,37,38,39}-dj{22,30,31,32}-postgresql]
+[testenv:py{36,37,38,39}-dj{22,31,32}-postgresql]
setenv =
{[testenv]setenv}
DB_BACKEND = postgresql
DB_PORT = {env:DB_PORT:5432}
-[testenv:py{36,37,38,39}-dj{22,30,31,32}-mysql]
+[testenv:py{36,37,38,39}-dj{22,31,32}-mysql]
setenv =
{[testenv]setenv}
DB_BACKEND = mysql
DB_PORT = {env:DB_PORT:3306}
-[testenv:py{36,37,38,39}-dj{22,30,31,32,main}-sqlite]
+[testenv:py{36,37,38,39}-dj{22,31,32,main}-sqlite]
setenv =
{[testenv]setenv}
DB_BACKEND = sqlite3
From e0b13324b131e594b5d4216f4b972f5ffce91fea Mon Sep 17 00:00:00 2001
From: Hasan Ramezani
Date: Wed, 7 Apr 2021 17:20:49 +0200
Subject: [PATCH 05/28] Add Django 3.2 to setup classifiers.
---
setup.cfg | 1 +
1 file changed, 1 insertion(+)
diff --git a/setup.cfg b/setup.cfg
index 6a52c962b..bb037466c 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -15,6 +15,7 @@ classifiers =
Framework :: Django
Framework :: Django :: 2.2
Framework :: Django :: 3.1
+ Framework :: Django :: 3.2
Intended Audience :: Developers
License :: OSI Approved :: BSD License
Operating System :: OS Independent
From 8f4bc8677df635c68ad792bc7ecc21c369ff2a5d Mon Sep 17 00:00:00 2001
From: Karthikeyan Singaravelan
Date: Sun, 18 Apr 2021 05:45:39 +0000
Subject: [PATCH 06/28] Use current_thread instead of currentThread method that
was deprecated in Python 3.10
---
debug_toolbar/panels/logging.py | 2 +-
debug_toolbar/panels/staticfiles.py | 2 +-
debug_toolbar/utils.py | 4 ++--
3 files changed, 4 insertions(+), 4 deletions(-)
diff --git a/debug_toolbar/panels/logging.py b/debug_toolbar/panels/logging.py
index f296fc882..a7252c2bb 100644
--- a/debug_toolbar/panels/logging.py
+++ b/debug_toolbar/panels/logging.py
@@ -78,6 +78,6 @@ def process_request(self, request):
def generate_stats(self, request, response):
records = collector.get_collection()
- self._records[threading.currentThread()] = records
+ self._records[threading.current_thread()] = records
collector.clear_collection()
self.record_stats({"records": records})
diff --git a/debug_toolbar/panels/staticfiles.py b/debug_toolbar/panels/staticfiles.py
index 9a15c0f28..ef6af5d3e 100644
--- a/debug_toolbar/panels/staticfiles.py
+++ b/debug_toolbar/panels/staticfiles.py
@@ -118,7 +118,7 @@ def process_request(self, request):
def generate_stats(self, request, response):
used_paths = collector.get_collection()
- self._paths[threading.currentThread()] = used_paths
+ self._paths[threading.current_thread()] = used_paths
self.record_stats(
{
diff --git a/debug_toolbar/utils.py b/debug_toolbar/utils.py
index cc5d74477..41889872a 100644
--- a/debug_toolbar/utils.py
+++ b/debug_toolbar/utils.py
@@ -252,14 +252,14 @@ def get_collection(self, thread=None):
is provided, returns a list for the current thread.
"""
if thread is None:
- thread = threading.currentThread()
+ thread = threading.current_thread()
if thread not in self.collections:
self.collections[thread] = []
return self.collections[thread]
def clear_collection(self, thread=None):
if thread is None:
- thread = threading.currentThread()
+ thread = threading.current_thread()
if thread in self.collections:
del self.collections[thread]
From 7f2ad72898cfeb92bbd6702c80faebc7d63288f1 Mon Sep 17 00:00:00 2001
From: Tim Schilling
Date: Mon, 19 Apr 2021 08:34:58 -0500
Subject: [PATCH 07/28] Support JS events when loading a panel. (#1441)
* Support panel rendered JS event.
This fixes the problem of the Timer Panel not inserting the browser
timings section after being loaded via the HistoryPanel.
These events could be wired into to better render panels or support
a more dynamic toolbar and/or panel.
Co-authored-by: Matthias Kestenholz
---
debug_toolbar/panels/__init__.py | 4 +
.../static/debug_toolbar/js/timer.js | 128 ++++++++++--------
.../static/debug_toolbar/js/toolbar.js | 16 ++-
.../static/debug_toolbar/js/utils.js | 15 ++
docs/changes.rst | 9 +-
docs/panels.rst | 33 ++++-
6 files changed, 143 insertions(+), 62 deletions(-)
diff --git a/debug_toolbar/panels/__init__.py b/debug_toolbar/panels/__init__.py
index ec3445c1e..8fd433c63 100644
--- a/debug_toolbar/panels/__init__.py
+++ b/debug_toolbar/panels/__init__.py
@@ -107,6 +107,10 @@ def content(self):
def scripts(self):
"""
Scripts used by the HTML content of the panel when it's displayed.
+
+ When a panel is rendered on the frontend, the ``djdt.panel.render``
+ JavaScript event will be dispatched. The scripts can listen for
+ this event to support dynamic functionality.
"""
return []
diff --git a/debug_toolbar/static/debug_toolbar/js/timer.js b/debug_toolbar/static/debug_toolbar/js/timer.js
index 1d4ac19d8..70d3fe5a2 100644
--- a/debug_toolbar/static/debug_toolbar/js/timer.js
+++ b/debug_toolbar/static/debug_toolbar/js/timer.js
@@ -1,59 +1,75 @@
-const timingOffset = performance.timing.navigationStart,
- timingEnd = performance.timing.loadEventEnd,
- totalTime = timingEnd - timingOffset;
-function getLeft(stat) {
- return ((performance.timing[stat] - timingOffset) / totalTime) * 100.0;
-}
-function getCSSWidth(stat, endStat) {
- let width =
- ((performance.timing[endStat] - performance.timing[stat]) / totalTime) *
- 100.0;
- // Calculate relative percent (same as sql panel logic)
- width = (100.0 * width) / (100.0 - getLeft(stat));
- return width < 1 ? "2px" : width + "%";
-}
-function addRow(tbody, stat, endStat) {
- const row = document.createElement("tr");
- if (endStat) {
- // Render a start through end bar
- row.innerHTML =
- "" +
- stat.replace("Start", "") +
- " " +
- ' ' +
- "" +
- (performance.timing[stat] - timingOffset) +
- " (+" +
- (performance.timing[endStat] - performance.timing[stat]) +
- ") ";
- row.querySelector("rect").setAttribute(
- "width",
- getCSSWidth(stat, endStat)
- );
- } else {
- // Render a point in time
- row.innerHTML =
- "" +
- stat +
- " " +
- ' ' +
- "" +
- (performance.timing[stat] - timingOffset) +
- " ";
- row.querySelector("rect").setAttribute("width", 2);
+import { $$ } from "./utils.js";
+
+function insertBrowserTiming() {
+ console.log(["inserted"]);
+ const timingOffset = performance.timing.navigationStart,
+ timingEnd = performance.timing.loadEventEnd,
+ totalTime = timingEnd - timingOffset;
+ function getLeft(stat) {
+ return ((performance.timing[stat] - timingOffset) / totalTime) * 100.0;
+ }
+ function getCSSWidth(stat, endStat) {
+ let width =
+ ((performance.timing[endStat] - performance.timing[stat]) /
+ totalTime) *
+ 100.0;
+ // Calculate relative percent (same as sql panel logic)
+ width = (100.0 * width) / (100.0 - getLeft(stat));
+ return width < 1 ? "2px" : width + "%";
+ }
+ function addRow(tbody, stat, endStat) {
+ const row = document.createElement("tr");
+ if (endStat) {
+ // Render a start through end bar
+ row.innerHTML =
+ "" +
+ stat.replace("Start", "") +
+ " " +
+ ' ' +
+ "" +
+ (performance.timing[stat] - timingOffset) +
+ " (+" +
+ (performance.timing[endStat] - performance.timing[stat]) +
+ ") ";
+ row.querySelector("rect").setAttribute(
+ "width",
+ getCSSWidth(stat, endStat)
+ );
+ } else {
+ // Render a point in time
+ row.innerHTML =
+ "" +
+ stat +
+ " " +
+ ' ' +
+ "" +
+ (performance.timing[stat] - timingOffset) +
+ " ";
+ row.querySelector("rect").setAttribute("width", 2);
+ }
+ row.querySelector("rect").setAttribute("x", getLeft(stat));
+ tbody.appendChild(row);
+ }
+
+ const browserTiming = document.getElementById("djDebugBrowserTiming");
+ // Determine if the browser timing section has already been rendered.
+ if (browserTiming.classList.contains("djdt-hidden")) {
+ const tbody = document.getElementById("djDebugBrowserTimingTableBody");
+ // This is a reasonably complete and ordered set of timing periods (2 params) and events (1 param)
+ addRow(tbody, "domainLookupStart", "domainLookupEnd");
+ addRow(tbody, "connectStart", "connectEnd");
+ addRow(tbody, "requestStart", "responseEnd"); // There is no requestEnd
+ addRow(tbody, "responseStart", "responseEnd");
+ addRow(tbody, "domLoading", "domComplete"); // Spans the events below
+ addRow(tbody, "domInteractive");
+ addRow(tbody, "domContentLoadedEventStart", "domContentLoadedEventEnd");
+ addRow(tbody, "loadEventStart", "loadEventEnd");
+ browserTiming.classList.remove("djdt-hidden");
}
- row.querySelector("rect").setAttribute("x", getLeft(stat));
- tbody.appendChild(row);
}
-const tbody = document.getElementById("djDebugBrowserTimingTableBody");
-// This is a reasonably complete and ordered set of timing periods (2 params) and events (1 param)
-addRow(tbody, "domainLookupStart", "domainLookupEnd");
-addRow(tbody, "connectStart", "connectEnd");
-addRow(tbody, "requestStart", "responseEnd"); // There is no requestEnd
-addRow(tbody, "responseStart", "responseEnd");
-addRow(tbody, "domLoading", "domComplete"); // Spans the events below
-addRow(tbody, "domInteractive");
-addRow(tbody, "domContentLoadedEventStart", "domContentLoadedEventEnd");
-addRow(tbody, "loadEventStart", "loadEventEnd");
-document.getElementById("djDebugBrowserTiming").classList.remove("djdt-hidden");
+const djDebug = document.getElementById("djDebug");
+// Insert the browser timing now since it's possible for this
+// script to miss the initial panel load event.
+insertBrowserTiming();
+$$.onPanelRender(djDebug, "TimerPanel", insertBrowserTiming);
diff --git a/debug_toolbar/static/debug_toolbar/js/toolbar.js b/debug_toolbar/static/debug_toolbar/js/toolbar.js
index d739cbdb3..579548d4e 100644
--- a/debug_toolbar/static/debug_toolbar/js/toolbar.js
+++ b/debug_toolbar/static/debug_toolbar/js/toolbar.js
@@ -20,7 +20,8 @@ const djdt = {
if (!this.className) {
return;
}
- const current = document.getElementById(this.className);
+ const panelId = this.className;
+ const current = document.getElementById(panelId);
if ($$.visible(current)) {
djdt.hide_panels();
} else {
@@ -39,13 +40,24 @@ const djdt = {
window.location
);
url.searchParams.append("store_id", store_id);
- url.searchParams.append("panel_id", this.className);
+ url.searchParams.append("panel_id", panelId);
ajax(url).then(function (data) {
inner.previousElementSibling.remove(); // Remove AJAX loader
inner.innerHTML = data.content;
$$.executeScripts(data.scripts);
$$.applyStyles(inner);
+ djDebug.dispatchEvent(
+ new CustomEvent("djdt.panel.render", {
+ detail: { panelId: panelId },
+ })
+ );
});
+ } else {
+ djDebug.dispatchEvent(
+ new CustomEvent("djdt.panel.render", {
+ detail: { panelId: panelId },
+ })
+ );
}
}
}
diff --git a/debug_toolbar/static/debug_toolbar/js/utils.js b/debug_toolbar/static/debug_toolbar/js/utils.js
index 4683b319f..da810aad0 100644
--- a/debug_toolbar/static/debug_toolbar/js/utils.js
+++ b/debug_toolbar/static/debug_toolbar/js/utils.js
@@ -7,6 +7,21 @@ const $$ = {
}
});
},
+ onPanelRender(root, panelId, fn) {
+ /*
+ This is a helper function to attach a handler for a `djdt.panel.render`
+ event of a specific panel.
+
+ root: The container element that the listener should be attached to.
+ panelId: The Id of the panel.
+ fn: A function to execute when the event is triggered.
+ */
+ root.addEventListener("djdt.panel.render", function (event) {
+ if (event.detail.panelId === panelId) {
+ fn.call(event);
+ }
+ });
+ },
show(element) {
element.classList.remove("djdt-hidden");
},
diff --git a/docs/changes.rst b/docs/changes.rst
index 19aaab12d..435574265 100644
--- a/docs/changes.rst
+++ b/docs/changes.rst
@@ -16,8 +16,13 @@ Next version
* Added ``PRETTIFY_SQL`` configuration option to support controlling
SQL token grouping. By default it's set to True. When set to False,
a performance improvement can be seen by the SQL panel.
-* Fixed issue with toolbar expecting URL paths to start with `/__debug__/`
- while the documentation indicates it's not required.
+* Added a JavaScript event when a panel loads of the format
+ ``djdt.panel.[PanelId]`` where PanelId is the ``panel_id`` property
+ of the panel's Python class. Listening for this event corrects the bug
+ in the Timer Panel in which it didn't insert the browser timings
+ after switching requests in the History Panel.
+* Fixed issue with the toolbar expecting URL paths to start with
+ ``/__debug__/`` while the documentation indicates it's not required.
3.2 (2020-12-03)
----------------
diff --git a/docs/panels.rst b/docs/panels.rst
index c21e90801..dfa5ec92a 100644
--- a/docs/panels.rst
+++ b/docs/panels.rst
@@ -184,9 +184,9 @@ URL: https://github.com/danyi1212/django-windowsauth
Path: ``windows_auth.panels.LDAPPanel``
-LDAP Operations performed during the request, including timing, request and response messages,
+LDAP Operations performed during the request, including timing, request and response messages,
the entries received, write changes list, stack-tracing and error debugging.
-This panel also shows connection usage metrics when it is collected.
+This panel also shows connection usage metrics when it is collected.
`Check out the docs `_.
Line Profiler
@@ -402,3 +402,32 @@ common methods available.
.. js:function:: djdt.show_toolbar
Shows the toolbar.
+
+Events
+^^^^^^
+
+.. js:attribute:: djdt.panel.render
+
+ This is an event raised when a panel is rendered. It has the property
+ ``detail.panelId`` which identifies which panel has been loaded. This
+ event can be useful when creating custom scripts to process the HTML
+ further.
+
+ An example of this for the ``CustomPanel`` would be:
+
+.. code-block:: javascript
+
+ import { $$ } from "./utils.js";
+ function addCustomMetrics() {
+ // Logic to process/add custom metrics here.
+
+ // Be sure to cover the case of this function being called twice
+ // due to file being loaded asynchronously.
+ }
+ const djDebug = document.getElementById("djDebug");
+ $$.onPanelRender(djDebug, "CustomPanel", addCustomMetrics);
+ // Since a panel's scripts are loaded asynchronously, it's possible that
+ // the above statement would occur after the djdt.panel.render event has
+ // been raised. To account for that, the rendering function should be
+ // called here as well.
+ addCustomMetrics();
From 73c24d62b5f0043ff6cb650b0be82451f5fcbbcc Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Fran=C3=A7ois=20Freitag?=
Date: Fri, 23 Apr 2021 12:08:18 +0200
Subject: [PATCH 08/28] Use default app config discovery
Django 3.2 deprecated default_app_config.
https://docs.djangoproject.com/en/3.2/releases/3.2/#automatic-appconfig-discovery
---
debug_toolbar/__init__.py | 5 ++++-
1 file changed, 4 insertions(+), 1 deletion(-)
diff --git a/debug_toolbar/__init__.py b/debug_toolbar/__init__.py
index 84eea7d7a..05d62a515 100644
--- a/debug_toolbar/__init__.py
+++ b/debug_toolbar/__init__.py
@@ -1,3 +1,5 @@
+import django
+
__all__ = ["VERSION"]
@@ -9,4 +11,5 @@
urls = "debug_toolbar.toolbar", "djdt"
-default_app_config = "debug_toolbar.apps.DebugToolbarConfig"
+if django.VERSION < (3, 2):
+ default_app_config = "debug_toolbar.apps.DebugToolbarConfig"
From 29605f84d18dcc969226c50ddcafb475a445eb6b Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Fran=C3=A7ois=20Freitag?=
Date: Fri, 23 Apr 2021 12:20:54 +0200
Subject: [PATCH 09/28] Use twine to check generated package and readme
The setup.py check command is deprecated. Use twine check instead.
Rename the tox environment to packaging, it now checks the package.
---
.github/workflows/test.yml | 2 +-
setup.cfg | 1 +
tox.ini | 13 +++++++++----
3 files changed, 11 insertions(+), 5 deletions(-)
diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml
index 1b7cd30c3..667846cdc 100644
--- a/.github/workflows/test.yml
+++ b/.github/workflows/test.yml
@@ -216,4 +216,4 @@ jobs:
python -m pip install --upgrade tox
- name: Test with tox
- run: tox -e docs,style,readme
+ run: tox -e docs,style,packaging
diff --git a/setup.cfg b/setup.cfg
index 82ca17b97..b65f3a887 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -3,6 +3,7 @@ name = django-debug-toolbar
version = 3.2.1
description = A configurable set of panels that display various debug information about the current request/response.
long_description = file: README.rst
+long_description_content_type = text/x-rst
author = Rob Hudson
author_email = rob@cogit8.org
url = https://github.com/jazzband/django-debug-toolbar
diff --git a/tox.ini b/tox.ini
index 882e19eb4..c3bb0bac2 100644
--- a/tox.ini
+++ b/tox.ini
@@ -2,7 +2,7 @@
envlist =
docs
style
- readme
+ packaging
py{36,37}-dj{22,31,32}-sqlite
py{38,39}-dj{22,31,32,main}-sqlite
py{36,37,38,39}-dj{22,31,32}-{postgresql,mysql}
@@ -74,9 +74,14 @@ deps =
isort>=5.0.2
skip_install = true
-[testenv:readme]
-commands = python setup.py check -r -s
-deps = readme_renderer
+[testenv:packaging]
+commands =
+ python setup.py sdist bdist_wheel
+ twine check --strict dist/*
+deps =
+ readme_renderer
+ twine
+ wheel
skip_install = true
[gh-actions]
From f5279478d3795f0adeacac3b1f7c990b1470b2ef Mon Sep 17 00:00:00 2001
From: Gildardo Adrian Maravilla Jacome
Date: Wed, 20 Jan 2021 06:36:22 -0600
Subject: [PATCH 10/28] Show template context on included templates
---
debug_toolbar/utils.py | 9 ++++++-
tests/panels/test_sql.py | 45 +++++++++++++++++++++++++++++++
tests/templates/sql/flat.html | 4 +++
tests/templates/sql/included.html | 1 +
tests/templates/sql/nested.html | 4 +++
5 files changed, 62 insertions(+), 1 deletion(-)
create mode 100644 tests/templates/sql/flat.html
create mode 100644 tests/templates/sql/included.html
create mode 100644 tests/templates/sql/nested.html
diff --git a/debug_toolbar/utils.py b/debug_toolbar/utils.py
index 41889872a..f8fb68538 100644
--- a/debug_toolbar/utils.py
+++ b/debug_toolbar/utils.py
@@ -135,7 +135,14 @@ def get_template_context(node, context, context_lines=3):
def get_template_source_from_exception_info(node, context):
- exception_info = context.template.get_exception_info(Exception("DDT"), node.token)
+ if context.template.origin == node.origin:
+ exception_info = context.template.get_exception_info(
+ Exception("DDT"), node.token
+ )
+ else:
+ exception_info = context.render_context.template.get_exception_info(
+ Exception("DDT"), node.token
+ )
line = exception_info["line"]
source_lines = exception_info["source_lines"]
name = exception_info["name"]
diff --git a/tests/panels/test_sql.py b/tests/panels/test_sql.py
index 9ed2b1a6e..84b789868 100644
--- a/tests/panels/test_sql.py
+++ b/tests/panels/test_sql.py
@@ -1,4 +1,5 @@
import datetime
+import os
import unittest
import django
@@ -396,3 +397,47 @@ def test_prettify_sql(self):
self.panel.generate_stats(self.request, response)
self.assertEqual(len(self.panel._queries), 1)
self.assertEqual(pretty_sql, self.panel._queries[-1][1]["sql"])
+
+ @override_settings(
+ DEBUG=True,
+ )
+ def test_flat_template_information(self):
+ """
+ Test case for when the query is used in a flat template hierarchy
+ (without included templates).
+ """
+ self.assertEqual(len(self.panel._queries), 0)
+
+ users = User.objects.all()
+ render(self.request, "sql/flat.html", {"users": users})
+
+ self.assertEqual(len(self.panel._queries), 1)
+
+ query = self.panel._queries[0]
+ template_info = query[1]["template_info"]
+ template_name = os.path.basename(template_info["name"])
+ self.assertEqual(template_name, "flat.html")
+ self.assertEqual(template_info["context"][2]["content"].strip(), "{{ users }}")
+ self.assertEqual(template_info["context"][2]["highlight"], True)
+
+ @override_settings(
+ DEBUG=True,
+ )
+ def test_nested_template_information(self):
+ """
+ Test case for when the query is used in a nested template
+ hierarchy (with included templates).
+ """
+ self.assertEqual(len(self.panel._queries), 0)
+
+ users = User.objects.all()
+ render(self.request, "sql/nested.html", {"users": users})
+
+ self.assertEqual(len(self.panel._queries), 1)
+
+ query = self.panel._queries[0]
+ template_info = query[1]["template_info"]
+ template_name = os.path.basename(template_info["name"])
+ self.assertEqual(template_name, "included.html")
+ self.assertEqual(template_info["context"][0]["content"].strip(), "{{ users }}")
+ self.assertEqual(template_info["context"][0]["highlight"], True)
diff --git a/tests/templates/sql/flat.html b/tests/templates/sql/flat.html
new file mode 100644
index 000000000..058dbe043
--- /dev/null
+++ b/tests/templates/sql/flat.html
@@ -0,0 +1,4 @@
+{% extends "base.html" %}
+{% block content %}
+ {{ users }}
+{% endblock %}
diff --git a/tests/templates/sql/included.html b/tests/templates/sql/included.html
new file mode 100644
index 000000000..87d2e1f70
--- /dev/null
+++ b/tests/templates/sql/included.html
@@ -0,0 +1 @@
+{{ users }}
diff --git a/tests/templates/sql/nested.html b/tests/templates/sql/nested.html
new file mode 100644
index 000000000..8558e2d45
--- /dev/null
+++ b/tests/templates/sql/nested.html
@@ -0,0 +1,4 @@
+{% extends "base.html" %}
+{% block content %}
+ {% include "sql/included.html" %}
+{% endblock %}
From 3b5f91006d2be85d4c763491e7aca31b8036327f Mon Sep 17 00:00:00 2001
From: Gildardo Adrian Maravilla Jacome
Date: Sun, 9 May 2021 02:21:59 -0500
Subject: [PATCH 11/28] Fix lint error
---
debug_toolbar/panels/history/panel.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/debug_toolbar/panels/history/panel.py b/debug_toolbar/panels/history/panel.py
index 4494bbfcd..0382cad62 100644
--- a/debug_toolbar/panels/history/panel.py
+++ b/debug_toolbar/panels/history/panel.py
@@ -15,7 +15,7 @@
class HistoryPanel(Panel):
- """ A panel to display History """
+ """A panel to display History"""
title = _("History")
nav_title = _("History")
From d36b7d6b768600212c89c26cfd8355842e78eec1 Mon Sep 17 00:00:00 2001
From: Matthias Kestenholz
Date: Fri, 14 May 2021 17:48:52 +0200
Subject: [PATCH 12/28] Fix the Y positioning of the handle
- The additional translateY makes djdt.top=0 move the handle to the top
of the browser window.
- Because the handle is rotated we have to subtract its offsetWidth, not
its offsetHeight to keep it in bounds at the bottom of the window.
---
debug_toolbar/static/debug_toolbar/css/toolbar.css | 2 +-
debug_toolbar/static/debug_toolbar/js/toolbar.js | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/debug_toolbar/static/debug_toolbar/css/toolbar.css b/debug_toolbar/static/debug_toolbar/css/toolbar.css
index 6bbfd3d73..13fbbed0a 100644
--- a/debug_toolbar/static/debug_toolbar/css/toolbar.css
+++ b/debug_toolbar/static/debug_toolbar/css/toolbar.css
@@ -198,7 +198,7 @@
#djDebug #djDebugToolbarHandle {
position: fixed;
- transform: rotate(-90deg);
+ transform: translateY(-100%) rotate(-90deg);
transform-origin: right bottom;
background-color: #fff;
border: 1px solid #111;
diff --git a/debug_toolbar/static/debug_toolbar/js/toolbar.js b/debug_toolbar/static/debug_toolbar/js/toolbar.js
index 579548d4e..becf39818 100644
--- a/debug_toolbar/static/debug_toolbar/js/toolbar.js
+++ b/debug_toolbar/static/debug_toolbar/js/toolbar.js
@@ -222,7 +222,7 @@ const djdt = {
if (handleTop) {
handleTop = Math.min(
handleTop,
- window.innerHeight - handle.offsetHeight
+ window.innerHeight - handle.offsetWidth
);
handle.style.top = handleTop + "px";
}
From ebd4ae3544d2a1aa230df63826e600b6469d3eb6 Mon Sep 17 00:00:00 2001
From: Matthias Kestenholz
Date: Fri, 14 May 2021 17:55:56 +0200
Subject: [PATCH 13/28] Fix #1471: Make the handle stay in bounds when resizing
---
.../static/debug_toolbar/js/toolbar.js | 23 +++++++++++--------
docs/changes.rst | 2 ++
2 files changed, 15 insertions(+), 10 deletions(-)
diff --git a/debug_toolbar/static/debug_toolbar/js/toolbar.js b/debug_toolbar/static/debug_toolbar/js/toolbar.js
index becf39818..c17ee3ea2 100644
--- a/debug_toolbar/static/debug_toolbar/js/toolbar.js
+++ b/debug_toolbar/static/debug_toolbar/js/toolbar.js
@@ -190,6 +190,7 @@ const djdt = {
requestAnimationFrame(function () {
djdt.handleDragged = false;
});
+ djdt.ensure_handle_visibility();
}
});
const show =
@@ -210,6 +211,15 @@ const djdt = {
e.classList.remove("djdt-active");
});
},
+ ensure_handle_visibility() {
+ const handle = document.getElementById("djDebugToolbarHandle");
+ // set handle position
+ const handleTop = Math.min(
+ localStorage.getItem("djdt.top") || 0,
+ window.innerHeight - handle.offsetWidth
+ );
+ handle.style.top = handleTop + "px";
+ },
hide_toolbar() {
djdt.hide_panels();
@@ -217,16 +227,8 @@ const djdt = {
const handle = document.getElementById("djDebugToolbarHandle");
$$.show(handle);
- // set handle position
- let handleTop = localStorage.getItem("djdt.top");
- if (handleTop) {
- handleTop = Math.min(
- handleTop,
- window.innerHeight - handle.offsetWidth
- );
- handle.style.top = handleTop + "px";
- }
-
+ djdt.ensure_handle_visibility();
+ window.addEventListener("resize", djdt.ensure_handle_visibility);
document.removeEventListener("keydown", onKeyDown);
localStorage.setItem("djdt.show", "false");
@@ -249,6 +251,7 @@ const djdt = {
$$.hide(document.getElementById("djDebugToolbarHandle"));
$$.show(document.getElementById("djDebugToolbar"));
localStorage.setItem("djdt.show", "true");
+ window.removeEventListener("resize", djdt.ensure_handle_visibility);
},
cookie: {
get(key) {
diff --git a/docs/changes.rst b/docs/changes.rst
index 435574265..e66c5fd52 100644
--- a/docs/changes.rst
+++ b/docs/changes.rst
@@ -4,6 +4,8 @@ Change log
Next version
------------
+* Ensured that the handle stays within bounds when resizing the window.
+
3.2.1 (2021-04-14)
------------------
From 7d4c5300a58c0afd81e6be947122e72a0c1a6ab0 Mon Sep 17 00:00:00 2001
From: Sam <3115209+saemideluxe@users.noreply.github.com>
Date: Fri, 4 Jun 2021 16:37:36 +0700
Subject: [PATCH 14/28] Fixes #1239
This is just PoC. I wouldn't know how to test this properly.
---
debug_toolbar/panels/sql/tracking.py | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/debug_toolbar/panels/sql/tracking.py b/debug_toolbar/panels/sql/tracking.py
index 75366802c..3dcaa9863 100644
--- a/debug_toolbar/panels/sql/tracking.py
+++ b/debug_toolbar/panels/sql/tracking.py
@@ -55,6 +55,10 @@ def cursor(*args, **kwargs):
)
def chunked_cursor(*args, **kwargs):
+ # prevent double wrapping
+ # solves https://github.com/jazzband/django-debug-toolbar/issues/1239
+ if hasattr(connection._djdt_cursor, "__wrapped__"):
+ return connection._djdt_cursor(*args, **kwargs)
return state.Wrapper(
connection._djdt_chunked_cursor(*args, **kwargs), connection, panel
)
From d0f7cfab5e0077c0eba81944f00a7c13dd452f33 Mon Sep 17 00:00:00 2001
From: Sam
Date: Mon, 7 Jun 2021 11:57:50 +0700
Subject: [PATCH 15/28] fix: failing postgresql tests
---
debug_toolbar/panels/sql/tracking.py | 9 ++++-----
1 file changed, 4 insertions(+), 5 deletions(-)
diff --git a/debug_toolbar/panels/sql/tracking.py b/debug_toolbar/panels/sql/tracking.py
index 3dcaa9863..3be525118 100644
--- a/debug_toolbar/panels/sql/tracking.py
+++ b/debug_toolbar/panels/sql/tracking.py
@@ -57,11 +57,10 @@ def cursor(*args, **kwargs):
def chunked_cursor(*args, **kwargs):
# prevent double wrapping
# solves https://github.com/jazzband/django-debug-toolbar/issues/1239
- if hasattr(connection._djdt_cursor, "__wrapped__"):
- return connection._djdt_cursor(*args, **kwargs)
- return state.Wrapper(
- connection._djdt_chunked_cursor(*args, **kwargs), connection, panel
- )
+ cursor = connection._djdt_chunked_cursor(*args, **kwargs)
+ if not isinstance(cursor, NormalCursorWrapper):
+ return state.Wrapper(cursor, connection, panel)
+ return cursor
connection.cursor = cursor
connection.chunked_cursor = chunked_cursor
From 7e2c161badf2b2c34ae2991aeaca977231e8e75c Mon Sep 17 00:00:00 2001
From: Sam
Date: Thu, 10 Jun 2021 15:44:40 +0700
Subject: [PATCH 16/28] fix: make sure to check against correct wrapper class
---
debug_toolbar/panels/sql/tracking.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/debug_toolbar/panels/sql/tracking.py b/debug_toolbar/panels/sql/tracking.py
index 3be525118..e7da994d9 100644
--- a/debug_toolbar/panels/sql/tracking.py
+++ b/debug_toolbar/panels/sql/tracking.py
@@ -58,7 +58,7 @@ def chunked_cursor(*args, **kwargs):
# prevent double wrapping
# solves https://github.com/jazzband/django-debug-toolbar/issues/1239
cursor = connection._djdt_chunked_cursor(*args, **kwargs)
- if not isinstance(cursor, NormalCursorWrapper):
+ if not isinstance(cursor, state.Wrapper):
return state.Wrapper(cursor, connection, panel)
return cursor
From eee102e0319a87d7d0c527f0224d62f34fae66ef Mon Sep 17 00:00:00 2001
From: James Addison
Date: Mon, 21 Jun 2021 19:06:37 +0100
Subject: [PATCH 17/28] Extract common base cursor wrapper class
---
debug_toolbar/panels/sql/tracking.py | 10 +++++++---
1 file changed, 7 insertions(+), 3 deletions(-)
diff --git a/debug_toolbar/panels/sql/tracking.py b/debug_toolbar/panels/sql/tracking.py
index e7da994d9..2ed691344 100644
--- a/debug_toolbar/panels/sql/tracking.py
+++ b/debug_toolbar/panels/sql/tracking.py
@@ -58,7 +58,7 @@ def chunked_cursor(*args, **kwargs):
# prevent double wrapping
# solves https://github.com/jazzband/django-debug-toolbar/issues/1239
cursor = connection._djdt_chunked_cursor(*args, **kwargs)
- if not isinstance(cursor, state.Wrapper):
+ if not isinstance(cursor, BaseCursorWrapper):
return state.Wrapper(cursor, connection, panel)
return cursor
@@ -74,7 +74,11 @@ def unwrap_cursor(connection):
del connection.chunked_cursor
-class ExceptionCursorWrapper:
+class BaseCursorWrapper:
+ pass
+
+
+class ExceptionCursorWrapper(BaseCursorWrapper):
"""
Wraps a cursor and raises an exception on any operation.
Used in Templates panel.
@@ -87,7 +91,7 @@ def __getattr__(self, attr):
raise SQLQueryTriggered()
-class NormalCursorWrapper:
+class NormalCursorWrapper(BaseCursorWrapper):
"""
Wraps a cursor and logs queries.
"""
From b6b0b4261b41c52b8a6b11f0bb2ca9a77fd686fa Mon Sep 17 00:00:00 2001
From: eriktelepovsky
Date: Thu, 24 Jun 2021 11:19:53 +0200
Subject: [PATCH 18/28] updated Slovak translation
---
.gitignore | 1 +
debug_toolbar/locale/sk/LC_MESSAGES/django.mo | Bin 9378 -> 9981 bytes
debug_toolbar/locale/sk/LC_MESSAGES/django.po | 149 ++++++++----------
3 files changed, 66 insertions(+), 84 deletions(-)
diff --git a/.gitignore b/.gitignore
index 564e7b8cc..df5a2d10c 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,6 +1,7 @@
*.pyc
*.DS_Store
*~
+.idea
build
.coverage
dist
diff --git a/debug_toolbar/locale/sk/LC_MESSAGES/django.mo b/debug_toolbar/locale/sk/LC_MESSAGES/django.mo
index a26e3cc7b87414440c05389a4ee5fc54829deb76..4867b2a7ad1778a59fd7c6d105340ac599022fc6 100644
GIT binary patch
delta 3639
zcmajhe{5Cd9mnzK_J{JLtk42&p|$5)DDB+sdM_2EwM9mOVXc5ger#AcZ7=28a&LEk
zbX+D}5M&O+0v?$$K?OvOn$69+L=22lbIBZD!iArLyz?d_5Y8<~vZ%j1iZaj&zu%O78Dy+vr
zT!#1J>)3$#lX8Y|3T4-t!0D79vE?IJZ%oE~hfFCKeu?ApiuGgU$C%F;bCa-6L)}-2
z6R;K;vkBtOI1l+TZTz|wJFyUla48O>o;!_2^lyGlMh%=p-S}%%Ll$0GVS7szNvAK)DP8>*oyT4z;F
z4XS}g%yxtt@FIJCsV#S*?pue9(YUCU9YTK0UVi1{Vbs<24J=8$nM0I=?
z)!}>AUtxgq?@4eDPY-0T#>_i>LD5{@Bcso9mA)^O>ftty0
zkY~*m)a&>uYUGtub4wP$TFUeB1?)zxVBxgf^$DnfPDMReg<63IR6nh@yacs%nbl;p
zSDWmG0i;i}9o6tRQ4M;i29BVX?gVOUUbENVu-AWR%RfQg_jA-hE}~}s7yO6Mn1AE5
zdjGGM<=)GuSg#h{i084g!kDA@5$?jnGmPoS=`(Yucc=9r>ceu}mQUhb%I}~i@+oS9
zr7Wk)x1a_R!a}|O3$qzxmY_~$2kL?QQG2@yHS>Pte3=+(FL$F>-~=APmr*mmg9D-&
zFGW4K*18eZUO%dzD2~(nzm1IcVmE#n51`J#WmE(ILOob;ORhW-`zV*98rp{H_+bp<
zLDZiA2sP99un{ky23%a7yMG#H)Ibdx89;S3AK4YtX6x6W9^8a#aI?LhKrQKGsDVC<
zY^!+@b!JYZ+I!nxe;0Mg&)ND*)vUiByh23-euO%7HBN4+>rgAvjP2NoYT$9K!Q-gC
ze;d`#Z&6!!2{qt9V;+8t8qihLMDzT)>qY)dZmCMB(4P4*dpJ<?TkptV7ML&z85N
zI^KsG;M1rN%Xd)&d>z%^S=3gXLv{QITmBF=&`&ZpGmaC*`7x!)K{t)48FipW-i4cS
z19spj?!{u3A%;(&4rjqE{v_jc)N|d)C&Kh&Ek0zgKZm?nCi4LqjjV9CF}Gncp1>9?
zz>BEsmryJ5CsarOv`(T`4YV3nKL@n}3s8r0Icg==TDPE9Dvb;t&=TRO05q-FmSWVoR7E#bPD1C|O&gS$F2>Z_tWIM2R+0h1U
zVGcGE8qhG2M+^{m5p~41w9IC-b-b-6MC>A>gwp4T1kt7fX-^K*LI2rf^4~2ODL@;zE0>6tss;(5q-+ov>d}WAHhMJUw|8kJ8l2AI``dPOljj-
zP43UB&PNOJC~*(5gQzC<5}zfO5=uI7O7{~@gbpk(%2=93ZfLB;>#g}WQnBrZ%9Z4U
z#Mf+H%6dC;#<*ARNW^(x*?=m39#_`GJ*N$BMF
z5RVW`2z^%5L?O{jDBVHK&*rYL+h)pJ377cl$kvH}%`Xb~r23uJvDjdb8z1Q|ek|Yf
zmsX5iE_pw%CU9SPIFg9OqJefd8E$q$jiF$mab6(QbsC-8S|_Sp&{;^SnZkcxYHM{uxts26b(**-IChg?&o6XB&CcBV
zmI>>M
zf5}OVzS$Ftr@f~uW_w2~R{7H2nTiUZtKTCe#S1xAM73?KdvJcdqh$(HbWiccTe+pf?g3IaRePzoRSZCL_HA
zqo?g?%(aTK;a#uU60E>h=tdjzq8L%wq0f4$fQ0a+ku{n)LsxQNts9ZmrJy4;3lL-T(jq
delta 3278
zcmZ|Rd2EzL7{~Ex3qmQ-QjT7>tn1ESBr9U^CFdUCtPayxy2mCyOpII-Wp8FbmVLLKsa~pf$9qfzua42@C
z(H+xq5@w;^Yrx)&Z?;iU2QQ%>+=uGuRogy-N=7rXSaTAUi8C05*HF)Qpx*z}wjbh5
z+C3tJ?=Qe~+AERdnB5r8_~rnWG<+S^!RPjYi>QIWLk;i)YNEf``@h-t1JrY&%!V;d
z1S+HPs0n8A6NckZTT+A?XFdk>;!-L~>1x!>s!#)Nw(dYT?H5rK_z;zev#964#2owv
zmD+GdiN*oQ+)Ntky^*MK#@P0xe&k;>n`Up!vMxr=umrWDGNf;_$==_C>exX|^q6hG
zjatwd)O)R{_r5}n^CNOB%q`T!@AV`9s)R9J8D&jGbvzu^p%-=P3+(;Hs6)91^;{*c
z!v=I=JL>Q~MD;tA{qSKn>iJUC0xAPkc*bl+eSS@-8NZ84)mco%FR&T!qEh+FfZ+W@
zs0qG_dhY~k<)5Gix@6mJs4e>mwM8BFdf+}4`ZN&(gB>TMI!r-z;6|m)huV@Ndw+_(
zKi9UGpq?v1O=JUV<=gSw5My4(jkL>RjLF68$e9Y5fwAl#7mBbEZ=xSp#05W}Pm%31
zk?}!?p#G5L+V)gbhs#h4X+o{=xNV<1p&QX4Hz_
zLEZltmD($)3EoDw({!L#)FUa_VHE0q4C-_z+w0l(`UuS8`dHKu|q1)DTvH_RCH;S%J-F-@omy@i>0
z3XkJ8)D{GGkzYNq7nOnisDX}H-$PBX)wVC7R^E;}Tz60zxNq%~63kE%YJx*i8OTIs
zdIYM!S;)8nvyh5Tqu;s?wKvtMt=NJZa36-ChZspbPs}B{3b#8mp|ma{!ib%ON*VD2
zp)FKdN@!a*>HFVJWh^nDAS<0|AB|l_tI(clTU2HRF}tjSV_=@P?Lu5mloC(cYq`i!
zou93}WU5rr#pmpm8q6b<9+fS`1Va0&LWYe?70QSpW*R<2tRSWm+X-#wbYc!MlTa2{
z5-J;s3PM|~Z3(RAN8h-L4oo?r?NE6(h$%uHDwS<+XW5i~nt29!XL8KD;p1aCqDmo)i5IU6g
zgqzTT4kapyg~S-5t9Yq2DF4~^$|QV>(C1c1sMH4O{N2EBF_A^+fa(vA%3`7$v49vt
zbQOIS%ZQ1@R$@JoMl2yz9@URt+|hD6;`gwY_apa&wJhp?D|C9XyQsF*Utd;T<(}xT
zFU@y(Jvn)9PoCTBb9r;}ecnuu*W>9mdzO1{X=7R4e?FHpI^UO@>G8Cb3>qAgom=3l
zsc5M6R}`jK6&B`XxW>6Wu6$Q|)!0I>D>c~iN)
ztUI*2(TRwg=<8kjjsAye^s5oyrir&zoj;LPM8xpti;(rtk~(6ve)M~
j=a2YTRh(<7@|&X4x_ZB>+;#qq;FZ$, 2012
# Juraj Bubniak , 2013
@@ -11,23 +11,22 @@ msgstr ""
"Project-Id-Version: Django Debug Toolbar\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2014-04-25 21:52+0200\n"
-"PO-Revision-Date: 2014-04-25 19:53+0000\n"
+"PO-Revision-Date: 2021-06-24 11:19+0200\n"
"Last-Translator: Aymeric Augustin \n"
"Language-Team: Slovak (http://www.transifex.com/projects/p/django-debug-toolbar/language/sk/)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Language: sk\n"
-"Plural-Forms: nplurals=3; plural=(n==1) ? 0 : (n>=2 && n<=4) ? 1 : 2;\n"
+"Plural-Forms: nplurals=4; plural=(n % 1 == 0 && n == 1 ? 0 : n % 1 == 0 && n >= 2 && n <= 4 ? 1 : n % 1 != 0 ? 2: 3);\n"
+"X-Generator: Poedit 2.4.2\n"
#: apps.py:11
msgid "Debug Toolbar"
-msgstr ""
+msgstr "Debug Toolbar"
#: views.py:14
-msgid ""
-"Data for this panel isn't available anymore. Please reload the page and "
-"retry."
+msgid "Data for this panel isn't available anymore. Please reload the page and retry."
msgstr "Dáta pre tento panel už nie sú k dispozícii. Načítajte si prosím stránku a skúste to znova."
#: panels/cache.py:191
@@ -39,8 +38,9 @@ msgstr "Cache"
msgid "%(cache_calls)d call in %(time).2fms"
msgid_plural "%(cache_calls)d calls in %(time).2fms"
msgstr[0] "%(cache_calls)d volanie za %(time).2fms"
-msgstr[1] "%(cache_calls)d volaní za %(time).2fms"
+msgstr[1] "%(cache_calls)d volania za %(time).2fms"
msgstr[2] "%(cache_calls)d volaní za %(time).2fms"
+msgstr[3] "%(cache_calls)d volaní za %(time).2fms"
#: panels/cache.py:204
#, python-format
@@ -48,7 +48,8 @@ msgid "Cache calls from %(count)d backend"
msgid_plural "Cache calls from %(count)d backends"
msgstr[0] "Cache volania z %(count)d backendu"
msgstr[1] "Cache volania z %(count)d backendov"
-msgstr[2] "Cache volania z %(count)d backendov"
+msgstr[2] "Cache volania z %(count)d backendu"
+msgstr[3] "Cache volania z %(count)d backendov"
#: panels/headers.py:35
msgid "Headers"
@@ -63,8 +64,9 @@ msgstr "Zápis"
msgid "%(count)s message"
msgid_plural "%(count)s messages"
msgstr[0] "%(count)s správa"
-msgstr[1] "%(count)s správ"
-msgstr[2] "%(count)s správ"
+msgstr[1] "%(count)s správy"
+msgstr[2] "%(count)s správy"
+msgstr[3] "%(count)s správ"
#: panels/logging.py:73
msgid "Log messages"
@@ -104,16 +106,18 @@ msgstr "Nastavenia z %s
"
msgid "%(num_receivers)d receiver of 1 signal"
msgid_plural "%(num_receivers)d receivers of 1 signal"
msgstr[0] "%(num_receivers)d príjemca 1 signálu"
-msgstr[1] "%(num_receivers)d príjemcov 1 signálu"
+msgstr[1] "%(num_receivers)d príjemcovia 1 signálu"
msgstr[2] "%(num_receivers)d príjemcov 1 signálu"
+msgstr[3] "%(num_receivers)d príjemcov 1 signálu"
#: panels/signals.py:48
#, python-format
msgid "%(num_receivers)d receiver of %(num_signals)d signals"
msgid_plural "%(num_receivers)d receivers of %(num_signals)d signals"
-msgstr[0] "%(num_receivers)d príjemca %(num_signals)d signálov"
+msgstr[0] "%(num_receivers)d príjemca %(num_signals)d signálu"
msgstr[1] "%(num_receivers)d príjemcov %(num_signals)d signálov"
-msgstr[2] "%(num_receivers)d príjemcov %(num_signals)d signálov"
+msgstr[2] "%(num_receivers)d príjemcu %(num_signals)d signálu"
+msgstr[3] "%(num_receivers)d príjemcov %(num_signals)d signálov"
#: panels/signals.py:53
msgid "Signals"
@@ -133,8 +137,9 @@ msgstr "Statické súbory"
msgid "%(num_used)s file used"
msgid_plural "%(num_used)s files used"
msgstr[0] "%(num_used)s použitý súbor"
-msgstr[1] "%(num_used)s použitých súborov"
+msgstr[1] "%(num_used)s použité súbory"
msgstr[2] "%(num_used)s použitých súborov"
+msgstr[3] "%(num_used)s použitých súborov"
#: panels/timer.py:23
#, python-format
@@ -146,10 +151,8 @@ msgstr "CPU: %(cum)0.2fms (%(total)0.2fms)"
msgid "Total: %0.2fms"
msgstr "Celkovo: %0.2fms"
-#: panels/timer.py:34 templates/debug_toolbar/panels/logging.html:7
-#: templates/debug_toolbar/panels/sql_explain.html:11
-#: templates/debug_toolbar/panels/sql_profile.html:12
-#: templates/debug_toolbar/panels/sql_select.html:11
+#: panels/timer.py:34 templates/debug_toolbar/panels/logging.html:7 templates/debug_toolbar/panels/sql_explain.html:11
+#: templates/debug_toolbar/panels/sql_profile.html:12 templates/debug_toolbar/panels/sql_select.html:11
msgid "Time"
msgstr "Čas"
@@ -232,7 +235,7 @@ msgstr "Akcia"
#: panels/sql/panel.py:39
msgid "In transaction"
-msgstr "Stav transakcie:"
+msgstr "V transakcii"
#: panels/sql/panel.py:40
msgid "In error"
@@ -285,9 +288,8 @@ msgstr "Poloha:"
#: templates/debug_toolbar/redirect.html:10
msgid ""
-"The Django Debug Toolbar has intercepted a redirect to the above URL for "
-"debug viewing purposes. You can click the above link to continue with the "
-"redirect as normal."
+"The Django Debug Toolbar has intercepted a redirect to the above URL for debug viewing purposes. You can click the above link to continue with the redirect "
+"as normal."
msgstr "Django Debug Toolbar zachytil presmerovanie na vyššie uvedenú URL pre účely ladenia. Pre normálne presmerovanie môžete kliknúť na vyššie uvedený odkaz."
#: templates/debug_toolbar/panels/cache.html:2
@@ -318,8 +320,7 @@ msgstr "Príkazy"
msgid "Calls"
msgstr "Volania"
-#: templates/debug_toolbar/panels/cache.html:43
-#: templates/debug_toolbar/panels/sql.html:20
+#: templates/debug_toolbar/panels/cache.html:43 templates/debug_toolbar/panels/sql.html:20
msgid "Time (ms)"
msgstr "Čas (ms)"
@@ -327,13 +328,11 @@ msgstr "Čas (ms)"
msgid "Type"
msgstr "Typ"
-#: templates/debug_toolbar/panels/cache.html:45
-#: templates/debug_toolbar/panels/request.html:8
+#: templates/debug_toolbar/panels/cache.html:45 templates/debug_toolbar/panels/request.html:8
msgid "Arguments"
msgstr "Argumenty"
-#: templates/debug_toolbar/panels/cache.html:46
-#: templates/debug_toolbar/panels/request.html:9
+#: templates/debug_toolbar/panels/cache.html:46 templates/debug_toolbar/panels/request.html:9
msgid "Keyword arguments"
msgstr "Kľúčové argumenty"
@@ -345,21 +344,13 @@ msgstr "Backend"
msgid "Request headers"
msgstr "Hlavičky požiadavky"
-#: templates/debug_toolbar/panels/headers.html:8
-#: templates/debug_toolbar/panels/headers.html:27
-#: templates/debug_toolbar/panels/headers.html:48
+#: templates/debug_toolbar/panels/headers.html:8 templates/debug_toolbar/panels/headers.html:27 templates/debug_toolbar/panels/headers.html:48
msgid "Key"
msgstr "Kľúč"
-#: templates/debug_toolbar/panels/headers.html:9
-#: templates/debug_toolbar/panels/headers.html:28
-#: templates/debug_toolbar/panels/headers.html:49
-#: templates/debug_toolbar/panels/request.html:33
-#: templates/debug_toolbar/panels/request.html:59
-#: templates/debug_toolbar/panels/request.html:85
-#: templates/debug_toolbar/panels/request.html:110
-#: templates/debug_toolbar/panels/settings.html:6
-#: templates/debug_toolbar/panels/timer.html:11
+#: templates/debug_toolbar/panels/headers.html:9 templates/debug_toolbar/panels/headers.html:28 templates/debug_toolbar/panels/headers.html:49
+#: templates/debug_toolbar/panels/request.html:33 templates/debug_toolbar/panels/request.html:59 templates/debug_toolbar/panels/request.html:85
+#: templates/debug_toolbar/panels/request.html:110 templates/debug_toolbar/panels/settings.html:6 templates/debug_toolbar/panels/timer.html:11
msgid "Value"
msgstr "Hodnota"
@@ -372,9 +363,7 @@ msgid "WSGI environ"
msgstr "WSGI prostredie"
#: templates/debug_toolbar/panels/headers.html:43
-msgid ""
-"Since the WSGI environ inherits the environment of the server, only a "
-"significant subset is shown below."
+msgid "Since the WSGI environ inherits the environment of the server, only a significant subset is shown below."
msgstr "Keďže WSGI prostredie dedí z prostredia servera, je nižšie zobrazená iba významná podmnožina."
#: templates/debug_toolbar/panels/logging.html:6
@@ -389,8 +378,7 @@ msgstr "Kanál"
msgid "Message"
msgstr "Správa"
-#: templates/debug_toolbar/panels/logging.html:10
-#: templates/debug_toolbar/panels/staticfiles.html:45
+#: templates/debug_toolbar/panels/logging.html:10 templates/debug_toolbar/panels/staticfiles.html:45
msgid "Location"
msgstr "Poloha"
@@ -406,8 +394,7 @@ msgstr "Volanie"
msgid "CumTime"
msgstr "CumTime"
-#: templates/debug_toolbar/panels/profiling.html:7
-#: templates/debug_toolbar/panels/profiling.html:9
+#: templates/debug_toolbar/panels/profiling.html:7 templates/debug_toolbar/panels/profiling.html:9
msgid "Per"
msgstr "Za"
@@ -435,9 +422,7 @@ msgstr "URL meno"
msgid "Cookies"
msgstr "Cookies"
-#: templates/debug_toolbar/panels/request.html:32
-#: templates/debug_toolbar/panels/request.html:58
-#: templates/debug_toolbar/panels/request.html:84
+#: templates/debug_toolbar/panels/request.html:32 templates/debug_toolbar/panels/request.html:58 templates/debug_toolbar/panels/request.html:84
#: templates/debug_toolbar/panels/request.html:109
msgid "Variable"
msgstr "Premenná"
@@ -491,15 +476,15 @@ msgstr "Príjemcovia"
msgid "%(num)s query"
msgid_plural "%(num)s queries"
msgstr[0] "%(num)s dopyt"
-msgstr[1] "%(num)s dopytov"
-msgstr[2] "%(num)s dopytov"
+msgstr[1] "%(num)s dopyty"
+msgstr[2] "%(num)s dopytu"
+msgstr[3] "%(num)s dopytov"
#: templates/debug_toolbar/panels/sql.html:18
msgid "Query"
msgstr "Dopyt"
-#: templates/debug_toolbar/panels/sql.html:19
-#: templates/debug_toolbar/panels/timer.html:36
+#: templates/debug_toolbar/panels/sql.html:19 templates/debug_toolbar/panels/timer.html:36
msgid "Timeline"
msgstr "Časová os"
@@ -527,9 +512,7 @@ msgstr "(neznámy)"
msgid "No SQL queries were recorded during this request."
msgstr "V priebehu tejto požiadavky neboli zaznamenané žiadne SQL dopyty."
-#: templates/debug_toolbar/panels/sql_explain.html:3
-#: templates/debug_toolbar/panels/sql_profile.html:3
-#: templates/debug_toolbar/panels/sql_select.html:3
+#: templates/debug_toolbar/panels/sql_explain.html:3 templates/debug_toolbar/panels/sql_profile.html:3 templates/debug_toolbar/panels/sql_select.html:3
#: templates/debug_toolbar/panels/template_source.html:3
msgid "Back"
msgstr "Späť"
@@ -538,15 +521,11 @@ msgstr "Späť"
msgid "SQL explained"
msgstr "SQL vysvetlené"
-#: templates/debug_toolbar/panels/sql_explain.html:9
-#: templates/debug_toolbar/panels/sql_profile.html:10
-#: templates/debug_toolbar/panels/sql_select.html:9
+#: templates/debug_toolbar/panels/sql_explain.html:9 templates/debug_toolbar/panels/sql_profile.html:10 templates/debug_toolbar/panels/sql_select.html:9
msgid "Executed SQL"
msgstr "Vykonané SQL"
-#: templates/debug_toolbar/panels/sql_explain.html:13
-#: templates/debug_toolbar/panels/sql_profile.html:14
-#: templates/debug_toolbar/panels/sql_select.html:13
+#: templates/debug_toolbar/panels/sql_explain.html:13 templates/debug_toolbar/panels/sql_profile.html:14 templates/debug_toolbar/panels/sql_select.html:13
msgid "Database"
msgstr "Databáza"
@@ -572,18 +551,15 @@ msgid_plural "Static file paths"
msgstr[0] "Cesta k statickému súboru"
msgstr[1] "Cesty k statickým súborom"
msgstr[2] "Cesty k statickým súborom"
+msgstr[3] "Ciest k statickým súborom"
#: templates/debug_toolbar/panels/staticfiles.html:8
#, python-format
msgid "(prefix %(prefix)s)"
msgstr "(prefix %(prefix)s)"
-#: templates/debug_toolbar/panels/staticfiles.html:12
-#: templates/debug_toolbar/panels/staticfiles.html:23
-#: templates/debug_toolbar/panels/staticfiles.html:35
-#: templates/debug_toolbar/panels/templates.html:10
-#: templates/debug_toolbar/panels/templates.html:28
-#: templates/debug_toolbar/panels/templates.html:43
+#: templates/debug_toolbar/panels/staticfiles.html:12 templates/debug_toolbar/panels/staticfiles.html:23 templates/debug_toolbar/panels/staticfiles.html:35
+#: templates/debug_toolbar/panels/templates.html:10 templates/debug_toolbar/panels/templates.html:28 templates/debug_toolbar/panels/templates.html:43
msgid "None"
msgstr "Žiadny"
@@ -593,21 +569,24 @@ msgid_plural "Static file apps"
msgstr[0] "Aplikácia pre statické súbory"
msgstr[1] "Aplikácie pre statické súbory"
msgstr[2] "Aplikácie pre statické súbory"
+msgstr[3] "Aplikácií pre statické súbory"
#: templates/debug_toolbar/panels/staticfiles.html:26
msgid "Static file"
msgid_plural "Static files"
-msgstr[0] ""
-msgstr[1] ""
-msgstr[2] "Statické súbory"
+msgstr[0] "Statický súbor"
+msgstr[1] "Statické súbory"
+msgstr[2] "Statického súbora"
+msgstr[3] "Statických súborov"
#: templates/debug_toolbar/panels/staticfiles.html:40
#, python-format
msgid "%(payload_count)s file"
msgid_plural "%(payload_count)s files"
msgstr[0] "%(payload_count)s súbor"
-msgstr[1] "%(payload_count)s súborov"
-msgstr[2] "%(payload_count)s súborov"
+msgstr[1] "%(payload_count)s súbory"
+msgstr[2] "%(payload_count)s súbora"
+msgstr[3] "%(payload_count)s súborov"
#: templates/debug_toolbar/panels/staticfiles.html:44
msgid "Path"
@@ -621,18 +600,19 @@ msgstr "Zdrojový kód šablóny:"
msgid "Template path"
msgid_plural "Template paths"
msgstr[0] "Cesta k šablóne"
-msgstr[1] "Cesta k šablóne"
-msgstr[2] "Cesta k šablóne"
+msgstr[1] "Cesty k šablóne"
+msgstr[2] "Cesty k šablóne"
+msgstr[3] "Ciest k šablóne"
#: templates/debug_toolbar/panels/templates.html:13
msgid "Template"
msgid_plural "Templates"
msgstr[0] "Šablóna"
-msgstr[1] "Šablóna"
-msgstr[2] "Šablóna"
+msgstr[1] "Šablóny"
+msgstr[2] "Šablóny"
+msgstr[3] "Šablón"
-#: templates/debug_toolbar/panels/templates.html:21
-#: templates/debug_toolbar/panels/templates.html:37
+#: templates/debug_toolbar/panels/templates.html:21 templates/debug_toolbar/panels/templates.html:37
msgid "Toggle context"
msgstr "Prepnúť kontext"
@@ -640,8 +620,9 @@ msgstr "Prepnúť kontext"
msgid "Context processor"
msgid_plural "Context processors"
msgstr[0] "Spracovateľ kontextu"
-msgstr[1] "Spracovateľ kontextu"
-msgstr[2] "Spracovateľ kontextu"
+msgstr[1] "Spracovatelia kontextu"
+msgstr[2] "Spracovateľa kontextu"
+msgstr[3] "Spracovateľov kontextu"
#: templates/debug_toolbar/panels/timer.html:2
msgid "Resource usage"
From 0e1954ff56bffc7cecb55e6087fef665b63917fc Mon Sep 17 00:00:00 2001
From: eriktelepovsky
Date: Thu, 24 Jun 2021 13:38:09 +0200
Subject: [PATCH 19/28] updated Slovak translation
---
debug_toolbar/locale/sk/LC_MESSAGES/django.mo | Bin 9981 -> 9984 bytes
debug_toolbar/locale/sk/LC_MESSAGES/django.po | 4 ++--
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/debug_toolbar/locale/sk/LC_MESSAGES/django.mo b/debug_toolbar/locale/sk/LC_MESSAGES/django.mo
index 4867b2a7ad1778a59fd7c6d105340ac599022fc6..e6af7c505db1c78cd74bdc52f0d032c66dda9ff9 100644
GIT binary patch
delta 999
zcmXZaOGs2v9LMoP&C4ktO*(1#NV8cegfWPq6(~yh8eB!`qK7?j0woDID%ZuhaG?Yh
zZKHfZFd$qaTq%f#3&Dj8ah0@i7NVvL5$y5(anI^=9{2qJ|MNfhPv=--{>_6
z#$;gv!}tIL*o#3-;w2o!3QVCN=Wr6!n2il3#^hlu>Rh+=DJotPmFG1o?{JB6%moH1
z7JBe4MsN=m;2-K>u+*F9;4|h?RG?v0;&Hr%?=S~f@FH$v4ep={j+A-d7oyI`${cS{
z!-5jk;}vYQA3Q`I>_r9av-?AMnfVl|(9c+aUr{$(Ld9FN`x}_ce9Nw9?D~Pj;3gX<
z=)>4GuhrG4PTav}>_P?jfY)&bqqv4D;3w+oGN^)&@f@C_3OYmG*kA7L=b}306fn@6
zRibWKi-p*PdYT^8&0gC1C@S#;s=!Zp9$i#{-%;^?pb~7M67SmiZ&acBk&yRi!2e^S
zcopNQ8zoSc-^UmD2-|TLQy8HQ1Nag3a|SDoxrarlb5AfIlNdwC?$2To^L@-BzX{zi
zrkaHaPUBtl;V)F>8B`~ZP>KFp^N6ZKD^TlIs1Dpl{gfT3PIOyep*l5!DsT+L%?u_O
zsO9fb0n@0&-!OoS)>YIKt)re~82_QmVtb?)Qnu0QZ*IX%?>+-)m%!~X%U
C*=8C5
delta 996
zcmXZaPe@cz6vy$4zR_`X9CLE|gEs0^3XwQ%{0Fr$BZg@s{sBct5+^shXrrP$jc^l!
zhFb|#2wX|yMyO4Oi@=2oaaFW%5J{~?2$F)|AMdU{_s_fMoO|w@esP&xzi?ciG3IHx
zF$I{w0Cr#=rZ6AVcnwFf8fQ@FR&fs3Fo=y6#*|Rgw#9~Cc+$}@s~9IG&{DP%Ci
zLJ!ViDITE$T%Zm*cf5Ha4l)m;0*#>(zri@pV=;ckBHYD#{D~?!6!pF@L!FOBU2pJ^
z1tn_08~E6M(1|*jLIoVM`>!#~{2i*$4_JmDQ8)X7innd|cQC|!q_f`mxKPj*Wll
zV64(>buFqB&De@bRDkyw!)1)%HmZOhsHe-K3jT{Zc!nzI9ChQIDsR6C)hV}>f!?eN
zb;Cz^6Q7`-rWS
z)ba&Xz%^9jPnd_Fty`!k`i^>%eN@7e%%*=O=){}jO_@~W(3ksicp#H3&jhXj6P#nO
diff --git a/debug_toolbar/locale/sk/LC_MESSAGES/django.po b/debug_toolbar/locale/sk/LC_MESSAGES/django.po
index f3706ae05..ef6657953 100644
--- a/debug_toolbar/locale/sk/LC_MESSAGES/django.po
+++ b/debug_toolbar/locale/sk/LC_MESSAGES/django.po
@@ -11,7 +11,7 @@ msgstr ""
"Project-Id-Version: Django Debug Toolbar\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2014-04-25 21:52+0200\n"
-"PO-Revision-Date: 2021-06-24 11:19+0200\n"
+"PO-Revision-Date: 2021-06-24 13:37+0200\n"
"Last-Translator: Aymeric Augustin \n"
"Language-Team: Slovak (http://www.transifex.com/projects/p/django-debug-toolbar/language/sk/)\n"
"MIME-Version: 1.0\n"
@@ -231,7 +231,7 @@ msgstr "Nečinný"
#: panels/sql/panel.py:38
msgid "Active"
-msgstr "Akcia"
+msgstr "Aktívne"
#: panels/sql/panel.py:39
msgid "In transaction"
From 538c4f68136b3bafa2dca562a1f02649ce07e35b Mon Sep 17 00:00:00 2001
From: Ashwini Chaudhary
Date: Sun, 11 Jul 2021 18:45:22 +0530
Subject: [PATCH 20/28] Fixes and improvements to history views
- Allow refreshing while requests are still happening by
using a list of dict.items()
- Fixed issue with `history_refresh`, it now returns SignedDataForm
to allow Switch requests to work after refresh.
- Clean up old rows in FE from that have now expired when refresh
happens.
- Fixed `history_sidebar` view to not raise 500 error when no toolbar
is found for an expired `store_id`.
- Old rows that have now expired, display [EXPIRED] next to the
Switch text.
---
debug_toolbar/panels/history/views.py | 10 ++-
.../static/debug_toolbar/js/history.js | 46 +++++++-----
tests/panels/test_history.py | 73 +++++++++++++++----
3 files changed, 94 insertions(+), 35 deletions(-)
diff --git a/debug_toolbar/panels/history/views.py b/debug_toolbar/panels/history/views.py
index b4cf8c835..20f4572fe 100644
--- a/debug_toolbar/panels/history/views.py
+++ b/debug_toolbar/panels/history/views.py
@@ -2,6 +2,7 @@
from django.template.loader import render_to_string
from debug_toolbar.decorators import require_show_toolbar, signed_data_view
+from debug_toolbar.forms import SignedDataForm
from debug_toolbar.panels.history.forms import HistoryStoreForm
from debug_toolbar.toolbar import DebugToolbar
@@ -16,6 +17,10 @@ def history_sidebar(request, verified_data):
store_id = form.cleaned_data["store_id"]
toolbar = DebugToolbar.fetch(store_id)
context = {}
+ if toolbar is None:
+ # When the store_id has been popped already due to
+ # RESULTS_CACHE_SIZE
+ return JsonResponse(context)
for panel in toolbar.panels:
if not panel.is_historical:
continue
@@ -40,7 +45,8 @@ def history_refresh(request, verified_data):
if form.is_valid():
requests = []
- for id, toolbar in reversed(DebugToolbar._store.items()):
+ # Convert to list to handle mutations happenening in parallel
+ for id, toolbar in list(DebugToolbar._store.items())[::-1]:
requests.append(
{
"id": id,
@@ -50,7 +56,7 @@ def history_refresh(request, verified_data):
"id": id,
"store_context": {
"toolbar": toolbar,
- "form": HistoryStoreForm(initial={"store_id": id}),
+ "form": SignedDataForm(initial={"store_id": id}),
},
},
),
diff --git a/debug_toolbar/static/debug_toolbar/js/history.js b/debug_toolbar/static/debug_toolbar/js/history.js
index e20c85438..4f0c20b80 100644
--- a/debug_toolbar/static/debug_toolbar/js/history.js
+++ b/debug_toolbar/static/debug_toolbar/js/history.js
@@ -7,21 +7,31 @@ $$.on(djDebug, "click", ".switchHistory", function (event) {
const newStoreId = this.dataset.storeId;
const tbody = this.closest("tbody");
- tbody
- .querySelector(".djdt-highlighted")
- .classList.remove("djdt-highlighted");
- this.closest("tr").classList.add("djdt-highlighted");
+ const highlighted = tbody.querySelector(".djdt-highlighted");
+ if (highlighted) {
+ highlighted.classList.remove("djdt-highlighted");
+ this.closest("tr").classList.add("djdt-highlighted");
+ }
ajaxForm(this).then(function (data) {
djDebug.setAttribute("data-store-id", newStoreId);
- Object.keys(data).forEach(function (panelId) {
- const panel = document.getElementById(panelId);
- if (panel) {
- panel.outerHTML = data[panelId].content;
- document.getElementById("djdt-" + panelId).outerHTML =
- data[panelId].button;
- }
- });
+ console.log("New id is" + newStoreId);
+ // Check if response is empty, it could be due to an expired store_id.
+ if (Object.keys(data).length === 0) {
+ const container = document.getElementById("djdtHistoryRequests");
+ container.querySelector(
+ 'button[data-store-id="' + newStoreId + '"]'
+ ).innerHTML = "Switch [EXPIRED]";
+ } else {
+ Object.keys(data).forEach(function (panelId) {
+ const panel = document.getElementById(panelId);
+ if (panel) {
+ panel.outerHTML = data[panelId].content;
+ document.getElementById("djdt-" + panelId).outerHTML =
+ data[panelId].button;
+ }
+ });
+ }
});
});
@@ -29,12 +39,14 @@ $$.on(djDebug, "click", ".refreshHistory", function (event) {
event.preventDefault();
const container = document.getElementById("djdtHistoryRequests");
ajaxForm(this).then(function (data) {
+ // Remove existing rows first then re-populate with new data
+ container
+ .querySelectorAll("tr[data-store-id]")
+ .forEach(function (node) {
+ node.remove();
+ });
data.requests.forEach(function (request) {
- if (
- !container.querySelector('[data-store-id="' + request.id + '"]')
- ) {
- container.innerHTML = request.content + container.innerHTML;
- }
+ container.innerHTML = request.content + container.innerHTML;
});
});
});
diff --git a/tests/panels/test_history.py b/tests/panels/test_history.py
index 03657a374..49e3bd0fa 100644
--- a/tests/panels/test_history.py
+++ b/tests/panels/test_history.py
@@ -1,3 +1,5 @@
+import html
+
from django.test import RequestFactory, override_settings
from django.urls import resolve, reverse
@@ -64,6 +66,21 @@ def test_urls(self):
@override_settings(DEBUG=True)
class HistoryViewsTestCase(IntegrationTestCase):
+ PANEL_KEYS = {
+ "VersionsPanel",
+ "TimerPanel",
+ "SettingsPanel",
+ "HeadersPanel",
+ "RequestPanel",
+ "SQLPanel",
+ "StaticFilesPanel",
+ "TemplatesPanel",
+ "CachePanel",
+ "SignalsPanel",
+ "LoggingPanel",
+ "ProfilingPanel",
+ }
+
def test_history_panel_integration_content(self):
"""Verify the history panel's content renders properly.."""
self.assertEqual(len(DebugToolbar._store), 0)
@@ -88,26 +105,45 @@ def test_history_sidebar_invalid(self):
def test_history_sidebar(self):
"""Validate the history sidebar view."""
self.client.get("/json_view/")
- store_id = list(DebugToolbar._store.keys())[0]
+ store_id = list(DebugToolbar._store)[0]
+ data = {"signed": SignedDataForm.sign({"store_id": store_id})}
+ response = self.client.get(reverse("djdt:history_sidebar"), data=data)
+ self.assertEqual(response.status_code, 200)
+ self.assertEqual(
+ set(response.json()),
+ self.PANEL_KEYS,
+ )
+
+ @override_settings(
+ DEBUG_TOOLBAR_CONFIG={"RESULTS_CACHE_SIZE": 1, "RENDER_PANELS": False}
+ )
+ def test_history_sidebar_expired_store_id(self):
+ """Validate the history sidebar view."""
+ self.client.get("/json_view/")
+ store_id = list(DebugToolbar._store)[0]
+ data = {"signed": SignedDataForm.sign({"store_id": store_id})}
+ response = self.client.get(reverse("djdt:history_sidebar"), data=data)
+ self.assertEqual(response.status_code, 200)
+ self.assertEqual(
+ set(response.json()),
+ self.PANEL_KEYS,
+ )
+ self.client.get("/json_view/")
+
+ # Querying old store_id should return in empty response
data = {"signed": SignedDataForm.sign({"store_id": store_id})}
response = self.client.get(reverse("djdt:history_sidebar"), data=data)
self.assertEqual(response.status_code, 200)
+ self.assertEqual(response.json(), {})
+
+ # Querying with latest store_id
+ latest_store_id = list(DebugToolbar._store)[0]
+ data = {"signed": SignedDataForm.sign({"store_id": latest_store_id})}
+ response = self.client.get(reverse("djdt:history_sidebar"), data=data)
+ self.assertEqual(response.status_code, 200)
self.assertEqual(
- set(response.json().keys()),
- {
- "VersionsPanel",
- "TimerPanel",
- "SettingsPanel",
- "HeadersPanel",
- "RequestPanel",
- "SQLPanel",
- "StaticFilesPanel",
- "TemplatesPanel",
- "CachePanel",
- "SignalsPanel",
- "LoggingPanel",
- "ProfilingPanel",
- },
+ set(response.json()),
+ self.PANEL_KEYS,
)
def test_history_refresh_invalid_signature(self):
@@ -128,5 +164,10 @@ def test_history_refresh(self):
self.assertEqual(response.status_code, 200)
data = response.json()
self.assertEqual(len(data["requests"]), 1)
+
+ store_id = list(DebugToolbar._store)[0]
+ signature = SignedDataForm.sign({"store_id": store_id})
+ self.assertIn(html.escape(signature), data["requests"][0]["content"])
+
for val in ["foo", "bar"]:
self.assertIn(val, data["requests"][0]["content"])
From 96b23eb155c1cd58c0e04ecea9e1aafc5bbac613 Mon Sep 17 00:00:00 2001
From: Ashwini Chaudhary
Date: Sat, 17 Jul 2021 08:17:04 +0530
Subject: [PATCH 21/28] Remove console.log
---
debug_toolbar/static/debug_toolbar/js/history.js | 1 -
1 file changed, 1 deletion(-)
diff --git a/debug_toolbar/static/debug_toolbar/js/history.js b/debug_toolbar/static/debug_toolbar/js/history.js
index 4f0c20b80..c53072093 100644
--- a/debug_toolbar/static/debug_toolbar/js/history.js
+++ b/debug_toolbar/static/debug_toolbar/js/history.js
@@ -15,7 +15,6 @@ $$.on(djDebug, "click", ".switchHistory", function (event) {
ajaxForm(this).then(function (data) {
djDebug.setAttribute("data-store-id", newStoreId);
- console.log("New id is" + newStoreId);
// Check if response is empty, it could be due to an expired store_id.
if (Object.keys(data).length === 0) {
const container = document.getElementById("djdtHistoryRequests");
From 78425e392d8344ff5d408e49b4c75e2bb21d292e Mon Sep 17 00:00:00 2001
From: Ashwini Chaudhary
Date: Sat, 17 Jul 2021 19:29:32 +0530
Subject: [PATCH 22/28] Move the highlight class addition outside to allow it
to be set in the first place
---
debug_toolbar/static/debug_toolbar/js/history.js | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/debug_toolbar/static/debug_toolbar/js/history.js b/debug_toolbar/static/debug_toolbar/js/history.js
index c53072093..cc14b2e4f 100644
--- a/debug_toolbar/static/debug_toolbar/js/history.js
+++ b/debug_toolbar/static/debug_toolbar/js/history.js
@@ -10,8 +10,8 @@ $$.on(djDebug, "click", ".switchHistory", function (event) {
const highlighted = tbody.querySelector(".djdt-highlighted");
if (highlighted) {
highlighted.classList.remove("djdt-highlighted");
- this.closest("tr").classList.add("djdt-highlighted");
}
+ this.closest("tr").classList.add("djdt-highlighted");
ajaxForm(this).then(function (data) {
djDebug.setAttribute("data-store-id", newStoreId);
From 8049478bf6b84a7b9c07d23152f13332c197ff57 Mon Sep 17 00:00:00 2001
From: Tim Schilling
Date: Tue, 20 Jul 2021 11:34:18 -0500
Subject: [PATCH 23/28] Use both SignedDataForm and HistoryStoreForm in
history_refresh.
---
debug_toolbar/panels/history/views.py | 6 +++++-
1 file changed, 5 insertions(+), 1 deletion(-)
diff --git a/debug_toolbar/panels/history/views.py b/debug_toolbar/panels/history/views.py
index 20f4572fe..10b4dcc1a 100644
--- a/debug_toolbar/panels/history/views.py
+++ b/debug_toolbar/panels/history/views.py
@@ -56,7 +56,11 @@ def history_refresh(request, verified_data):
"id": id,
"store_context": {
"toolbar": toolbar,
- "form": SignedDataForm(initial={"store_id": id}),
+ "form": SignedDataForm(
+ initial=HistoryStoreForm(
+ initial={"store_id": id}
+ ).initial
+ ),
},
},
),
From 085f8dd12660036da2ecbb1217de48509e7bd3da Mon Sep 17 00:00:00 2001
From: James Addison
Date: Tue, 20 Jul 2021 21:42:53 +0100
Subject: [PATCH 24/28] Add test coverage to ensure that SQL tracker wrappers
are applied only once to database cursors (#1478)
---
tests/panels/test_sql.py | 16 ++++++++++++++++
1 file changed, 16 insertions(+)
diff --git a/tests/panels/test_sql.py b/tests/panels/test_sql.py
index 84b789868..08535a79e 100644
--- a/tests/panels/test_sql.py
+++ b/tests/panels/test_sql.py
@@ -1,6 +1,7 @@
import datetime
import os
import unittest
+from unittest.mock import patch
import django
from django.contrib.auth.models import User
@@ -10,6 +11,7 @@
from django.shortcuts import render
from django.test.utils import override_settings
+import debug_toolbar.panels.sql.tracking as sql_tracking
from debug_toolbar import settings as dt_settings
from ..base import BaseTestCase
@@ -61,6 +63,20 @@ def test_recording_chunked_cursor(self):
# ensure query was logged
self.assertEqual(len(self.panel._queries), 1)
+ @patch("debug_toolbar.panels.sql.tracking.state", wraps=sql_tracking.state)
+ def test_cursor_wrapper_singleton(self, mock_state):
+ list(User.objects.all())
+
+ # ensure that cursor wrapping is applied only once
+ self.assertEqual(mock_state.Wrapper.call_count, 1)
+
+ @patch("debug_toolbar.panels.sql.tracking.state", wraps=sql_tracking.state)
+ def test_chunked_cursor_wrapper_singleton(self, mock_state):
+ list(User.objects.all().iterator())
+
+ # ensure that cursor wrapping is applied only once
+ self.assertEqual(mock_state.Wrapper.call_count, 1)
+
def test_generate_server_timing(self):
self.assertEqual(len(self.panel._queries), 0)
From 15a581d5d0abbf82d9f62a3bfdc8ce39d45f0f1a Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Pawe=C5=82=20Sacawa?=
Date: Mon, 9 Aug 2021 21:06:46 -0400
Subject: [PATCH 25/28] Add: response status to HistoryPanel (#1490)
* Add: response status to HistoryPanel
* Doc: update docs/changes.rst
---
debug_toolbar/panels/history/panel.py | 1 +
debug_toolbar/templates/debug_toolbar/panels/history.html | 1 +
debug_toolbar/templates/debug_toolbar/panels/history_tr.html | 3 +++
docs/changes.rst | 1 +
4 files changed, 6 insertions(+)
diff --git a/debug_toolbar/panels/history/panel.py b/debug_toolbar/panels/history/panel.py
index 0cc352e8b..541c59136 100644
--- a/debug_toolbar/panels/history/panel.py
+++ b/debug_toolbar/panels/history/panel.py
@@ -68,6 +68,7 @@ def generate_stats(self, request, response):
{
"request_url": request.get_full_path(),
"request_method": request.method,
+ "status_code": response.status_code,
"data": data,
"time": timezone.now(),
}
diff --git a/debug_toolbar/templates/debug_toolbar/panels/history.html b/debug_toolbar/templates/debug_toolbar/panels/history.html
index f5e967a17..84c6cb5bd 100644
--- a/debug_toolbar/templates/debug_toolbar/panels/history.html
+++ b/debug_toolbar/templates/debug_toolbar/panels/history.html
@@ -10,6 +10,7 @@
{% trans "Method" %}
{% trans "Path" %}
{% trans "Request Variables" %}
+ {% trans "Status" %}
{% trans "Action" %}
diff --git a/debug_toolbar/templates/debug_toolbar/panels/history_tr.html b/debug_toolbar/templates/debug_toolbar/panels/history_tr.html
index 9ce984396..31793472a 100644
--- a/debug_toolbar/templates/debug_toolbar/panels/history_tr.html
+++ b/debug_toolbar/templates/debug_toolbar/panels/history_tr.html
@@ -38,6 +38,9 @@
+
+ {{ store_context.toolbar.stats.HistoryPanel.status_code|escape }}
+