diff options
| -rw-r--r-- | debug_toolbar/middleware.py | 26 | ||||
| -rw-r--r-- | debug_toolbar/models.py | 54 | ||||
| -rw-r--r-- | debug_toolbar/templates/debug_toolbar/base.html | 4 | ||||
| -rw-r--r-- | debug_toolbar/templates/debug_toolbar/panels/sql.html | 8 | ||||
| -rw-r--r-- | debug_toolbar/templates/debug_toolbar/panels/templates.html | 4 | ||||
| -rw-r--r-- | debug_toolbar/toolbar/loader.py | 5 | ||||
| -rw-r--r-- | debug_toolbar/urls.py | 16 | ||||
| -rw-r--r-- | docs/installation.rst | 122 | ||||
| -rw-r--r-- | docs/tips.rst | 13 | ||||
| -rw-r--r-- | example/settings.py | 12 | ||||
| -rw-r--r-- | tests/settings.py | 10 | ||||
| -rw-r--r-- | tests/test_integration.py | 53 | ||||
| -rw-r--r-- | tests/urls.py | 3 |
13 files changed, 164 insertions, 166 deletions
diff --git a/debug_toolbar/middleware.py b/debug_toolbar/middleware.py index daea751..c95cade 100644 --- a/debug_toolbar/middleware.py +++ b/debug_toolbar/middleware.py @@ -4,17 +4,13 @@ Debug Toolbar middleware from __future__ import unicode_literals -import imp import threading from django.conf import settings from django.http import HttpResponseRedirect from django.shortcuts import render from django.utils.encoding import force_text -from django.utils.importlib import import_module -from django.utils import six -import debug_toolbar.urls from debug_toolbar.toolbar.loader import DebugToolbar from debug_toolbar.utils.settings import CONFIG @@ -55,8 +51,6 @@ class DebugToolbarMiddleware(object): debug_toolbars = {} def __init__(self): - self._urlconfs = {} - # The method to call to decide to show the toolbar self.show_toolbar = CONFIG['SHOW_TOOLBAR_CALLBACK'] or show_toolbar @@ -66,26 +60,6 @@ class DebugToolbarMiddleware(object): def process_request(self, request): __traceback_hide__ = True # noqa if self.show_toolbar(request): - urlconf = getattr(request, 'urlconf', settings.ROOT_URLCONF) - if isinstance(urlconf, six.string_types): - urlconf = import_module(getattr(request, 'urlconf', settings.ROOT_URLCONF)) - - if urlconf not in self._urlconfs: - new_urlconf = imp.new_module('urlconf') - new_urlconf.urlpatterns = (debug_toolbar.urls.urlpatterns + - list(urlconf.urlpatterns)) - - if hasattr(urlconf, 'handler403'): - new_urlconf.handler403 = urlconf.handler403 - if hasattr(urlconf, 'handler404'): - new_urlconf.handler404 = urlconf.handler404 - if hasattr(urlconf, 'handler500'): - new_urlconf.handler500 = urlconf.handler500 - - self._urlconfs[urlconf] = new_urlconf - - request.urlconf = self._urlconfs[urlconf] - toolbar = DebugToolbar(request) for panel in toolbar.panels: panel.enabled = panel.dom_id() not in request.COOKIES diff --git a/debug_toolbar/models.py b/debug_toolbar/models.py index d6b18aa..fc78874 100644 --- a/debug_toolbar/models.py +++ b/debug_toolbar/models.py @@ -1,21 +1,65 @@ from __future__ import unicode_literals from django.conf import settings +from django.conf.urls import include, patterns, url +from django.core.urlresolvers import reverse, NoReverseMatch from django.utils.importlib import import_module from debug_toolbar.toolbar.loader import load_panel_classes from debug_toolbar.middleware import DebugToolbarMiddleware -for middleware_path in settings.MIDDLEWARE_CLASSES: +def is_toolbar_middleware(middleware_path): # Replace this with import_by_path in Django >= 1.6. try: mod_path, cls_name = middleware_path.rsplit('.', 1) mod = import_module(mod_path) middleware_cls = getattr(mod, cls_name) except (AttributeError, ImportError, ValueError): - continue + return + return issubclass(middleware_cls, DebugToolbarMiddleware) - if issubclass(middleware_cls, DebugToolbarMiddleware): - load_panel_classes() - break + +def is_toolbar_middleware_installed(): + return any(is_toolbar_middleware(middleware) + for middleware in settings.MIDDLEWARE_CLASSES) + + +def prepend_to_setting(setting_name, value): + """Insert value at the beginning of a list or tuple setting.""" + values = getattr(settings, setting_name) + # Make a list [value] or tuple (value,) + value = type(values)((value,)) + setattr(settings, setting_name, value + values) + + +def patch_internal_ips(): + if not settings.INTERNAL_IPS: + prepend_to_setting('INTERNAL_IPS', '127.0.0.1') + prepend_to_setting('INTERNAL_IPS', '::1') + + +def patch_middleware_classes(): + if not is_toolbar_middleware_installed(): + prepend_to_setting('MIDDLEWARE_CLASSES', + 'debug_toolbar.middleware.DebugToolbarMiddleware') + + +def patch_root_urlconf(): + try: + reverse('djdt:render_panel') + except NoReverseMatch: + urlconf_module = import_module(settings.ROOT_URLCONF) + urlconf_module.urlpatterns += patterns('', # noqa + url(r'^__debug__/', include('debug_toolbar.urls', namespace='djdt', app_name='djdt')), + ) + + +if settings.DEBUG: + patch_internal_ips() + patch_middleware_classes() + patch_root_urlconf() + + +if is_toolbar_middleware_installed(): + load_panel_classes() diff --git a/debug_toolbar/templates/debug_toolbar/base.html b/debug_toolbar/templates/debug_toolbar/base.html index f8bd377..4c82140 100644 --- a/debug_toolbar/templates/debug_toolbar/base.html +++ b/debug_toolbar/templates/debug_toolbar/base.html @@ -1,4 +1,4 @@ -{% load i18n %} +{% load i18n %}{% load url from future %} <style type="text/css"> @media print { #djDebug {display:none;}} </style> @@ -9,7 +9,7 @@ if(!window.jQuery) document.write('<scr'+'ipt src="{{ STATIC_URL }}debug_toolbar <script src="{{ STATIC_URL }}debug_toolbar/js/jquery.cookie.js"></script> <script src="{{ STATIC_URL }}debug_toolbar/js/toolbar.js"></script> <div id="djDebug" style="display:none;" dir="ltr" - data-toolbar-id="{{ toolbar_id }}" data-render-panel-url="/__debug__/render_panel/" + data-toolbar-id="{{ toolbar_id }}" data-render-panel-url="{% url 'djdt:render_panel' %}" {{ TOOLBAR_ROOT_TAG_ATTRS|safe }}> <div style="display:none;" id="djDebugToolbar"> <ul id="djDebugPanelList"> diff --git a/debug_toolbar/templates/debug_toolbar/panels/sql.html b/debug_toolbar/templates/debug_toolbar/panels/sql.html index 064413c..54babfc 100644 --- a/debug_toolbar/templates/debug_toolbar/panels/sql.html +++ b/debug_toolbar/templates/debug_toolbar/panels/sql.html @@ -1,4 +1,4 @@ -{% load i18n %} +{% load i18n %}{% load url from future %} {% load debug_toolbar_utils %} <div class="clearfix"> <ul class="stats"> @@ -47,11 +47,11 @@ <form method="post"> {{ query.form }} - <button formaction="/__debug__/sql_select/" class="remoteCall">Sel</button> - <button formaction="/__debug__/sql_explain/" class="remoteCall">Expl</button> + <button formaction="{% url 'djdt:sql_select' %}" class="remoteCall">Sel</button> + <button formaction="{% url 'djdt:sql_explain' %}" class="remoteCall">Expl</button> {% ifequal query.engine 'mysql' %} - <button formaction="/__debug__/sql_profile/" class="remoteCall">Prof</button> + <button formaction="{% url 'djdt:sql_profile' %}" class="remoteCall">Prof</button> {% endifequal %} </form> {% endif %} diff --git a/debug_toolbar/templates/debug_toolbar/panels/templates.html b/debug_toolbar/templates/debug_toolbar/panels/templates.html index 7e44a46..5cbb742 100644 --- a/debug_toolbar/templates/debug_toolbar/panels/templates.html +++ b/debug_toolbar/templates/debug_toolbar/panels/templates.html @@ -1,4 +1,4 @@ -{% load i18n %} +{% load i18n %}{% load url from future %} <h4>{% blocktrans count template_dirs|length as template_count %}Template path{% plural %}Template paths{% endblocktrans %}</h4> {% if template_dirs %} <ol> @@ -14,7 +14,7 @@ {% if templates %} <dl> {% for template in templates %} - <dt><strong><a class="remoteCall toggleTemplate" href="/__debug__/template_source/?template={{ template.template.name }}">{{ template.template.name|addslashes }}</a></strong></dt> + <dt><strong><a class="remoteCall toggleTemplate" href="{% url 'djdt:template_source' %}?template={{ template.template.name }}">{{ template.template.name|addslashes }}</a></strong></dt> <dd><samp>{{ template.template.origin_name|addslashes }}</samp></dd> {% if template.context %} <dd> diff --git a/debug_toolbar/toolbar/loader.py b/debug_toolbar/toolbar/loader.py index de4aa93..870eec3 100644 --- a/debug_toolbar/toolbar/loader.py +++ b/debug_toolbar/toolbar/loader.py @@ -18,13 +18,10 @@ class DebugToolbar(object): self.request = request self._panels = SortedDict() base_url = self.request.META.get('SCRIPT_NAME', '') - self.config = { - 'MEDIA_URL': '%s/__debug__/m/' % base_url, - } + self.config = {} self.config.update(CONFIG) self.template_context = { 'BASE_URL': base_url, # for backwards compatibility - 'DEBUG_TOOLBAR_MEDIA_URL': self.config['MEDIA_URL'], 'STATIC_URL': settings.STATIC_URL, 'TOOLBAR_ROOT_TAG_ATTRS': self.config['ROOT_TAG_ATTRS'], } diff --git a/debug_toolbar/urls.py b/debug_toolbar/urls.py index 05ef245..860187a 100644 --- a/debug_toolbar/urls.py +++ b/debug_toolbar/urls.py @@ -1,20 +1,18 @@ """ URLpatterns for the debug toolbar. -These should not be loaded explicitly; the debug toolbar middleware will patch -this into the urlconf for the request. +The debug toolbar middleware will monkey-patch them into the default urlconf +if they aren't explicitly included. """ from __future__ import unicode_literals from django.conf.urls import patterns, url -_PREFIX = '__debug__' - urlpatterns = patterns('debug_toolbar.views', # noqa - url(r'^%s/render_panel/$' % _PREFIX, 'render_panel', name='render_panel'), - url(r'^%s/sql_select/$' % _PREFIX, 'sql_select', name='sql_select'), - url(r'^%s/sql_explain/$' % _PREFIX, 'sql_explain', name='sql_explain'), - url(r'^%s/sql_profile/$' % _PREFIX, 'sql_profile', name='sql_profile'), - url(r'^%s/template_source/$' % _PREFIX, 'template_source', name='template_source'), + url(r'^render_panel/$', 'render_panel', name='render_panel'), + url(r'^sql_select/$', 'sql_select', name='sql_select'), + url(r'^sql_explain/$', 'sql_explain', name='sql_explain'), + url(r'^sql_profile/$', 'sql_profile', name='sql_profile'), + url(r'^template_source/$', 'template_source', name='template_source'), ) diff --git a/docs/installation.rst b/docs/installation.rst index 32483df..514548a 100644 --- a/docs/installation.rst +++ b/docs/installation.rst @@ -1,66 +1,102 @@ Installation ============ -#. The recommended way to install the Debug Toolbar is via pip_:: +Getting the code +---------------- - $ pip install django-debug-toolbar +The recommended way to install the Debug Toolbar is via pip_:: - If you aren't familiar with pip, you may also obtain a copy of the - ``debug_toolbar`` directory and add it to your Python path. + $ pip install django-debug-toolbar - .. _pip: http://www.pip-installer.org/ +If you aren't familiar with pip, you may also obtain a copy of the +``debug_toolbar`` directory and add it to your Python path. +.. _pip: http://www.pip-installer.org/ - To test an upcoming release, you can install the `in-development version - <http://github.com/django-debug-toolbar/django-debug-toolbar/tarball/master#egg=django-debug-toolbar-dev>`_ - instead with the following command:: +To test an upcoming release, you can install the `in-development version +<http://github.com/django-debug-toolbar/django-debug-toolbar/tarball/master#egg=django-debug-toolbar-dev>`_ +instead with the following command:: - $ pip install django-debug-toolbar==dev + $ pip install django-debug-toolbar==dev -#. Add the following middleware to your project's ``settings.py`` file:: +Quick setup +----------- - MIDDLEWARE_CLASSES = ( - # ... - 'debug_toolbar.middleware.DebugToolbarMiddleware', - # ... - ) +Add ``debug_toolbar`` to your ``INSTALLED_APPS`` setting:: - Tying into middleware allows each panel to be instantiated on request and - rendering to happen on response. + INSTALLED_APPS = ( + # ... + 'debug_toolbar', + ) - The order of ``MIDDLEWARE_CLASSES`` is important. You should include the - Debug Toolbar middleware as early as possible in the list. However, it must - come after any other middleware that encodes the response's content, such - as ``GZipMiddleware``. +For a simple Django project, that's all you need! - .. note:: +The Debug Toolbar will automatically adjust a few settings when you start the +development server. This happens only when the ``DEBUG`` setting is ``True``. - The debug toolbar will only display itself if the mimetype of the - response is either ``text/html`` or ``application/xhtml+xml`` and - contains a closing ``</body>`` tag. +If the automatic setup doesn't work for your project, if you want to learn +what it does, or if you prefer defining your settings explicitly, read below. - .. note :: +.. note:: - Be aware of middleware ordering and other middleware that may intercept - requests and return responses. Putting the debug toolbar middleware - *after* the Flatpage middleware, for example, means the toolbar will not - show up on flatpages. + The automatic setup relies on ``debug_toolbar.models`` being imported when + the server starts. Django doesn't provide a better hook to execute code + during the start-up sequence. This works with Django's built-in + development server ``runserver`` because it validates models before + serving requests. You should use the explicit setup with other servers. -#. Make sure your IP is listed in the ``INTERNAL_IPS`` setting. If you are - working locally this will be:: +Explicit setup +-------------- - INTERNAL_IPS = ('127.0.0.1',) +URLconf +~~~~~~~ - .. note:: +Add the Debug Toolbar's URLs to your project's URLconf as follows:: - This is required because of the built-in requirements of the - ``show_toolbar`` method. See below for how to define a method to - determine your own logic for displaying the toolbar. + from django.conf import settings + from django.conf.urls import include, patterns, url -#. Add ``debug_toolbar`` to your ``INSTALLED_APPS`` setting so Django can - find the template files associated with the Debug Toolbar:: + if settings.DEBUG: + urlpatterns += patterns('', + url(r'^__debug__/', include('debug_toolbar.urls', namespace='djdt', app_name='djdt')), + ) - INSTALLED_APPS = ( - # ... - 'debug_toolbar', - ) +This example uses the ``__debug__`` prefix, but you can use any prefix that +doesn't clash with your application's URLs. + +It is mandatory to use the ``djdt`` namespace as shown above. This avoids +conflicts when reversing URLs by name. + +If the URLs aren't included in your root URLconf, the Debug Toolbar +automatically appends them. + +Middleware +~~~~~~~~~~ + +The Debug Toolbar is mostly implemented in a middleware. Enable it in your +settings module as follows:: + + MIDDLEWARE_CLASSES = ( + # ... + 'debug_toolbar.middleware.DebugToolbarMiddleware', + # ... + ) + +The order of ``MIDDLEWARE_CLASSES`` is important. You should include the Debug +Toolbar middleware as early as possible in the list. However, it must come +after any other middleware that encodes the response's content, such as +``GZipMiddleware``. + +If ``MIDDLEWARE_CLASSES`` doesn't contain the middleware, the Debug Toolbar +automatically adds it the beginning of the list. + +Internal IPs +~~~~~~~~~~~~ + +The Debug Toolbar is shown only if your IP is listed in the ``INTERNAL_IPS`` +setting. (You can change this logic with the ``SHOW_TOOLBAR_CALLBACK`` +option.) For local development, you should add ``'127.0.0.1'`` to +``INTERNAL_IPS``. + +If ``INTERNAL_IPS`` is empty, the Debug Toolbar automatically sets it to +``'127.0.0.1'`` and ``'::1'``. diff --git a/docs/tips.rst b/docs/tips.rst index 5de4ec3..d92e5ab 100644 --- a/docs/tips.rst +++ b/docs/tips.rst @@ -1,6 +1,19 @@ Tips ==== +The toolbar isn't displayed! +---------------------------- + +The Debug Toolbar will only display itself if the mimetype of the response is +either ``text/html`` or ``application/xhtml+xml`` and contains a closing +``</body>`` tag. + +Be aware of middleware ordering and other middleware that may intercept +requests and return responses. Putting the debug toolbar middleware *after* +the Flatpage middleware, for example, means the toolbar will not show up on +flatpages. + + Performance considerations -------------------------- diff --git a/example/settings.py b/example/settings.py index ce1f8e2..1eba309 100644 --- a/example/settings.py +++ b/example/settings.py @@ -10,8 +10,6 @@ SECRET_KEY = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890' DEBUG = True -INTERNAL_IPS = ['127.0.0.1', '::1'] - TEMPLATE_DEBUG = True @@ -27,16 +25,6 @@ INSTALLED_APPS = ( 'debug_toolbar', ) -MIDDLEWARE_CLASSES = ( - 'debug_toolbar.middleware.DebugToolbarMiddleware', - 'django.contrib.sessions.middleware.SessionMiddleware', - 'django.middleware.common.CommonMiddleware', - 'django.middleware.csrf.CsrfViewMiddleware', - 'django.contrib.auth.middleware.AuthenticationMiddleware', - 'django.contrib.messages.middleware.MessageMiddleware', - 'django.middleware.clickjacking.XFrameOptionsMiddleware', -) - ROOT_URLCONF = 'example.urls' STATIC_URL = '/static/' diff --git a/tests/settings.py b/tests/settings.py index d21e9e4..a41b5a7 100644 --- a/tests/settings.py +++ b/tests/settings.py @@ -8,12 +8,12 @@ BASE_DIR = os.path.dirname(os.path.dirname(__file__)) SECRET_KEY = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890' -INTERNAL_IPS = ['127.0.0.1', '::1'] +INTERNAL_IPS = ['127.0.0.1'] # Application definition -INSTALLED_APPS = ( +INSTALLED_APPS = [ 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', @@ -22,9 +22,9 @@ INSTALLED_APPS = ( 'django.contrib.staticfiles', 'debug_toolbar', 'tests', -) +] -MIDDLEWARE_CLASSES = ( +MIDDLEWARE_CLASSES = [ 'debug_toolbar.middleware.DebugToolbarMiddleware', 'django.contrib.sessions.middleware.SessionMiddleware', 'django.middleware.common.CommonMiddleware', @@ -32,7 +32,7 @@ MIDDLEWARE_CLASSES = ( 'django.contrib.auth.middleware.AuthenticationMiddleware', 'django.contrib.messages.middleware.MessageMiddleware', 'django.middleware.clickjacking.XFrameOptionsMiddleware', -) +] ROOT_URLCONF = 'tests.urls' diff --git a/tests/test_integration.py b/tests/test_integration.py index a80f50c..412b14b 100644 --- a/tests/test_integration.py +++ b/tests/test_integration.py @@ -6,7 +6,6 @@ from xml.etree import ElementTree as ET from django.test import TestCase, RequestFactory from django.test.utils import override_settings -from django.utils import six from debug_toolbar.middleware import DebugToolbarMiddleware, show_toolbar from debug_toolbar.panels.request_vars import RequestVarsDebugPanel @@ -34,58 +33,6 @@ class DebugToolbarTestCase(BaseTestCase): with self.settings(INTERNAL_IPS=[]): self.assertFalse(show_toolbar(self.request)) - def test_request_urlconf_string(self): - request = rf.get('/') - request.urlconf = 'tests.urls' - middleware = DebugToolbarMiddleware() - - middleware.process_request(request) - - self.assertFalse(isinstance(request.urlconf, six.string_types)) - - patterns = request.urlconf.urlpatterns - self.assertTrue(hasattr(patterns[1], '_callback_str')) - self.assertEqual(patterns[-1]._callback_str, 'tests.views.execute_sql') - - def test_request_urlconf_string_per_request(self): - request = rf.get('/') - request.urlconf = 'debug_toolbar.urls' - middleware = DebugToolbarMiddleware() - - middleware.process_request(request) - request.urlconf = 'tests.urls' - middleware.process_request(request) - - self.assertFalse(isinstance(request.urlconf, six.string_types)) - - patterns = request.urlconf.urlpatterns - self.assertTrue(hasattr(patterns[1], '_callback_str')) - self.assertEqual(patterns[-1]._callback_str, 'tests.views.execute_sql') - - def test_request_urlconf_module(self): - request = rf.get('/') - request.urlconf = __import__('tests.urls').urls - middleware = DebugToolbarMiddleware() - - middleware.process_request(request) - - self.assertFalse(isinstance(request.urlconf, six.string_types)) - - patterns = request.urlconf.urlpatterns - self.assertTrue(hasattr(patterns[1], '_callback_str')) - self.assertEqual(patterns[-1]._callback_str, 'tests.views.execute_sql') - - def test_tuple_urlconf(self): - request = rf.get('/') - urls = __import__('tests.urls').urls - urls.urlpatterns = tuple(urls.urlpatterns) - request.urlconf = urls - middleware = DebugToolbarMiddleware() - - middleware.process_request(request) - - self.assertFalse(isinstance(request.urlconf, six.string_types)) - def _resolve_stats(self, path): # takes stats from RequestVars panel self.request.path = path diff --git a/tests/urls.py b/tests/urls.py index 977084b..d4417a1 100644 --- a/tests/urls.py +++ b/tests/urls.py @@ -2,7 +2,7 @@ from __future__ import unicode_literals -from django.conf.urls import patterns, url +from django.conf.urls import include, patterns, url from django.contrib import admin from .models import NonAsciiRepr @@ -18,4 +18,5 @@ urlpatterns = patterns('tests.views', # noqa url(r'^non_ascii_request/$', 'regular_view', {'title': NonAsciiRepr()}), url(r'^new_user/$', 'new_user'), url(r'^execute_sql/$', 'execute_sql'), + url(r'^__debug__/', include('debug_toolbar.urls', namespace='djdt', app_name='djdt')), ) |
