Ticket #3163: create_db_schema-trunk7512-qsrf_ready.diff
File create_db_schema-trunk7512-qsrf_ready.diff, 25.0 KB (added by , 17 years ago) |
---|
-
django/db/models/options.py
22 22 DEFAULT_NAMES = ('verbose_name', 'db_table', 'ordering', 23 23 'unique_together', 'permissions', 'get_latest_by', 24 24 'order_with_respect_to', 'app_label', 'db_tablespace', 25 'abstract' )25 'abstract', 'create_db_schema') 26 26 27 27 class Options(object): 28 28 def __init__(self, meta): … … 37 37 self.get_latest_by = None 38 38 self.order_with_respect_to = None 39 39 self.db_tablespace = settings.DEFAULT_TABLESPACE 40 self.create_db_schema = True 40 41 self.admin = None 41 42 self.meta = meta 42 43 self.pk = None -
django/core/management/commands/syncdb.py
57 57 app_name = app.__name__.split('.')[-2] 58 58 model_list = models.get_models(app) 59 59 for model in model_list: 60 # Create the model's database table, if it doesn't already exist. 61 if verbosity >= 2: 62 print "Processing %s.%s model" % (app_name, model._meta.object_name) 63 if table_name_converter(model._meta.db_table) in tables: 64 continue 65 sql, references = sql_model_create(model, self.style, seen_models) 66 seen_models.add(model) 67 created_models.add(model) 68 for refto, refs in references.items(): 69 pending_references.setdefault(refto, []).extend(refs) 70 if refto in seen_models: 71 sql.extend(sql_for_pending_references(refto, self.style, pending_references)) 72 sql.extend(sql_for_pending_references(model, self.style, pending_references)) 73 if verbosity >= 1: 74 print "Creating table %s" % model._meta.db_table 75 for statement in sql: 76 cursor.execute(statement) 77 tables.append(table_name_converter(model._meta.db_table)) 60 if model._meta.create_db_schema: 61 # Create the model's database table, if it doesn't already exist. 62 if verbosity >= 2: 63 print "Processing %s.%s model" % (app_name, model._meta.object_name) 64 if table_name_converter(model._meta.db_table) in tables: 65 continue 66 sql, references = sql_model_create(model, self.style, seen_models) 67 seen_models.add(model) 68 created_models.add(model) 69 for refto, refs in references.items(): 70 pending_references.setdefault(refto, []).extend(refs) 71 if refto in seen_models: 72 sql.extend(sql_for_pending_references(refto, self.style, pending_references)) 73 sql.extend(sql_for_pending_references(model, self.style, pending_references)) 74 if verbosity >= 1: 75 print "Creating table %s" % model._meta.db_table 76 for statement in sql: 77 cursor.execute(statement) 78 tables.append(table_name_converter(model._meta.db_table)) 78 79 79 80 # Create the m2m tables. This must be done after all tables have been created 80 81 # to ensure that all referred tables will exist. … … 121 122 app_name = app.__name__.split('.')[-2] 122 123 for model in models.get_models(app): 123 124 if model in created_models: 124 index_sql = sql_indexes_for_model(model, self.style) 125 if index_sql: 126 if verbosity >= 1: 127 print "Installing index for %s.%s model" % (app_name, model._meta.object_name) 128 try: 129 for sql in index_sql: 130 cursor.execute(sql) 131 except Exception, e: 132 sys.stderr.write("Failed to install index for %s.%s model: %s" % \ 133 (app_name, model._meta.object_name, e)) 134 transaction.rollback_unless_managed() 135 else: 136 transaction.commit_unless_managed() 125 if model._meta.create_db_schema: 126 index_sql = sql_indexes_for_model(model, self.style) 127 if index_sql: 128 if verbosity >= 1: 129 print "Installing index for %s.%s model" % (app_name, model._meta.object_name) 130 try: 131 for sql in index_sql: 132 cursor.execute(sql) 133 except Exception, e: 134 sys.stderr.write("Failed to install index for %s.%s model: %s" % \ 135 (app_name, model._meta.object_name, e)) 136 transaction.rollback_unless_managed() 137 else: 138 transaction.commit_unless_managed() 137 139 138 140 # Install the 'initial_data' fixture, using format discovery 139 141 from django.core.management import call_command -
django/core/management/sql.py
13 13 cursor = connection.cursor() 14 14 return get_introspection_module().get_table_list(cursor) 15 15 16 def django_table_list(only_existing=False ):16 def django_table_list(only_existing=False, filter_not_generated_tables=False): 17 17 """ 18 18 Returns a list of all table names that have associated Django models and 19 19 are in INSTALLED_APPS. 20 20 21 21 If only_existing is True, the resulting list will only include the tables 22 22 that actually exist in the database. 23 24 If filter_not_generated_tables is True, then all tables with associated Django models 25 which have Meta option create_db_schema=False will not be added to the list. 23 26 """ 24 27 from django.db import models 25 28 tables = [] 26 29 for app in models.get_apps(): 27 30 for model in models.get_models(app): 28 tables.append(model._meta.db_table) 29 tables.extend([f.m2m_db_table() for f in model._meta.local_many_to_many]) 31 if (not filter_not_generated_tables) or (model._meta.create_db_schema): 32 tables.append(model._meta.db_table) 33 tables.extend([f.m2m_db_table() for f in model._meta.local_many_to_many]) 30 34 if only_existing: 31 35 existing = table_list() 32 36 tables = [t for t in tables if t in existing] … … 45 49 converter = lambda x: x 46 50 return set([m for m in all_models if converter(m._meta.db_table) in map(converter, table_list)]) 47 51 48 def sequence_list(): 49 "Returns a list of information about all DB sequences for all models in all apps." 52 def sequence_list(filter_not_generated_tables=False): 53 """ 54 Returns a list of information about all DB sequences for all models in all apps. 55 56 If filter_not_generated_tables is True, then only the sequences for the Django models 57 which have Meta option create_db_schema=False will be added to the list. 58 """ 50 59 from django.db import models 51 60 52 61 apps = models.get_apps() … … 54 63 55 64 for app in apps: 56 65 for model in models.get_models(app): 57 for f in model._meta.local_fields: 58 if isinstance(f, models.AutoField): 59 sequence_list.append({'table': model._meta.db_table, 'column': f.column}) 60 break # Only one AutoField is allowed per model, so don't bother continuing. 66 if (not filter_not_generated_tables) or (model._meta.create_db_schema): 67 for f in model._meta.local_fields: 68 if isinstance(f, models.AutoField): 69 sequence_list.append({'table': model._meta.db_table, 'column': f.column}) 70 break # Only one AutoField is allowed per model, so don't bother continuing. 61 71 62 72 for f in model._meta.local_many_to_many: 63 73 sequence_list.append({'table': f.m2m_db_table(), 'column': None}) … … 158 168 for model in app_models: 159 169 if cursor and table_name_converter(model._meta.db_table) in table_names: 160 170 # Drop the table now 161 output.append('%s %s;' % (style.SQL_KEYWORD('DROP TABLE'), 162 style.SQL_TABLE(qn(model._meta.db_table)))) 171 if model._meta.create_db_schema: 172 output.append('%s %s;' % (style.SQL_KEYWORD('DROP TABLE'), 173 style.SQL_TABLE(qn(model._meta.db_table)))) 163 174 if connection.features.supports_constraints and model in references_to_delete: 164 175 for rel_class, f in references_to_delete[model]: 165 176 table = rel_class._meta.db_table … … 167 178 r_table = model._meta.db_table 168 179 r_col = model._meta.get_field(f.rel.field_name).column 169 180 r_name = '%s_refs_%s_%x' % (col, r_col, abs(hash((table, r_table)))) 170 output.append('%s %s %s %s;' % \ 171 (style.SQL_KEYWORD('ALTER TABLE'), 172 style.SQL_TABLE(qn(table)), 173 style.SQL_KEYWORD(connection.ops.drop_foreignkey_sql()), 174 style.SQL_FIELD(truncate_name(r_name, connection.ops.max_name_length())))) 181 if rel_class._meta.create_db_schema: 182 output.append('%s %s %s %s;' % \ 183 (style.SQL_KEYWORD('ALTER TABLE'), 184 style.SQL_TABLE(qn(table)), 185 style.SQL_KEYWORD(connection.ops.drop_foreignkey_sql()), 186 style.SQL_FIELD(truncate_name(r_name, connection.ops.max_name_length())))) 175 187 del references_to_delete[model] 176 188 if model._meta.has_auto_field: 177 189 ds = connection.ops.drop_sequence_sql(model._meta.db_table) … … 208 220 def sql_flush(style, only_django=False): 209 221 """ 210 222 Returns a list of the SQL statements used to flush the database. 211 223 212 224 If only_django is True, then only table names that have associated Django 213 225 models and are in INSTALLED_APPS will be included. 214 226 """ 215 227 from django.db import connection 216 228 if only_django: 217 tables = django_table_list( )229 tables = django_table_list(filter_not_generated_tables=True) 218 230 else: 219 231 tables = table_list() 220 statements = connection.ops.sql_flush(style, tables, sequence_list( ))232 statements = connection.ops.sql_flush(style, tables, sequence_list(filter_not_generated_tables=True)) 221 233 return statements 222 234 223 235 def sql_custom(app): … … 297 309 table_output.append(style.SQL_KEYWORD('UNIQUE') + ' (%s)' % \ 298 310 ", ".join([style.SQL_FIELD(qn(opts.get_field(f).column)) for f in field_constraints])) 299 311 300 full_statement = [style.SQL_KEYWORD('CREATE TABLE') + ' ' + style.SQL_TABLE(qn(opts.db_table)) + ' ('] 301 for i, line in enumerate(table_output): # Combine and add commas. 302 full_statement.append(' %s%s' % (line, i < len(table_output)-1 and ',' or '')) 303 full_statement.append(')') 304 if opts.db_tablespace and connection.features.supports_tablespaces: 305 full_statement.append(connection.ops.tablespace_sql(opts.db_tablespace)) 306 full_statement.append(';') 307 final_output.append('\n'.join(full_statement)) 312 # Now build up the CREATE TABLE section but only if the model requires it 313 if opts.create_db_schema: 314 full_statement = [style.SQL_KEYWORD('CREATE TABLE') + ' ' + style.SQL_TABLE(qn(opts.db_table)) + ' ('] 315 for i, line in enumerate(table_output): # Combine and add commas. 316 full_statement.append(' %s%s' % (line, i < len(table_output)-1 and ',' or '')) 317 full_statement.append(')') 318 if opts.db_tablespace and connection.features.supports_tablespaces: 319 full_statement.append(connection.ops.tablespace_sql(opts.db_tablespace)) 320 full_statement.append(';') 321 final_output.append('\n'.join(full_statement)) 308 322 309 if opts.has_auto_field:310 # Add any extra SQL needed to support auto-incrementing primary keys.311 auto_column = opts.auto_field.db_column or opts.auto_field.name312 autoinc_sql = connection.ops.autoinc_sql(opts.db_table, auto_column)313 if autoinc_sql:314 for stmt in autoinc_sql:315 final_output.append(stmt)323 if opts.has_auto_field: 324 # Add any extra SQL needed to support auto-incrementing primary keys. 325 auto_column = opts.auto_field.db_column or opts.auto_field.name 326 autoinc_sql = connection.ops.autoinc_sql(opts.db_table, auto_column) 327 if autoinc_sql: 328 for stmt in autoinc_sql: 329 final_output.append(stmt) 316 330 317 331 return final_output, pending_references 318 332 … … 337 351 # For MySQL, r_name must be unique in the first 64 characters. 338 352 # So we are careful with character usage here. 339 353 r_name = '%s_refs_%s_%x' % (r_col, col, abs(hash((r_table, table)))) 340 final_output.append(style.SQL_KEYWORD('ALTER TABLE') + ' %s ADD CONSTRAINT %s FOREIGN KEY (%s) REFERENCES %s (%s)%s;' % \ 341 (qn(r_table), truncate_name(r_name, connection.ops.max_name_length()), 342 qn(r_col), qn(table), qn(col), 343 connection.ops.deferrable_sql())) 354 if rel_opts.create_db_schema: 355 final_output.append(style.SQL_KEYWORD('ALTER TABLE') + ' %s ADD CONSTRAINT %s FOREIGN KEY (%s) REFERENCES %s (%s)%s;' % \ 356 (qn(r_table), truncate_name(r_name, connection.ops.max_name_length()), 357 qn(r_col), qn(table), qn(col), 358 connection.ops.deferrable_sql())) 344 359 del pending_references[model] 345 360 return final_output 346 361 … … 413 428 for r_table, r_col, table, col in deferred: 414 429 r_name = '%s_refs_%s_%x' % (r_col, col, 415 430 abs(hash((r_table, table)))) 416 final_output.append(style.SQL_KEYWORD('ALTER TABLE') + ' %s ADD CONSTRAINT %s FOREIGN KEY (%s) REFERENCES %s (%s)%s;' % 431 final_output.append(style.SQL_KEYWORD('ALTER TABLE') + ' %s ADD CONSTRAINT %s FOREIGN KEY (%s) REFERENCES %s (%s)%s;' % 417 432 (qn(r_table), 418 433 truncate_name(r_name, connection.ops.max_name_length()), 419 434 qn(r_col), qn(table), qn(col), … … 460 475 output = [] 461 476 462 477 qn = connection.ops.quote_name 463 for f in model._meta.local_fields: 464 if f.db_index and not ((f.primary_key or f.unique) and connection.features.autoindexes_primary_keys): 465 unique = f.unique and 'UNIQUE ' or '' 466 tablespace = f.db_tablespace or model._meta.db_tablespace 467 if tablespace and connection.features.supports_tablespaces: 468 tablespace_sql = ' ' + connection.ops.tablespace_sql(tablespace) 469 else: 470 tablespace_sql = '' 471 output.append( 472 style.SQL_KEYWORD('CREATE %sINDEX' % unique) + ' ' + \ 473 style.SQL_TABLE(qn('%s_%s' % (model._meta.db_table, f.column))) + ' ' + \ 474 style.SQL_KEYWORD('ON') + ' ' + \ 475 style.SQL_TABLE(qn(model._meta.db_table)) + ' ' + \ 476 "(%s)" % style.SQL_FIELD(qn(f.column)) + \ 477 "%s;" % tablespace_sql 478 ) 478 if model._meta.create_db_schema: 479 for f in model._meta.local_fields: 480 if f.db_index and not ((f.primary_key or f.unique) and connection.features.autoindexes_primary_keys): 481 unique = f.unique and 'UNIQUE ' or '' 482 tablespace = f.db_tablespace or model._meta.db_tablespace 483 if tablespace and connection.features.supports_tablespaces: 484 tablespace_sql = ' ' + connection.ops.tablespace_sql(tablespace) 485 else: 486 tablespace_sql = '' 487 output.append( 488 style.SQL_KEYWORD('CREATE %sINDEX' % unique) + ' ' + \ 489 style.SQL_TABLE(qn('%s_%s' % (model._meta.db_table, f.column))) + ' ' + \ 490 style.SQL_KEYWORD('ON') + ' ' + \ 491 style.SQL_TABLE(qn(model._meta.db_table)) + ' ' + \ 492 "(%s)" % style.SQL_FIELD(qn(f.column)) + \ 493 "%s;" % tablespace_sql 494 ) 479 495 return output 480 496 481 497 def emit_post_sync_signal(created_models, verbosity, interactive): -
tests/modeltests/create_db_schema/models.py
1 """ 2 xx. create_db_schema 3 4 Models can have a ``create_db_schema`` attribute, which specifies 5 whether the SQL code is generated for the table on various manage.py operations 6 or not. 7 """ 8 9 from django.db import models 10 11 """ 12 General test strategy: 13 * All tests are numbered (01, 02, 03... etc). 14 * Each test contains three models (A, B, C, followed with the number of test), 15 containing both indexed and non-indexed fields (to verify sql_index), 16 usual fields (model A), foreign keys (model B) and many-to-many fields (model C). 17 D table is generated automatically as intermediate M2M one. 18 * The normal (default; create_db_schema = True) behaviour during the manage.py 19 operations is not thoroughly checked; it is the duty of the appropriate tests 20 for the primary functionality of these operations. 21 The most attention is paid to whether the create_db_schema = False 22 disables the SQL generation properly. 23 * The intermediate table for M2M relations is not ever verified explicitly, 24 because it is not ever marked with create_db_schema explicitly. 25 """ 26 27 # This dictionary maps the name of the model/SQL table (like 'A01') 28 # to the boolean specifying whether this name should appear in the final SQL 29 checks = {} 30 31 32 """ 33 01: create_db_schema is not set. 34 In such case, it should be equal (by default) to True, 35 and SQL is generated for all three models. 36 """ 37 checks['A01'] = True 38 checks['B01'] = True 39 checks['C01'] = True 40 41 class A01(models.Model): 42 class Meta: db_table = 'A01' 43 44 f_a = models.TextField(db_index = True) 45 f_b = models.IntegerField() 46 47 class B01(models.Model): 48 class Meta: db_table = 'B01' 49 50 fk_a = models.ForeignKey(A01) 51 f_a = models.TextField(db_index = True) 52 f_b = models.IntegerField() 53 54 class C01(models.Model): 55 class Meta: db_table = 'C01' 56 57 mm_a = models.ManyToManyField(A01, db_table = 'D01') 58 f_a = models.TextField(db_index = True) 59 f_b = models.IntegerField() 60 61 62 """ 63 02: create_db_schema is set to True. 64 SQL is generated for all three models. 65 """ 66 checks['A02'] = True 67 checks['B02'] = True 68 checks['C02'] = True 69 70 class A02(models.Model): 71 class Meta: 72 db_table = 'A02' 73 create_db_schema = True 74 75 f_a = models.TextField(db_index = True) 76 f_b = models.IntegerField() 77 78 class B02(models.Model): 79 class Meta: 80 db_table = 'B02' 81 create_db_schema = True 82 83 fk_a = models.ForeignKey(A02) 84 f_a = models.TextField(db_index = True) 85 f_b = models.IntegerField() 86 87 class C02(models.Model): 88 class Meta: 89 db_table = 'C02' 90 create_db_schema = True 91 92 mm_a = models.ManyToManyField(A02, db_table = 'D02') 93 f_a = models.TextField(db_index = True) 94 f_b = models.IntegerField() 95 96 97 """ 98 03: create_db_schema is set to False. 99 SQL is NOT generated for any of the three models. 100 """ 101 checks['A03'] = False 102 checks['B03'] = False 103 checks['C03'] = False 104 105 class A03(models.Model): 106 class Meta: 107 db_table = 'A03' 108 create_db_schema = False 109 110 f_a = models.TextField(db_index = True) 111 f_b = models.IntegerField() 112 113 class B03(models.Model): 114 class Meta: 115 db_table = 'B03' 116 create_db_schema = False 117 118 fk_a = models.ForeignKey(A03) 119 f_a = models.TextField(db_index = True) 120 f_b = models.IntegerField() 121 122 class C03(models.Model): 123 class Meta: 124 db_table = 'C03' 125 create_db_schema = False 126 127 mm_a = models.ManyToManyField(A03, db_table = 'D03') 128 f_a = models.TextField(db_index = True) 129 f_b = models.IntegerField() 130 131 132 # We will use short names for these templates 133 sql_templates = { 134 'create table': 'CREATE TABLE "%s"', 135 'create index': 'CREATE INDEX "%s_f_a"', 136 'drop table': 'DROP TABLE "%s"', 137 'delete from': 'DELETE FROM "%s"' 138 } 139 140 def get_failed_models(arr_sql, sql_template_names): 141 """ 142 Find the models which should not be in the SQL but they are present, 143 or they should be in the SQL but they are missing. 144 """ 145 txt_sql = ' '.join(arr_sql) 146 for (model, should_be_present) in checks.iteritems(): 147 # Do we expect to see the model name in the SQL text? 148 for sql_template_name in sql_template_names: 149 # We are interested not in just the model name like "A01", 150 # but in the whole string like 'CREATE TABLE "A01"' 151 # so we apply the model name to the template 152 # to find out the expected string 153 expected = (sql_templates[sql_template_name])%model 154 if ((expected in txt_sql) != should_be_present): 155 # Our expectations failed! 156 yield 'The string %s %s present in SQL but it %s.'%( 157 expected, 158 {False: 'is not', True: 'is'}[expected in txt_sql], 159 {False: 'should not be', True: 'should be'}[should_be_present] 160 ) 161 162 163 __test__ = {'API_TESTS':""" 164 >>> from django.db.models import get_app 165 >>> from django.core.management.sql import * 166 >>> from django.core.management.color import no_style 167 >>> import sys 168 169 >>> myapp = get_app('create_db_schema') 170 >>> mystyle = no_style() 171 172 # a. Verify sql_create 173 >>> list(get_failed_models( sql_create(myapp, mystyle), ['create table'] )) 174 [] 175 176 # b. Verify sql_delete 177 >>> list(get_failed_models( sql_delete(myapp, mystyle), ['drop table'] )) 178 [] 179 180 # c. Verify sql_reset 181 >>> list(get_failed_models( sql_reset(myapp, mystyle), ['drop table', 'create table', 'create index'] )) 182 [] 183 184 # d. Verify sql_flush 185 >>> # sql_flush(mystyle) 186 >>> list(get_failed_models( sql_flush(mystyle), ['delete from'] )) 187 [] 188 189 # e. Verify sql_custom 190 # No custom data provided, should not be no output. 191 >>> sql_custom(myapp) 192 [] 193 194 # f. Verify sql_indexes 195 >>> list(get_failed_models( sql_indexes(myapp, mystyle), ['create index'] )) 196 [] 197 198 # g. Verify sql_all 199 >>> list(get_failed_models( sql_all(myapp, mystyle), ['create table', 'create index'] )) 200 [] 201 """} -
AUTHORS
382 382 ymasuda@ethercube.com 383 383 Jarek Zgoda <jarek.zgoda@gmail.com> 384 384 Cheng Zhang 385 Alexander Myodov <amyodov@gmail.com> 385 386 386 387 A big THANK YOU goes to: 387 388 -
docs/model-api.txt
1090 1090 that aren't allowed in Python variable names -- notably, the hyphen -- 1091 1091 that's OK. Django quotes column and table names behind the scenes. 1092 1092 1093 ``create_db_schema`` 1094 -------------------- 1095 1096 **New in Django development version** 1097 1098 Marks this model as requiring SQL operations when calling ``manage.py``:: 1099 1100 create_db_schema = False 1101 1102 If this isn't given, Django will use ``create_db_schema = True`` 1103 what means that the operations like ``manage.py sqlreset``, ``manage.py syncdb`` 1104 and others will regenerate the appropriate table when needed. 1105 If the option is set to False, the appropriate table will not be affected with 1106 any SQL operations. 1107 1108 This is useful for databases where some DB tables are controlled by Django models, 1109 but some other DB tables and views are created via raw SQL and should not be affected 1110 by any ``manage.py`` actions. 1111 Note that if the initial SQL data is provided (see `Providing initial SQL 1112 data`_ below), it still will be present in the output of 1113 ``sqlall``/``sqlcustom`` commands. 1114 1093 1115 ``db_tablespace`` 1094 1116 ----------------- 1095 1117