Repository: adamchainz/django-perf-rec

Issue:
Django 1.8 support
It should be mostly compatible since we had it working on Django 1.8, but maybe the ORM patches will need updating.



Discussion:

Resolving patch:
diff --git a/django_perf_rec/db.py b/django_perf_rec/db.py
--- a/django_perf_rec/db.py
+++ b/django_perf_rec/db.py
@@ -4,6 +4,7 @@
 from functools import wraps
 from types import MethodType
 
+# import six
 from django.conf import settings
 from django.db import connections
 
@@ -12,12 +13,16 @@
 from .utils import sorted_names
 
 
+# @six.python_2_unicode_compatible
 class DBOp(object):
 
     def __init__(self, alias, sql):
         self.alias = alias
         self.sql = sql
 
+    def __repr__(self):
+        return "DBOp({!r}, {!r})".format(repr(self.alias), repr(self.sql))
+
     def __eq__(self, other):
         return (
             isinstance(other, DBOp) and


Test patch:
diff --git a/tests/django18_sqlite3_backend/__init__.py b/tests/django18_sqlite3_backend/__init__.py
new file mode 100644
--- /dev/null
+++ b/tests/django18_sqlite3_backend/__init__.py
@@ -0,0 +1,2 @@
+# -*- coding:utf-8 -*-
+from __future__ import absolute_import, division, print_function, unicode_literals
diff --git a/tests/django18_sqlite3_backend/base.py b/tests/django18_sqlite3_backend/base.py
new file mode 100644
--- /dev/null
+++ b/tests/django18_sqlite3_backend/base.py
@@ -0,0 +1,13 @@
+# -*- coding:utf-8 -*-
+from __future__ import absolute_import, division, print_function, unicode_literals
+
+from django.db.backends.sqlite3.base import DatabaseWrapper as OrigDatabaseWrapper
+
+from .operations import DatabaseOperations
+
+
+class DatabaseWrapper(OrigDatabaseWrapper):
+
+    def __init__(self, *args, **kwargs):
+        super(DatabaseWrapper, self).__init__(*args, **kwargs)
+        self.ops = DatabaseOperations(self)
diff --git a/tests/django18_sqlite3_backend/operations.py b/tests/django18_sqlite3_backend/operations.py
new file mode 100644
--- /dev/null
+++ b/tests/django18_sqlite3_backend/operations.py
@@ -0,0 +1,42 @@
+# -*- coding:utf-8 -*-
+from __future__ import absolute_import, division, print_function, unicode_literals
+
+from django.db.backends.sqlite3.operations import DatabaseOperations as OrigDatabaseOperations
+
+
+class DatabaseOperations(OrigDatabaseOperations):
+
+    # From Django commit 4f6a7663bcddffb114f2647f9928cbf1fdd8e4b5
+
+    def _quote_params_for_last_executed_query(self, params):
+        """
+        Only for last_executed_query! Don't use this to execute SQL queries!
+        """
+        sql = 'SELECT ' + ', '.join(['QUOTE(?)'] * len(params))
+        # Bypass Django's wrappers and use the underlying sqlite3 connection
+        # to avoid logging this query - it would trigger infinite recursion.
+        cursor = self.connection.connection.cursor()
+        # Native sqlite3 cursors cannot be used as context managers.
+        try:
+            return cursor.execute(sql, params).fetchone()
+        finally:
+            cursor.close()
+
+    def last_executed_query(self, cursor, sql, params):
+        # Python substitutes parameters in Modules/_sqlite/cursor.c with:
+        # pysqlite_statement_bind_parameters(self->statement, parameters, allow_8bit_chars);
+        # Unfortunately there is no way to reach self->statement from Python,
+        # so we quote and substitute parameters manually.
+        if params:
+            if isinstance(params, (list, tuple)):
+                params = self._quote_params_for_last_executed_query(params)
+            else:
+                keys = params.keys()
+                values = tuple(params.values())
+                values = self._quote_params_for_last_executed_query(values)
+                params = dict(zip(keys, values))
+            return sql % params
+        # For consistency with SQLiteCursorWrapper.execute(), just return sql
+        # when there are no parameters. See #13648 and #17158.
+        else:
+            return sql
diff --git a/tests/settings.py b/tests/settings.py
--- a/tests/settings.py
+++ b/tests/settings.py
@@ -3,6 +3,8 @@
 
 import os
 
+import django
+
 BASE_DIR = os.path.dirname(os.path.dirname(__file__))
 
 DEBUG = True
@@ -10,20 +12,28 @@
 
 SECRET_KEY = 'NOTASECRET'
 
+
+if django.VERSION[:2] == (1, 8):
+    # Use a wrapper that includes Django commit 4f6a7663bcddffb114f2647f9928cbf1fdd8e4b5
+    # so that full SQL queries from sqlite come through
+    engine = 'django18_sqlite3_backend'
+else:
+    engine = 'django.db.backends.sqlite3'
+
 DATABASES = {
     'default': {
-        'ENGINE': 'django.db.backends.sqlite3',
+        'ENGINE': engine,
         'NAME': ':memory:',
     },
     'replica': {
-        'ENGINE': 'django.db.backends.sqlite3',
+        'ENGINE': engine,
         'NAME': ':memory:',
         'TEST': {
             'MIRROR': 'default',
         }
     },
     'second': {
-        'ENGINE': 'django.db.backends.sqlite3',
+        'ENGINE': engine,
         'NAME': ':memory:',
     },
 }