[Django]-How to use django-debug-toolbar for django-tastypie?

40👍

Here is a middleware I wrote for similar purposes that wraps json in HTML for enabling the debug toolbar and also pretty prints it. Furthermore, it supports binary data. I’m not using tastypie, but I think it should work with that too.

# settings-dev.py
from django.http import HttpResponse
import json   

MIDDLEWARE_CLASSES += (
    'debug_toolbar.middleware.DebugToolbarMiddleware',
    'NonHtmlDebugToolbarMiddleware',
)

class NonHtmlDebugToolbarMiddleware(object):
    """
    The Django Debug Toolbar usually only works for views that return HTML.
    This middleware wraps any non-HTML response in HTML if the request
    has a 'debug' query parameter (e.g. http://localhost/foo?debug)
    Special handling for json (pretty printing) and
    binary data (only show data length)
    """

    @staticmethod
    def process_response(request, response):
        if request.GET.get('debug') == '':
            if response['Content-Type'] == 'application/octet-stream':
                new_content = '<html><body>Binary Data, ' \
                    'Length: {}</body></html>'.format(len(response.content))
                response = HttpResponse(new_content)
            elif response['Content-Type'] != 'text/html':
                content = response.content
                try:
                    json_ = json.loads(content)
                    content = json.dumps(json_, sort_keys=True, indent=2)
                except ValueError:
                    pass
                response = HttpResponse('<html><body><pre>{}'
                                        '</pre></body></html>'.format(content))

        return response

8👍

The Django Debug Toolbar’s middleware actually has code in it to prevent it being activated for non-html type responses like those returned by TastyPie. What I have done in the past is create a bit of middleware that converts json responses into HTML so the toolbar will be activated and I can count queries etc… It is a bit of a hack but it gets to job done and is easy to turn on/off.

from django.conf import settings


class JsonAsHTML(object):
    '''
    View a JSON response in your browser as HTML
    Useful for viewing stats using Django Debug Toolbar 

    This middleware should be place AFTER Django Debug Toolbar middleware   
    '''

    def process_response(self, request, response):

        #not for production or production like environment 
        if not settings.DEBUG:
            return response

        #do nothing for actual ajax requests
        if request.is_ajax():
            return response

        #only do something if this is a json response
        if "application/json" in response['Content-Type'].lower():
            title = "JSON as HTML Middleware for: %s" % request.get_full_path()
            response.content = "<html><head><title>%s</title></head><body>%s</body></html>" % (title, response.content)
            response['Content-Type'] = 'text/html'
        return response

3👍

Django 1.10 introduced ‘new style middleware’: https://docs.djangoproject.com/en/2.0/releases/1.10/#new-style-middleware

This is a new style middleware version:

import json

from django.http import HttpResponse


class NonHtmlDebugToolbarMiddleware:
    """
    The Django Debug Toolbar usually only works for views that return HTML.
    This middleware wraps any non-HTML response in HTML if the request
    has a 'debug' query parameter (e.g. http://localhost/foo?debug)
    Special handling for json (pretty printing) and
    binary data (only show data length)
    """
    def __init__(self, get_response):
        self.get_response = get_response

    def __call__(self, request):
        response = self.get_response(request)

        if response['Content-Type'] == 'application/json':
            content = response.content
            try:
                json_ = json.loads(content)
                content = json.dumps(json_, sort_keys=True, indent=2)
            except ValueError:
                pass

            response = HttpResponse('<html><body><pre>{}'
                                    '</pre></body></html>'.format(content),
                                    content_type='text/html')

        return response
👤Mikle

2👍

I’m afraid that’s not possible. See the accepted answer for a workable solution.

Here’s why your approach did not work:

The toolbar does not kick-in because the answer is not in HTML. All other formats can’t be “parsed” by the toolbar’s middleware to include the Toolbar.

You can add your own tools to show SQL queries though. Take a look at this simple snippet: http://djangosnippets.org/snippets/161/ Or you can use a third party app for this, like django-snippetscream.

For example, you could check if DEBUG is True and add this info to the “meta” object returned by Tastypie.

Also, take a look at the SQL logging in your console (runserver). Some useful resource for this: http://dabapps.com/blog/logging-sql-queries-django-13/

1👍

Try https://github.com/django-debug-toolbar/django-debug-toolbar/pull/253

pip install git+https://github.com/caktus/django-debug-toolbar@ajax-panel#egg=django-debug-toolbar

This will allow debug toolbar to display information about the request on the calling page.

Alternatively if you want an HTML renderer and your not too far into your project, I highly recommend django-rest-framework

👤Thomas

1👍

@html_decorator
def test(request):

    view = resolve("/api/v1/albumimage/like/user/%d/" % 2 )

    accept =  request.META.get("HTTP_ACCEPT")
    accept += ",application/json"
    request.META["HTTP_ACCEPT"] = accept   
    res = view.func(request, **view.kwargs)

    return HttpResponse(res._container)



def html_decorator(func):
    """                                                                                                                                                                                                                                                                       
    wrap it inside html                                                                                                                                                                                                                                                       
    """

    def _decorated(*args, ** kwargs):
        response = func(*args, **kwargs)

        wrapped = ("<html><body>",
                   response.content,
                   "</body></html>")

        return HttpResponse(wrapped)

    return _decorated

This is how I solved it.
Ok it’s not automatic, but will do for now.

👤eugene

1👍

I fixed that by rebuilding django snippest from http://djangosnippets.org/snippets/344/

"""
Database and request debug info for Tastypie.

Based of idea from http://djangosnippets.org/snippets/344/

# settings.py:
DEBUG=True
DEBUG_SQL=True

MIDDLEWARE_CLASSES = (
    'YOURPATH.SQLLogMiddleware.SQLLogMiddleware',
    'django.middleware.transaction.TransactionMiddleware',
    ...)

"""

# Python
import time
import logging
import json

# Django
from django.conf import settings
from django.db import connection


class SQLLogMiddleware:
    """\
    Attach debug information to result json.
    """
    def process_request(self, request):
        request.sqllog_start = time.time()

    def process_response (self, request, response):
        # request.sqllog_start is empty if an append slash redirect happened.
        debug_sql = getattr(settings, "DEBUG_SQL", False)
        if not getattr(request, 'sqllog_start', False):
            return response
        if (not request.sqllog_start) or not (settings.DEBUG and debug_sql):
            return response

        try:
            content = json.loads(response.content)
        except ValueError:
            return response
        timesql = 0.0
        for query in connection.queries:
            timesql += float(query['time'])
        seen = {}
        duplicate = 0
        for query in connection.queries:
            sql = query["sql"]
            c = seen.get(sql, 0)
            if c:
                duplicate += 1
            if c:
                query["seen"] = c + 1
            seen[sql] = c + 1

        timerequest = round(time.time() - request.sqllog_start, 3)
        queries = connection.queries

        debug = {'request_path': request.path,
                 'query_count': len(queries),
                 'duplicate_query_count': duplicate,
                 'sql_execute_time': timesql,
                 'request_execution_time': timerequest,
                 'queries': []}

        for query in queries:
            debug['queries'].append({'time': query['time'],
                                     'sql': query['sql']})

        content['debug'] = debug
        response.content = json.dumps(content)
        logging.info(debug)
        return response

0👍

With version 3.1 of the Django Debug Toolbar, there is a History Panel which removes the need to wrap the JSON responses with html.

Leave a comment