Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse code

Fixed #17760 -- Implemented callable database features as cached prop…

…erties

This does remove the requirement to call features.confirm() method
before checking the properties.
Thanks cdestiger and Ramiro Morales for their work on the patch.
  • Loading branch information...
commit aa423575e7b464433fcfc4bf4b8e1d7627b17ce6 1 parent 484fcd3
Claude Paroz authored June 09, 2012
3  django/contrib/gis/db/backends/spatialite/creation.py
@@ -31,9 +31,6 @@ def create_test_db(self, verbosity=1, autoclobber=False):
31 31
         self.connection.close()
32 32
         self.connection.settings_dict["NAME"] = test_database_name
33 33
 
34  
-        # Confirm the feature set of the test database
35  
-        self.connection.features.confirm()
36  
-
37 34
         # Need to load the SpatiaLite initialization SQL before running `syncdb`.
38 35
         self.load_spatialite_sql()
39 36
 
31  django/db/backends/__init__.py
@@ -10,6 +10,7 @@
10 10
 from django.db import DEFAULT_DB_ALIAS
11 11
 from django.db.backends import util
12 12
 from django.db.transaction import TransactionManagementError
  13
+from django.utils.functional import cached_property
13 14
 from django.utils.importlib import import_module
14 15
 from django.utils.timezone import is_aware
15 16
 
@@ -402,12 +403,10 @@ class BaseDatabaseFeatures(object):
402 403
     # Does the backend reset sequences between tests?
403 404
     supports_sequence_reset = True
404 405
 
405  
-    # Features that need to be confirmed at runtime
406  
-    # Cache whether the confirmation has been performed.
407  
-    _confirmed = False
408  
-    supports_transactions = None
409  
-    supports_stddev = None
410  
-    can_introspect_foreign_keys = None
  406
+    # Confirm support for introspected foreign keys
  407
+    # Every database can do this reliably, except MySQL,
  408
+    # which can't do it for MyISAM tables
  409
+    can_introspect_foreign_keys = True
411 410
 
412 411
     # Support for the DISTINCT ON clause
413 412
     can_distinct_on_fields = False
@@ -415,15 +414,8 @@ class BaseDatabaseFeatures(object):
415 414
     def __init__(self, connection):
416 415
         self.connection = connection
417 416
 
418  
-    def confirm(self):
419  
-        "Perform manual checks of any database features that might vary between installs"
420  
-        if not self._confirmed:
421  
-            self._confirmed = True
422  
-            self.supports_transactions = self._supports_transactions()
423  
-            self.supports_stddev = self._supports_stddev()
424  
-            self.can_introspect_foreign_keys = self._can_introspect_foreign_keys()
425  
-
426  
-    def _supports_transactions(self):
  417
+    @cached_property
  418
+    def supports_transactions(self):
427 419
         "Confirm support for transactions"
428 420
         cursor = self.connection.cursor()
429 421
         cursor.execute('CREATE TABLE ROLLBACK_TEST (X INT)')
@@ -436,7 +428,8 @@ def _supports_transactions(self):
436 428
         self.connection._commit()
437 429
         return count == 0
438 430
 
439  
-    def _supports_stddev(self):
  431
+    @cached_property
  432
+    def supports_stddev(self):
440 433
         "Confirm support for STDDEV and related stats functions"
441 434
         class StdDevPop(object):
442 435
             sql_function = 'STDDEV_POP'
@@ -447,12 +440,6 @@ class StdDevPop(object):
447 440
         except NotImplementedError:
448 441
             return False
449 442
 
450  
-    def _can_introspect_foreign_keys(self):
451  
-        "Confirm support for introspected foreign keys"
452  
-        # Every database can do this reliably, except MySQL,
453  
-        # which can't do it for MyISAM tables
454  
-        return True
455  
-
456 443
 
457 444
 class BaseDatabaseOperations(object):
458 445
     """
3  django/db/backends/creation.py
@@ -264,9 +264,6 @@ def create_test_db(self, verbosity=1, autoclobber=False):
264 264
         self.connection.close()
265 265
         self.connection.settings_dict["NAME"] = test_database_name
266 266
 
267  
-        # Confirm the feature set of the test database
268  
-        self.connection.features.confirm()
269  
-
270 267
         # Report syncdb messages at one level lower than that requested.
271 268
         # This ensures we don't get flooded with messages during testing
272 269
         # (unless you really ask to be flooded)
32  django/db/backends/mysql/base.py
@@ -37,6 +37,7 @@
37 37
 from django.db.backends.mysql.creation import DatabaseCreation
38 38
 from django.db.backends.mysql.introspection import DatabaseIntrospection
39 39
 from django.db.backends.mysql.validation import DatabaseValidation
  40
+from django.utils.functional import cached_property
40 41
 from django.utils.safestring import SafeString, SafeUnicode
41 42
 from django.utils import timezone
42 43
 
@@ -170,26 +171,25 @@ class DatabaseFeatures(BaseDatabaseFeatures):
170 171
 
171 172
     def __init__(self, connection):
172 173
         super(DatabaseFeatures, self).__init__(connection)
173  
-        self._storage_engine = None
174 174
 
  175
+    @cached_property
175 176
     def _mysql_storage_engine(self):
176 177
         "Internal method used in Django tests. Don't rely on this from your code"
177  
-        if self._storage_engine is None:
178  
-            cursor = self.connection.cursor()
179  
-            cursor.execute('CREATE TABLE INTROSPECT_TEST (X INT)')
180  
-            # This command is MySQL specific; the second column
181  
-            # will tell you the default table type of the created
182  
-            # table. Since all Django's test tables will have the same
183  
-            # table type, that's enough to evaluate the feature.
184  
-            cursor.execute("SHOW TABLE STATUS WHERE Name='INTROSPECT_TEST'")
185  
-            result = cursor.fetchone()
186  
-            cursor.execute('DROP TABLE INTROSPECT_TEST')
187  
-            self._storage_engine = result[1]
188  
-        return self._storage_engine
189  
-
190  
-    def _can_introspect_foreign_keys(self):
  178
+        cursor = self.connection.cursor()
  179
+        cursor.execute('CREATE TABLE INTROSPECT_TEST (X INT)')
  180
+        # This command is MySQL specific; the second column
  181
+        # will tell you the default table type of the created
  182
+        # table. Since all Django's test tables will have the same
  183
+        # table type, that's enough to evaluate the feature.
  184
+        cursor.execute("SHOW TABLE STATUS WHERE Name='INTROSPECT_TEST'")
  185
+        result = cursor.fetchone()
  186
+        cursor.execute('DROP TABLE INTROSPECT_TEST')
  187
+        return result[1]
  188
+
  189
+    @cached_property
  190
+    def can_introspect_foreign_keys(self):
191 191
         "Confirm support for introspected foreign keys"
192  
-        return self._mysql_storage_engine() != 'MyISAM'
  192
+        return self._mysql_storage_engine != 'MyISAM'
193 193
 
194 194
 class DatabaseOperations(BaseDatabaseOperations):
195 195
     compiler_module = "django.db.backends.mysql.compiler"
4  django/db/backends/sqlite3/base.py
@@ -19,6 +19,7 @@
19 19
 from django.db.backends.sqlite3.creation import DatabaseCreation
20 20
 from django.db.backends.sqlite3.introspection import DatabaseIntrospection
21 21
 from django.utils.dateparse import parse_date, parse_datetime, parse_time
  22
+from django.utils.functional import cached_property
22 23
 from django.utils.safestring import SafeString
23 24
 from django.utils import timezone
24 25
 
@@ -86,7 +87,8 @@ class DatabaseFeatures(BaseDatabaseFeatures):
86 87
     has_bulk_insert = True
87 88
     can_combine_inserts_with_and_without_auto_increment_pk = True
88 89
 
89  
-    def _supports_stddev(self):
  90
+    @cached_property
  91
+    def supports_stddev(self):
90 92
         """Confirm support for STDDEV and related stats functions
91 93
 
92 94
         SQLite supports STDDEV as an extension package; so
3  tests/regressiontests/backends/tests.py
@@ -403,8 +403,7 @@ def test_database_operations_helper_class(self):
403 403
         self.assertTrue(hasattr(connection.ops, 'connection'))
404 404
         self.assertEqual(connection, connection.ops.connection)
405 405
 
406  
-    def test_supports_needed_confirm(self):
407  
-        connection.features.confirm()
  406
+    def test_cached_db_features(self):
408 407
         self.assertIn(connection.features.supports_transactions, (True, False))
409 408
         self.assertIn(connection.features.supports_stddev, (True, False))
410 409
         self.assertIn(connection.features.can_introspect_foreign_keys, (True, False))
2  tests/regressiontests/transactions_regress/tests.py
@@ -208,7 +208,7 @@ def work():
208 208
         work()
209 209
 
210 210
     @skipIf(connection.vendor == 'mysql' and \
211  
-            connection.features._mysql_storage_engine() == 'MyISAM',
  211
+            connection.features._mysql_storage_engine == 'MyISAM',
212 212
             "MyISAM MySQL storage engine doesn't support savepoints")
213 213
     @skipUnlessDBFeature('uses_savepoints')
214 214
     def test_savepoint_rollback(self):

0 notes on commit aa42357

Please sign in to comment.
Something went wrong with that request. Please try again.