Ticket #3163: create_db_schema-trunk.diff
File create_db_schema-trunk.diff, 20.4 KB (added by , 16 years ago) |
---|
-
django/db/models/options.py
21 21 DEFAULT_NAMES = ('verbose_name', 'db_table', 'ordering', 22 22 'unique_together', 'permissions', 'get_latest_by', 23 23 'order_with_respect_to', 'app_label', 'db_tablespace', 24 'abstract' )24 'abstract', 'create_db_schema') 25 25 26 26 class Options(object): 27 27 def __init__(self, meta, app_label=None): … … 36 36 self.get_latest_by = None 37 37 self.order_with_respect_to = None 38 38 self.db_tablespace = settings.DEFAULT_TABLESPACE 39 self.create_db_schema = True 39 40 self.admin = None 40 41 self.meta = meta 41 42 self.pk = None -
django/db/backends/__init__.py
382 382 cursor = self.connection.cursor() 383 383 return self.get_table_list(cursor) 384 384 385 def django_table_names(self, only_existing=False ):385 def django_table_names(self, only_existing=False, filter_not_generated_tables=False): 386 386 """ 387 387 Returns a list of all table names that have associated Django models and 388 388 are in INSTALLED_APPS. 389 389 390 390 If only_existing is True, the resulting list will only include the tables 391 391 that actually exist in the database. 392 393 If filter_not_generated_tables is True, then all tables with associated Django models 394 which have Meta option create_db_schema=False will not be added to the list. 392 395 """ 393 396 from django.db import models 394 397 tables = set() 395 398 for app in models.get_apps(): 396 399 for model in models.get_models(app): 397 tables.add(model._meta.db_table) 398 tables.update([f.m2m_db_table() for f in model._meta.local_many_to_many]) 400 if (not filter_not_generated_tables) or (model._meta.create_db_schema): 401 tables.add(model._meta.db_table) 402 tables.update([f.m2m_db_table() for f in model._meta.local_many_to_many]) 399 403 if only_existing: 400 404 tables = [t for t in tables if t in self.table_names()] 401 405 return tables … … 411 415 if self.table_name_converter(m._meta.db_table) in map(self.table_name_converter, tables) 412 416 ]) 413 417 414 def sequence_list(self): 415 "Returns a list of information about all DB sequences for all models in all apps." 418 def sequence_list(self, filter_not_generated_tables=False): 419 """ 420 Returns a list of information about all DB sequences for all models in all apps. 421 422 If filter_not_generated_tables is True, then only the sequences for the Django models 423 which have Meta option create_db_schema=False will be added to the list. 424 """ 416 425 from django.db import models 417 426 418 427 apps = models.get_apps() … … 420 429 421 430 for app in apps: 422 431 for model in models.get_models(app): 423 for f in model._meta.local_fields: 424 if isinstance(f, models.AutoField): 425 sequence_list.append({'table': model._meta.db_table, 'column': f.column}) 426 break # Only one AutoField is allowed per model, so don't bother continuing. 432 if (not filter_not_generated_tables) or (model._meta.create_db_schema): 433 for f in model._meta.local_fields: 434 if isinstance(f, models.AutoField): 435 sequence_list.append({'table': model._meta.db_table, 'column': f.column}) 436 break # Only one AutoField is allowed per model, so don't bother continuing. 427 437 428 for f in model._meta.local_many_to_many:429 sequence_list.append({'table': f.m2m_db_table(), 'column': None})438 for f in model._meta.local_many_to_many: 439 sequence_list.append({'table': f.m2m_db_table(), 'column': None}) 430 440 431 441 return sequence_list 432 442 … … 445 455 def validate_field(self, errors, opts, f): 446 456 "By default, there is no backend-specific validation" 447 457 pass 448 -
django/db/backends/creation.py
71 71 table_output.append(style.SQL_KEYWORD('UNIQUE') + ' (%s)' % \ 72 72 ", ".join([style.SQL_FIELD(qn(opts.get_field(f).column)) for f in field_constraints])) 73 73 74 full_statement = [style.SQL_KEYWORD('CREATE TABLE') + ' ' + style.SQL_TABLE(qn(opts.db_table)) + ' ('] 75 for i, line in enumerate(table_output): # Combine and add commas. 76 full_statement.append(' %s%s' % (line, i < len(table_output)-1 and ',' or '')) 77 full_statement.append(')') 78 if opts.db_tablespace: 79 full_statement.append(self.connection.ops.tablespace_sql(opts.db_tablespace)) 80 full_statement.append(';') 81 final_output.append('\n'.join(full_statement)) 74 # Now build up the CREATE TABLE section but only if the model requires it 75 if opts.create_db_schema: 76 full_statement = [style.SQL_KEYWORD('CREATE TABLE') + ' ' + style.SQL_TABLE(qn(opts.db_table)) + ' ('] 77 for i, line in enumerate(table_output): # Combine and add commas. 78 full_statement.append(' %s%s' % (line, i < len(table_output)-1 and ',' or '')) 79 full_statement.append(')') 80 if opts.db_tablespace: 81 full_statement.append(self.connection.ops.tablespace_sql(opts.db_tablespace)) 82 full_statement.append(';') 83 final_output.append('\n'.join(full_statement)) 82 84 83 if opts.has_auto_field:84 # Add any extra SQL needed to support auto-incrementing primary keys.85 auto_column = opts.auto_field.db_column or opts.auto_field.name86 autoinc_sql = self.connection.ops.autoinc_sql(opts.db_table, auto_column)87 if autoinc_sql:88 for stmt in autoinc_sql:89 final_output.append(stmt)85 if opts.has_auto_field: 86 # Add any extra SQL needed to support auto-incrementing primary keys. 87 auto_column = opts.auto_field.db_column or opts.auto_field.name 88 autoinc_sql = self.connection.ops.autoinc_sql(opts.db_table, auto_column) 89 if autoinc_sql: 90 for stmt in autoinc_sql: 91 final_output.append(stmt) 90 92 91 93 return final_output, pending_references 92 94 … … 125 127 # For MySQL, r_name must be unique in the first 64 characters. 126 128 # So we are careful with character usage here. 127 129 r_name = '%s_refs_%s_%x' % (r_col, col, abs(hash((r_table, table)))) 128 final_output.append(style.SQL_KEYWORD('ALTER TABLE') + ' %s ADD CONSTRAINT %s FOREIGN KEY (%s) REFERENCES %s (%s)%s;' % \ 129 (qn(r_table), truncate_name(r_name, self.connection.ops.max_name_length()), 130 qn(r_col), qn(table), qn(col), 131 self.connection.ops.deferrable_sql())) 130 if rel_opts.create_db_schema: 131 final_output.append(style.SQL_KEYWORD('ALTER TABLE') + ' %s ADD CONSTRAINT %s FOREIGN KEY (%s) REFERENCES %s (%s)%s;' % \ 132 (qn(r_table), truncate_name(r_name, self.connection.ops.max_name_length()), 133 qn(r_col), qn(table), qn(col), 134 self.connection.ops.deferrable_sql())) 132 135 del pending_references[model] 133 136 return final_output 134 137 … … 259 262 qn = self.connection.ops.quote_name 260 263 output = ['%s %s;' % (style.SQL_KEYWORD('DROP TABLE'), 261 264 style.SQL_TABLE(qn(model._meta.db_table)))] 262 if model in references_to_delete :265 if model in references_to_delete and model._meta.create_db_schema: 263 266 output.extend(self.sql_remove_table_constraints(model, references_to_delete, style)) 264 267 265 268 if model._meta.has_auto_field: … … 279 282 r_table = model._meta.db_table 280 283 r_col = model._meta.get_field(f.rel.field_name).column 281 284 r_name = '%s_refs_%s_%x' % (col, r_col, abs(hash((table, r_table)))) 282 output.append('%s %s %s %s;' % \ 283 (style.SQL_KEYWORD('ALTER TABLE'), 284 style.SQL_TABLE(qn(table)), 285 style.SQL_KEYWORD(self.connection.ops.drop_foreignkey_sql()), 286 style.SQL_FIELD(truncate_name(r_name, self.connection.ops.max_name_length())))) 285 if rel_class._meta.create_db_schema: 286 output.append('%s %s %s %s;' % \ 287 (style.SQL_KEYWORD('ALTER TABLE'), 288 style.SQL_TABLE(qn(table)), 289 style.SQL_KEYWORD(self.connection.ops.drop_foreignkey_sql()), 290 style.SQL_FIELD(truncate_name(r_name, self.connection.ops.max_name_length())))) 287 291 del references_to_delete[model] 288 292 return output 289 293 … … 401 405 def sql_table_creation_suffix(self): 402 406 "SQL to append to the end of the test table creation statements" 403 407 return '' 404 -
django/core/management/commands/reset.py
45 45 * The database isn't running or isn't configured correctly. 46 46 * At least one of the database tables doesn't exist. 47 47 * The SQL was invalid. 48 Hint: Look at the output of 'django-admin.py sqlreset %s'. That's the SQL this command wasn't able to run .49 The full error: %s""" % (app_name, app_name, e))48 Hint: Look at the output of 'django-admin.py sqlreset %s'. That's the SQL this command wasn't able to run:\n%s. 49 The full error: %s""" % (app_name, app_name, sql, e)) 50 50 transaction.commit_unless_managed() 51 51 else: 52 52 print "Reset cancelled." -
django/core/management/commands/syncdb.py
41 41 # but raises an ImportError for some reason. The only way we 42 42 # can do this is to check the text of the exception. Note that 43 43 # we're a bit broad in how we check the text, because different 44 # Python implementations may not use the same text. 44 # Python implementations may not use the same text. 45 45 # CPython uses the text "No module named management" 46 46 # PyPy uses "No module named myproject.myapp.management" 47 47 msg = exc.args[0] … … 60 60 for app in models.get_apps(): 61 61 app_name = app.__name__.split('.')[-2] 62 62 model_list = models.get_models(app) 63 for model in model_list:63 for model in (m for m in model_list if m._meta.create_db_schema): 64 64 # Create the model's database table, if it doesn't already exist. 65 65 if verbosity >= 2: 66 66 print "Processing %s.%s model" % (app_name, model._meta.object_name) … … 99 99 # Send the post_syncdb signal, so individual apps can do whatever they need 100 100 # to do at this point. 101 101 emit_post_sync_signal(created_models, verbosity, interactive) 102 102 103 103 # The connection may have been closed by a syncdb handler. 104 104 cursor = connection.cursor() 105 105 106 106 # Install custom SQL for the app (but only if this 107 107 # is a model we've just created) 108 108 for app in models.get_apps(): … … 132 132 for app in models.get_apps(): 133 133 app_name = app.__name__.split('.')[-2] 134 134 for model in models.get_models(app): 135 if model in created_models :135 if model in created_models and model._meta.create_db_schema: 136 136 index_sql = connection.creation.sql_indexes_for_model(model, self.style) 137 137 if index_sql: 138 138 if verbosity >= 1: -
django/core/management/sql.py
119 119 def sql_flush(style, only_django=False): 120 120 """ 121 121 Returns a list of the SQL statements used to flush the database. 122 122 123 123 If only_django is True, then only table names that have associated Django 124 124 models and are in INSTALLED_APPS will be included. 125 125 """ 126 126 from django.db import connection 127 127 if only_django: 128 tables = connection.introspection.django_table_names( )128 tables = connection.introspection.django_table_names(filter_not_generated_tables=True) 129 129 else: 130 130 tables = connection.introspection.table_names() 131 statements = connection.ops.sql_flush(style, tables, connection.introspection.sequence_list( ))131 statements = connection.ops.sql_flush(style, tables, connection.introspection.sequence_list(filter_not_generated_tables=True)) 132 132 return statements 133 133 134 134 def sql_custom(app, style): … … 148 148 "Returns a list of the CREATE INDEX SQL statements for all models in the given app." 149 149 from django.db import connection, models 150 150 output = [] 151 for model in models.get_models(app):151 for model in (m for m in models.get_models(app) if m._meta.create_db_schema): 152 152 output.extend(connection.creation.sql_indexes_for_model(model, style)) 153 153 return output 154 154 -
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
430 430 ymasuda@ethercube.com 431 431 Jarek Zgoda <jarek.zgoda@gmail.com> 432 432 Cheng Zhang 433 Alexander Myodov <alex@myodov.com> 433 434 434 435 A big THANK YOU goes to: 435 436