Repository: jazzband/django-debug-toolbar

Issue:
support params in sql panel that cannot be JSON encoded by default JSONEncoder
If sql params are values that cannot be encoded by the built-in `json.JSONEncoder`, then the sql panel doesn't get the params and subsequently can't perform the query or `EXPLAIN` the query.

In my case, I often have params that are instance of `datetime.datetime`.

I currently use the following patch: https://github.com/zt-initech/django-debug-toolbar/commit/47e766434a81622d94432069257f7f4bc79e5dde

It is not very nice. I don't like using both JSON and pickle, and there are probably other problems.

How do you think this problem should be fixed?
For example, should params always be pickled?
Can you help me fix this patch to get it merged upstream?



Discussion:
This issue is probably related: https://github.com/jazzband/django-debug-toolbar/issues/747


Resolving patch:
diff --git a/debug_toolbar/panels/sql/tracking.py b/debug_toolbar/panels/sql/tracking.py
--- a/debug_toolbar/panels/sql/tracking.py
+++ b/debug_toolbar/panels/sql/tracking.py
@@ -1,5 +1,6 @@
 from __future__ import absolute_import, unicode_literals
 
+import datetime
 import json
 from threading import local
 from time import time
@@ -95,8 +96,10 @@ def _quote_params(self, params):
         return [self._quote_expr(p) for p in params]
 
     def _decode(self, param):
+        # make sure datetime, date and time are converted to string by force_text
+        CONVERT_TYPES = (datetime.datetime, datetime.date, datetime.time)
         try:
-            return force_text(param, strings_only=True)
+            return force_text(param, strings_only=not isinstance(param, CONVERT_TYPES))
         except UnicodeDecodeError:
             return '(encoded string)'
 
@@ -114,7 +117,7 @@ def _record(self, method, sql, params):
             _params = ''
             try:
                 _params = json.dumps([self._decode(p) for p in params])
-            except Exception:
+            except TypeError:
                 pass  # object not JSON serializable
 
             template_info = get_template_info()


Test patch:
diff --git a/tests/panels/test_sql.py b/tests/panels/test_sql.py
--- a/tests/panels/test_sql.py
+++ b/tests/panels/test_sql.py
@@ -2,10 +2,12 @@
 
 from __future__ import absolute_import, unicode_literals
 
+import datetime
 import unittest
 
 from django.contrib.auth.models import User
 from django.db import connection
+from django.db.models import Count
 from django.db.utils import DatabaseError
 from django.shortcuts import render
 from django.test.utils import override_settings
@@ -69,6 +71,35 @@ def test_non_ascii_query(self):
         # ensure the panel renders correctly
         self.assertIn('café', self.panel.content)
 
+    def test_param_conversion(self):
+        self.assertEqual(len(self.panel._queries), 0)
+
+        list(
+            User.objects
+                .filter(first_name='Foo')
+                .filter(is_staff=True)
+                .filter(is_superuser=False)
+        )
+        list(
+            User.objects
+                .annotate(group_count=Count('groups__id'))
+                .filter(group_count__lt=10)
+                .filter(group_count__gt=1)
+        )
+        list(User.objects.filter(date_joined=datetime.datetime(2017, 12, 22, 16, 7, 1)))
+
+        self.panel.process_response(self.request, self.response)
+        self.panel.generate_stats(self.request, self.response)
+
+        # ensure query was logged
+        self.assertEqual(len(self.panel._queries), 3)
+
+        self.assertEqual(tuple([q[1]['params'] for q in self.panel._queries]), (
+            '["Foo", true, false]',
+            '[10, 1]',
+            '["2017-12-22 16:07:01"]'
+        ))
+
     def test_insert_content(self):
         """
         Test that the panel only inserts content after generate_stats and