diff --git a/debug_toolbar/panels/sql/tracking.py b/debug_toolbar/panels/sql/tracking.py index 2ed691344..7090ac613 100644 --- a/debug_toolbar/panels/sql/tracking.py +++ b/debug_toolbar/panels/sql/tracking.py @@ -137,6 +137,14 @@ def _decode(self, param): def _record(self, method, sql, params): start_time = time() try: + if isinstance(params, list): + + def strip_GeomFromEWKB(param): + if isinstance(param, str): + return param.lstrip("ST_GeomFromEWKB('\\x").rstrip("'::bytea)") + return param + + params = [strip_GeomFromEWKB(param) for param in params] return method(sql, params) finally: stop_time = time() diff --git a/docs/installation.rst b/docs/installation.rst index e68ee593e..723c8f595 100644 --- a/docs/installation.rst +++ b/docs/installation.rst @@ -106,14 +106,14 @@ option. This option allows you to specify a custom function for this purpose. .. warning:: If using Docker the following will set your `INTERNAL_IPS` correctly only if you are in Debug mode.:: - + if DEBUG: import os # only if you haven't already imported this import socket # only if you haven't already imported this hostname, _, ips = socker.gethostbyname_ex(socket.gethostname()) INTERNAL_IPS = [ip[:-1] + '1' for ip in ips] + ['127.0.0.1', '10.0.2.2'] - - + + Troubleshooting --------------- diff --git a/tests/models.py b/tests/models.py index d6829eabc..0c070a7f2 100644 --- a/tests/models.py +++ b/tests/models.py @@ -1,3 +1,4 @@ +from django.conf import settings from django.db import models @@ -23,3 +24,10 @@ class Binary(models.Model): class PostgresJSON(models.Model): field = JSONField() + + +if settings.USE_GIS: + from django.contrib.gis.db import models as gismodels + + class Location(gismodels.Model): + point = gismodels.PointField() diff --git a/tests/panels/test_staticfiles.py b/tests/panels/test_staticfiles.py index d660b3c77..32ed7ea61 100644 --- a/tests/panels/test_staticfiles.py +++ b/tests/panels/test_staticfiles.py @@ -26,9 +26,10 @@ def test_default_case(self): ) self.assertEqual(self.panel.num_used, 0) self.assertNotEqual(self.panel.num_found, 0) - self.assertEqual( - self.panel.get_staticfiles_apps(), ["django.contrib.admin", "debug_toolbar"] - ) + expected_apps = ["django.contrib.admin", "debug_toolbar"] + if settings.USE_GIS: + expected_apps = ["django.contrib.gis"] + expected_apps + self.assertEqual(self.panel.get_staticfiles_apps(), expected_apps) self.assertEqual( self.panel.get_staticfiles_dirs(), finders.FileSystemFinder().locations ) @@ -74,9 +75,10 @@ def test_finder_directory_does_not_exist(self): ) self.assertEqual(self.panel.num_used, 0) self.assertNotEqual(self.panel.num_found, 0) - self.assertEqual( - self.panel.get_staticfiles_apps(), ["django.contrib.admin", "debug_toolbar"] - ) + expected_apps = ["django.contrib.admin", "debug_toolbar"] + if settings.USE_GIS: + expected_apps = ["django.contrib.gis"] + expected_apps + self.assertEqual(self.panel.get_staticfiles_apps(), expected_apps) self.assertEqual( self.panel.get_staticfiles_dirs(), finders.FileSystemFinder().locations ) diff --git a/tests/settings.py b/tests/settings.py index 2a4b5e68c..63456a2f6 100644 --- a/tests/settings.py +++ b/tests/settings.py @@ -27,6 +27,11 @@ "tests", ] +USE_GIS = os.getenv("DB_BACKEND") in ("postgis",) + +if USE_GIS: + INSTALLED_APPS = ["django.contrib.gis"] + INSTALLED_APPS + MEDIA_URL = "/media/" # Avoids https://code.djangoproject.com/ticket/21451 MIDDLEWARE = [ @@ -82,7 +87,9 @@ DATABASES = { "default": { - "ENGINE": "django.db.backends.%s" % os.getenv("DB_BACKEND", "sqlite3"), + "ENGINE": "django.{}db.backends.{}".format( + "contrib.gis." if USE_GIS else "", os.getenv("DB_BACKEND", "sqlite3") + ), "NAME": os.getenv("DB_NAME", ":memory:"), "USER": os.getenv("DB_USER"), "PASSWORD": os.getenv("DB_PASSWORD"), diff --git a/tests/test_integration.py b/tests/test_integration.py index 3be1ef589..bfee12a9f 100644 --- a/tests/test_integration.py +++ b/tests/test_integration.py @@ -1,9 +1,11 @@ +import json import os import re import unittest import django import html5lib +from django.conf import settings from django.contrib.staticfiles.testing import StaticLiveServerTestCase from django.core import signing from django.core.cache import cache @@ -16,6 +18,7 @@ from debug_toolbar.forms import SignedDataForm from debug_toolbar.middleware import DebugToolbarMiddleware, show_toolbar from debug_toolbar.panels import Panel +from debug_toolbar.panels.sql.forms import SQLSelectForm from debug_toolbar.toolbar import DebugToolbar from .base import BaseTestCase, IntegrationTestCase @@ -293,6 +296,28 @@ def test_sql_explain_checks_show_toolbar(self): ) self.assertEqual(response.status_code, 404) + @unittest.skipUnless(settings.USE_GIS, "Test only valid with gis support") + def test_sql_explain_gis(self): + from django.contrib.gis.geos import GEOSGeometry + + from .models import Location + + db_table = Location._meta.db_table + + url = "/__debug__/sql_explain/" + geom = GEOSGeometry("POLYGON((0 0, 0 1, 1 1, 0 0))") + data = { + "sql": f'SELECT "{db_table}"."point" FROM "{db_table}" WHERE "{db_table}"."point" @ {geom.hex} LIMIT 1', + "raw_sql": f'SELECT "{db_table}"."point" FROM "{db_table}" WHERE "{db_table}"."point" @ %s LIMIT 1', + "params": json.dumps([geom.hex]), + "alias": "default", + "duration": "0", + } + data["hash"] = SQLSelectForm().make_hash(data) + + response = self.client.post(url, data=data) + self.assertEqual(response.status_code, 200) + @unittest.skipUnless( connection.vendor == "postgresql", "Test valid only on PostgreSQL" ) diff --git a/tox.ini b/tox.ini index f1cb89a0b..0cd67796b 100644 --- a/tox.ini +++ b/tox.ini @@ -3,9 +3,9 @@ envlist = docs style packaging - py{36,37}-dj{22,31,32}-{sqlite,postgresql,mysql} - py{38,39}-dj{22,31,32,40,main}-{sqlite,postgresql,mysql} - py{310}-dj{40,main}-{sqlite,postgresql,mysql} + py{36,37}-dj{22,31,32}-{sqlite,postgresql,postgis,mysql} + py{38,39}-dj{22,31,32,40,main}-{sqlite,postgresql,postgis,mysql} + py{310}-dj{40,main}-{sqlite,postgresql,postgis,mysql} [testenv] deps = @@ -15,6 +15,7 @@ deps = dj40: Django>=4.0a1,<4.1 sqlite: mock postgresql: psycopg2-binary + postgis: psycopg2-binary mysql: mysqlclient djmain: https://github.com/django/django/archive/main.tar.gz coverage @@ -97,4 +98,5 @@ python = DB_BACKEND = mysql: mysql postgresql: postgresql + postgis: postgresql sqlite3: sqlite