Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse code

Added a stealth option to flush to allow cascades.

This allows using flush on a subset of the tables without having to
manually cascade to all tables with foreign keys to the tables being
truncated, when they're known to be empty.

On databases where truncate is implemented with DELETE FROM, this
doesn't make a difference. The cascade is allowed, not mandatory.
  • Loading branch information...
commit 13b7f299de79e3eb101c3f015386eba39a8f3928 1 parent 7a65c95
Aymeric Augustin authored June 09, 2013
7  django/core/management/commands/flush.py
@@ -32,8 +32,9 @@ def handle_noargs(self, **options):
32 32
         connection = connections[db]
33 33
         verbosity = int(options.get('verbosity'))
34 34
         interactive = options.get('interactive')
35  
-        # 'reset_sequences' is a stealth option
  35
+        # 'reset_sequences' and 'allow_cascade' are stealth options
36 36
         reset_sequences = options.get('reset_sequences', True)
  37
+        allow_cascade = options.get('allow_cascade', False)
37 38
 
38 39
         self.style = no_style()
39 40
 
@@ -45,7 +46,9 @@ def handle_noargs(self, **options):
45 46
             except ImportError:
46 47
                 pass
47 48
 
48  
-        sql_list = sql_flush(self.style, connection, only_django=True, reset_sequences=reset_sequences)
  49
+        sql_list = sql_flush(self.style, connection, only_django=True,
  50
+                             reset_sequences=reset_sequences,
  51
+                             allow_cascade=allow_cascade)
49 52
 
50 53
         if interactive:
51 54
             confirm = input("""You have requested a flush of the database.
4  django/core/management/sql.py
@@ -102,7 +102,7 @@ def sql_delete(app, style, connection):
102 102
     return output[::-1]  # Reverse it, to deal with table dependencies.
103 103
 
104 104
 
105  
-def sql_flush(style, connection, only_django=False, reset_sequences=True):
  105
+def sql_flush(style, connection, only_django=False, reset_sequences=True, allow_cascade=False):
106 106
     """
107 107
     Returns a list of the SQL statements used to flush the database.
108 108
 
@@ -114,7 +114,7 @@ def sql_flush(style, connection, only_django=False, reset_sequences=True):
114 114
     else:
115 115
         tables = connection.introspection.table_names()
116 116
     seqs = connection.introspection.sequence_list() if reset_sequences else ()
117  
-    statements = connection.ops.sql_flush(style, tables, seqs)
  117
+    statements = connection.ops.sql_flush(style, tables, seqs, allow_cascade)
118 118
     return statements
119 119
 
120 120
 
8  django/db/backends/__init__.py
@@ -390,7 +390,7 @@ def constraint_checks_disabled(self):
390 390
     def disable_constraint_checking(self):
391 391
         """
392 392
         Backends can implement as needed to temporarily disable foreign key
393  
-        constraint checking. Should return True if the constraints were 
  393
+        constraint checking. Should return True if the constraints were
394 394
         disabled and will need to be reenabled.
395 395
         """
396 396
         return False
@@ -947,7 +947,7 @@ def set_time_zone_sql(self):
947 947
         """
948 948
         return ''
949 949
 
950  
-    def sql_flush(self, style, tables, sequences):
  950
+    def sql_flush(self, style, tables, sequences, allow_cascade=False):
951 951
         """
952 952
         Returns a list of SQL statements required to remove all data from
953 953
         the given database tables (without actually removing the tables
@@ -958,6 +958,10 @@ def sql_flush(self, style, tables, sequences):
958 958
 
959 959
         The `style` argument is a Style object as returned by either
960 960
         color_style() or no_style() in django.core.management.color.
  961
+
  962
+        The `allow_cascade` argument determines whether truncation may cascade
  963
+        to tables with foreign keys pointing the tables being truncated.
  964
+        PostgreSQL requires a cascade even if these tables are empty.
961 965
         """
962 966
         raise NotImplementedError()
963 967
 
7  django/db/backends/mysql/base.py
@@ -298,14 +298,17 @@ def quote_name(self, name):
298 298
     def random_function_sql(self):
299 299
         return 'RAND()'
300 300
 
301  
-    def sql_flush(self, style, tables, sequences):
  301
+    def sql_flush(self, style, tables, sequences, allow_cascade=False):
302 302
         # NB: The generated SQL below is specific to MySQL
303 303
         # 'TRUNCATE x;', 'TRUNCATE y;', 'TRUNCATE z;'... style SQL statements
304 304
         # to clear all tables of all data
305 305
         if tables:
306 306
             sql = ['SET FOREIGN_KEY_CHECKS = 0;']
307 307
             for table in tables:
308  
-                sql.append('%s %s;' % (style.SQL_KEYWORD('TRUNCATE'), style.SQL_FIELD(self.quote_name(table))))
  308
+                sql.append('%s %s;' % (
  309
+                    style.SQL_KEYWORD('TRUNCATE'),
  310
+                    style.SQL_FIELD(self.quote_name(table)),
  311
+                ))
309 312
             sql.append('SET FOREIGN_KEY_CHECKS = 1;')
310 313
             sql.extend(self.sequence_reset_by_name_sql(style, sequences))
311 314
             return sql
12  django/db/backends/oracle/base.py
@@ -339,17 +339,17 @@ def savepoint_create_sql(self, sid):
339 339
     def savepoint_rollback_sql(self, sid):
340 340
         return convert_unicode("ROLLBACK TO SAVEPOINT " + self.quote_name(sid))
341 341
 
342  
-    def sql_flush(self, style, tables, sequences):
  342
+    def sql_flush(self, style, tables, sequences, allow_cascade=False):
343 343
         # Return a list of 'TRUNCATE x;', 'TRUNCATE y;',
344 344
         # 'TRUNCATE z;'... style SQL statements
345 345
         if tables:
346 346
             # Oracle does support TRUNCATE, but it seems to get us into
347 347
             # FK referential trouble, whereas DELETE FROM table works.
348  
-            sql = ['%s %s %s;' % \
349  
-                    (style.SQL_KEYWORD('DELETE'),
350  
-                     style.SQL_KEYWORD('FROM'),
351  
-                     style.SQL_FIELD(self.quote_name(table)))
352  
-                    for table in tables]
  348
+            sql = ['%s %s %s;' % (
  349
+                style.SQL_KEYWORD('DELETE'),
  350
+                style.SQL_KEYWORD('FROM'),
  351
+                style.SQL_FIELD(self.quote_name(table))
  352
+            ) for table in tables]
353 353
             # Since we've just deleted all the rows, running our sequence
354 354
             # ALTER code will reset the sequence to 0.
355 355
             sql.extend(self.sequence_reset_by_name_sql(style, sequences))
19  django/db/backends/postgresql_psycopg2/operations.py
@@ -101,15 +101,24 @@ def quote_name(self, name):
101 101
     def set_time_zone_sql(self):
102 102
         return "SET TIME ZONE %s"
103 103
 
104  
-    def sql_flush(self, style, tables, sequences):
  104
+    def sql_flush(self, style, tables, sequences, allow_cascade=False):
105 105
         if tables:
106 106
             # Perform a single SQL 'TRUNCATE x, y, z...;' statement.  It allows
107 107
             # us to truncate tables referenced by a foreign key in any other
108 108
             # table.
109  
-            sql = ['%s %s;' % \
110  
-                (style.SQL_KEYWORD('TRUNCATE'),
111  
-                    style.SQL_FIELD(', '.join([self.quote_name(table) for table in tables]))
112  
-            )]
  109
+            tables_sql = ', '.join(
  110
+                style.SQL_FIELD(self.quote_name(table)) for table in tables)
  111
+            if allow_cascade:
  112
+                sql = ['%s %s %s;' % (
  113
+                    style.SQL_KEYWORD('TRUNCATE'),
  114
+                    tables_sql,
  115
+                    style.SQL_KEYWORD('CASCADE'),
  116
+                )]
  117
+            else:
  118
+                sql = ['%s %s;' % (
  119
+                    style.SQL_KEYWORD('TRUNCATE'),
  120
+                    tables_sql,
  121
+                )]
113 122
             sql.extend(self.sequence_reset_by_name_sql(style, sequences))
114 123
             return sql
115 124
         else:
12  django/db/backends/sqlite3/base.py
@@ -209,15 +209,15 @@ def quote_name(self, name):
209 209
     def no_limit_value(self):
210 210
         return -1
211 211
 
212  
-    def sql_flush(self, style, tables, sequences):
  212
+    def sql_flush(self, style, tables, sequences, allow_cascade=False):
213 213
         # NB: The generated SQL below is specific to SQLite
214 214
         # Note: The DELETE FROM... SQL generated below works for SQLite databases
215 215
         # because constraints don't exist
216  
-        sql = ['%s %s %s;' % \
217  
-                (style.SQL_KEYWORD('DELETE'),
218  
-                 style.SQL_KEYWORD('FROM'),
219  
-                 style.SQL_FIELD(self.quote_name(table))
220  
-                 ) for table in tables]
  216
+        sql = ['%s %s %s;' % (
  217
+            style.SQL_KEYWORD('DELETE'),
  218
+            style.SQL_KEYWORD('FROM'),
  219
+            style.SQL_FIELD(self.quote_name(table))
  220
+        ) for table in tables]
221 221
         # Note: No requirement for reset of auto-incremented indices (cf. other
222 222
         # sql_flush() implementations). Just return SQL at this point
223 223
         return sql

0 notes on commit 13b7f29

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