web/lib/django/db/backends/creation.py
changeset 29 cc9b7e14412b
parent 0 0d40e90630ef
equal deleted inserted replaced
28:b758351d191f 29:cc9b7e14412b
     1 import sys
     1 import sys
     2 import time
     2 import time
     3 try:
       
     4     set
       
     5 except NameError:
       
     6     # Python 2.3 compat
       
     7     from sets import Set as set
       
     8 
     3 
     9 from django.conf import settings
     4 from django.conf import settings
    10 from django.core.management import call_command
     5 from django.core.management import call_command
    11 
     6 
    12 # The prefix to put on the default database name when creating
     7 # The prefix to put on the default database name when creating
    45         final_output = []
    40         final_output = []
    46         table_output = []
    41         table_output = []
    47         pending_references = {}
    42         pending_references = {}
    48         qn = self.connection.ops.quote_name
    43         qn = self.connection.ops.quote_name
    49         for f in opts.local_fields:
    44         for f in opts.local_fields:
    50             col_type = f.db_type()
    45             col_type = f.db_type(connection=self.connection)
    51             tablespace = f.db_tablespace or opts.db_tablespace
    46             tablespace = f.db_tablespace or opts.db_tablespace
    52             if col_type is None:
    47             if col_type is None:
    53                 # Skip ManyToManyFields, because they're not represented as
    48                 # Skip ManyToManyFields, because they're not represented as
    54                 # database columns in this table.
    49                 # database columns in this table.
    55                 continue
    50                 continue
    71                 if pending:
    66                 if pending:
    72                     pr = pending_references.setdefault(f.rel.to, []).append((model, f))
    67                     pr = pending_references.setdefault(f.rel.to, []).append((model, f))
    73                 else:
    68                 else:
    74                     field_output.extend(ref_output)
    69                     field_output.extend(ref_output)
    75             table_output.append(' '.join(field_output))
    70             table_output.append(' '.join(field_output))
    76         if opts.order_with_respect_to:
       
    77             table_output.append(style.SQL_FIELD(qn('_order')) + ' ' + \
       
    78                 style.SQL_COLTYPE(models.IntegerField().db_type()))
       
    79         for field_constraints in opts.unique_together:
    71         for field_constraints in opts.unique_together:
    80             table_output.append(style.SQL_KEYWORD('UNIQUE') + ' (%s)' % \
    72             table_output.append(style.SQL_KEYWORD('UNIQUE') + ' (%s)' % \
    81                 ", ".join([style.SQL_FIELD(qn(opts.get_field(f).column)) for f in field_constraints]))
    73                 ", ".join([style.SQL_FIELD(qn(opts.get_field(f).column)) for f in field_constraints]))
    82 
    74 
    83         full_statement = [style.SQL_KEYWORD('CREATE TABLE') + ' ' + style.SQL_TABLE(qn(opts.db_table)) + ' (']
    75         full_statement = [style.SQL_KEYWORD('CREATE TABLE') + ' ' + style.SQL_TABLE(qn(opts.db_table)) + ' (']
   143             del pending_references[model]
   135             del pending_references[model]
   144         return final_output
   136         return final_output
   145 
   137 
   146     def sql_for_many_to_many(self, model, style):
   138     def sql_for_many_to_many(self, model, style):
   147         "Return the CREATE TABLE statments for all the many-to-many tables defined on a model"
   139         "Return the CREATE TABLE statments for all the many-to-many tables defined on a model"
       
   140         import warnings
       
   141         warnings.warn(
       
   142             'Database creation API for m2m tables has been deprecated. M2M models are now automatically generated',
       
   143             PendingDeprecationWarning
       
   144         )
       
   145 
   148         output = []
   146         output = []
   149         for f in model._meta.local_many_to_many:
   147         for f in model._meta.local_many_to_many:
   150             if model._meta.managed or f.rel.to._meta.managed:
   148             if model._meta.managed or f.rel.to._meta.managed:
   151                 output.extend(self.sql_for_many_to_many_field(model, f, style))
   149                 output.extend(self.sql_for_many_to_many_field(model, f, style))
   152         return output
   150         return output
   153 
   151 
   154     def sql_for_many_to_many_field(self, model, f, style):
   152     def sql_for_many_to_many_field(self, model, f, style):
   155         "Return the CREATE TABLE statements for a single m2m field"
   153         "Return the CREATE TABLE statements for a single m2m field"
       
   154         import warnings
       
   155         warnings.warn(
       
   156             'Database creation API for m2m tables has been deprecated. M2M models are now automatically generated',
       
   157             PendingDeprecationWarning
       
   158         )
       
   159 
   156         from django.db import models
   160         from django.db import models
   157         from django.db.backends.util import truncate_name
   161         from django.db.backends.util import truncate_name
   158 
   162 
   159         output = []
   163         output = []
   160         if f.creates_table:
   164         if f.auto_created:
   161             opts = model._meta
   165             opts = model._meta
   162             qn = self.connection.ops.quote_name
   166             qn = self.connection.ops.quote_name
   163             tablespace = f.db_tablespace or opts.db_tablespace
   167             tablespace = f.db_tablespace or opts.db_tablespace
   164             if tablespace:
   168             if tablespace:
   165                 sql = self.connection.ops.tablespace_sql(tablespace, inline=True)
   169                 sql = self.connection.ops.tablespace_sql(tablespace, inline=True)
   171                 tablespace_sql = ''
   175                 tablespace_sql = ''
   172             table_output = [style.SQL_KEYWORD('CREATE TABLE') + ' ' + \
   176             table_output = [style.SQL_KEYWORD('CREATE TABLE') + ' ' + \
   173                 style.SQL_TABLE(qn(f.m2m_db_table())) + ' (']
   177                 style.SQL_TABLE(qn(f.m2m_db_table())) + ' (']
   174             table_output.append('    %s %s %s%s,' %
   178             table_output.append('    %s %s %s%s,' %
   175                 (style.SQL_FIELD(qn('id')),
   179                 (style.SQL_FIELD(qn('id')),
   176                 style.SQL_COLTYPE(models.AutoField(primary_key=True).db_type()),
   180                 style.SQL_COLTYPE(models.AutoField(primary_key=True).db_type(connection=self.connection)),
   177                 style.SQL_KEYWORD('NOT NULL PRIMARY KEY'),
   181                 style.SQL_KEYWORD('NOT NULL PRIMARY KEY'),
   178                 tablespace_sql))
   182                 tablespace_sql))
   179 
   183 
   180             deferred = []
   184             deferred = []
   181             inline_output, deferred = self.sql_for_inline_many_to_many_references(model, f, style)
   185             inline_output, deferred = self.sql_for_inline_many_to_many_references(model, f, style)
   208                     output.append(stmt)
   212                     output.append(stmt)
   209         return output
   213         return output
   210 
   214 
   211     def sql_for_inline_many_to_many_references(self, model, field, style):
   215     def sql_for_inline_many_to_many_references(self, model, field, style):
   212         "Create the references to other tables required by a many-to-many table"
   216         "Create the references to other tables required by a many-to-many table"
       
   217         import warnings
       
   218         warnings.warn(
       
   219             'Database creation API for m2m tables has been deprecated. M2M models are now automatically generated',
       
   220             PendingDeprecationWarning
       
   221         )
       
   222 
   213         from django.db import models
   223         from django.db import models
   214         opts = model._meta
   224         opts = model._meta
   215         qn = self.connection.ops.quote_name
   225         qn = self.connection.ops.quote_name
   216 
   226 
   217         table_output = [
   227         table_output = [
   218             '    %s %s %s %s (%s)%s,' %
   228             '    %s %s %s %s (%s)%s,' %
   219                 (style.SQL_FIELD(qn(field.m2m_column_name())),
   229                 (style.SQL_FIELD(qn(field.m2m_column_name())),
   220                 style.SQL_COLTYPE(models.ForeignKey(model).db_type()),
   230                 style.SQL_COLTYPE(models.ForeignKey(model).db_type(connection=self.connection)),
   221                 style.SQL_KEYWORD('NOT NULL REFERENCES'),
   231                 style.SQL_KEYWORD('NOT NULL REFERENCES'),
   222                 style.SQL_TABLE(qn(opts.db_table)),
   232                 style.SQL_TABLE(qn(opts.db_table)),
   223                 style.SQL_FIELD(qn(opts.pk.column)),
   233                 style.SQL_FIELD(qn(opts.pk.column)),
   224                 self.connection.ops.deferrable_sql()),
   234                 self.connection.ops.deferrable_sql()),
   225             '    %s %s %s %s (%s)%s,' %
   235             '    %s %s %s %s (%s)%s,' %
   226                 (style.SQL_FIELD(qn(field.m2m_reverse_name())),
   236                 (style.SQL_FIELD(qn(field.m2m_reverse_name())),
   227                 style.SQL_COLTYPE(models.ForeignKey(field.rel.to).db_type()),
   237                 style.SQL_COLTYPE(models.ForeignKey(field.rel.to).db_type(connection=self.connection)),
   228                 style.SQL_KEYWORD('NOT NULL REFERENCES'),
   238                 style.SQL_KEYWORD('NOT NULL REFERENCES'),
   229                 style.SQL_TABLE(qn(field.rel.to._meta.db_table)),
   239                 style.SQL_TABLE(qn(field.rel.to._meta.db_table)),
   230                 style.SQL_FIELD(qn(field.rel.to._meta.pk.column)),
   240                 style.SQL_FIELD(qn(field.rel.to._meta.pk.column)),
   231                 self.connection.ops.deferrable_sql())
   241                 self.connection.ops.deferrable_sql())
   232         ]
   242         ]
   243             output.extend(self.sql_indexes_for_field(model, f, style))
   253             output.extend(self.sql_indexes_for_field(model, f, style))
   244         return output
   254         return output
   245 
   255 
   246     def sql_indexes_for_field(self, model, f, style):
   256     def sql_indexes_for_field(self, model, f, style):
   247         "Return the CREATE INDEX SQL statements for a single model field"
   257         "Return the CREATE INDEX SQL statements for a single model field"
       
   258         from django.db.backends.util import truncate_name
       
   259 
   248         if f.db_index and not f.unique:
   260         if f.db_index and not f.unique:
   249             qn = self.connection.ops.quote_name
   261             qn = self.connection.ops.quote_name
   250             tablespace = f.db_tablespace or model._meta.db_tablespace
   262             tablespace = f.db_tablespace or model._meta.db_tablespace
   251             if tablespace:
   263             if tablespace:
   252                 sql = self.connection.ops.tablespace_sql(tablespace)
   264                 sql = self.connection.ops.tablespace_sql(tablespace)
   254                     tablespace_sql = ' ' + sql
   266                     tablespace_sql = ' ' + sql
   255                 else:
   267                 else:
   256                     tablespace_sql = ''
   268                     tablespace_sql = ''
   257             else:
   269             else:
   258                 tablespace_sql = ''
   270                 tablespace_sql = ''
       
   271             i_name = '%s_%s' % (model._meta.db_table, self._digest(f.column))
   259             output = [style.SQL_KEYWORD('CREATE INDEX') + ' ' +
   272             output = [style.SQL_KEYWORD('CREATE INDEX') + ' ' +
   260                 style.SQL_TABLE(qn('%s_%s' % (model._meta.db_table, f.column))) + ' ' +
   273                 style.SQL_TABLE(qn(truncate_name(i_name, self.connection.ops.max_name_length()))) + ' ' +
   261                 style.SQL_KEYWORD('ON') + ' ' +
   274                 style.SQL_KEYWORD('ON') + ' ' +
   262                 style.SQL_TABLE(qn(model._meta.db_table)) + ' ' +
   275                 style.SQL_TABLE(qn(model._meta.db_table)) + ' ' +
   263                 "(%s)" % style.SQL_FIELD(qn(f.column)) +
   276                 "(%s)" % style.SQL_FIELD(qn(f.column)) +
   264                 "%s;" % tablespace_sql]
   277                 "%s;" % tablespace_sql]
   265         else:
   278         else:
   298             r_name = '%s_refs_%s_%s' % (col, r_col, self._digest(table, r_table))
   311             r_name = '%s_refs_%s_%s' % (col, r_col, self._digest(table, r_table))
   299             output.append('%s %s %s %s;' % \
   312             output.append('%s %s %s %s;' % \
   300                 (style.SQL_KEYWORD('ALTER TABLE'),
   313                 (style.SQL_KEYWORD('ALTER TABLE'),
   301                 style.SQL_TABLE(qn(table)),
   314                 style.SQL_TABLE(qn(table)),
   302                 style.SQL_KEYWORD(self.connection.ops.drop_foreignkey_sql()),
   315                 style.SQL_KEYWORD(self.connection.ops.drop_foreignkey_sql()),
   303                 style.SQL_FIELD(truncate_name(r_name, self.connection.ops.max_name_length()))))
   316                 style.SQL_FIELD(qn(truncate_name(r_name, self.connection.ops.max_name_length())))))
   304         del references_to_delete[model]
   317         del references_to_delete[model]
   305         return output
   318         return output
   306 
   319 
   307     def sql_destroy_many_to_many(self, model, f, style):
   320     def sql_destroy_many_to_many(self, model, f, style):
   308         "Returns the DROP TABLE statements for a single m2m field"
   321         "Returns the DROP TABLE statements for a single m2m field"
       
   322         import warnings
       
   323         warnings.warn(
       
   324             'Database creation API for m2m tables has been deprecated. M2M models are now automatically generated',
       
   325             PendingDeprecationWarning
       
   326         )
       
   327 
   309         qn = self.connection.ops.quote_name
   328         qn = self.connection.ops.quote_name
   310         output = []
   329         output = []
   311         if f.creates_table:
   330         if f.auto_created:
   312             output.append("%s %s;" % (style.SQL_KEYWORD('DROP TABLE'),
   331             output.append("%s %s;" % (style.SQL_KEYWORD('DROP TABLE'),
   313                 style.SQL_TABLE(qn(f.m2m_db_table()))))
   332                 style.SQL_TABLE(qn(f.m2m_db_table()))))
   314             ds = self.connection.ops.drop_sequence_sql("%s_%s" % (model._meta.db_table, f.column))
   333             ds = self.connection.ops.drop_sequence_sql("%s_%s" % (model._meta.db_table, f.column))
   315             if ds:
   334             if ds:
   316                 output.append(ds)
   335                 output.append(ds)
   320         """
   339         """
   321         Creates a test database, prompting the user for confirmation if the
   340         Creates a test database, prompting the user for confirmation if the
   322         database already exists. Returns the name of the test database created.
   341         database already exists. Returns the name of the test database created.
   323         """
   342         """
   324         if verbosity >= 1:
   343         if verbosity >= 1:
   325             print "Creating test database..."
   344             print "Creating test database '%s'..." % self.connection.alias
   326 
   345 
   327         test_database_name = self._create_test_db(verbosity, autoclobber)
   346         test_database_name = self._create_test_db(verbosity, autoclobber)
   328 
   347 
   329         self.connection.close()
   348         self.connection.close()
   330         settings.DATABASE_NAME = test_database_name
   349         self.connection.settings_dict["NAME"] = test_database_name
   331         self.connection.settings_dict["DATABASE_NAME"] = test_database_name
       
   332         can_rollback = self._rollback_works()
   350         can_rollback = self._rollback_works()
   333         settings.DATABASE_SUPPORTS_TRANSACTIONS = can_rollback
   351         self.connection.settings_dict["SUPPORTS_TRANSACTIONS"] = can_rollback
   334         self.connection.settings_dict["DATABASE_SUPPORTS_TRANSACTIONS"] = can_rollback
   352 
   335 
   353         call_command('syncdb', verbosity=verbosity, interactive=False, database=self.connection.alias)
   336         call_command('syncdb', verbosity=verbosity, interactive=False)
       
   337 
   354 
   338         if settings.CACHE_BACKEND.startswith('db://'):
   355         if settings.CACHE_BACKEND.startswith('db://'):
   339             from django.core.cache import parse_backend_uri
   356             from django.core.cache import parse_backend_uri
   340             _, cache_name, _ = parse_backend_uri(settings.CACHE_BACKEND)
   357             _, cache_name, _ = parse_backend_uri(settings.CACHE_BACKEND)
   341             call_command('createcachetable', cache_name)
   358             call_command('createcachetable', cache_name)
   348 
   365 
   349     def _create_test_db(self, verbosity, autoclobber):
   366     def _create_test_db(self, verbosity, autoclobber):
   350         "Internal implementation - creates the test db tables."
   367         "Internal implementation - creates the test db tables."
   351         suffix = self.sql_table_creation_suffix()
   368         suffix = self.sql_table_creation_suffix()
   352 
   369 
   353         if settings.TEST_DATABASE_NAME:
   370         if self.connection.settings_dict['TEST_NAME']:
   354             test_database_name = settings.TEST_DATABASE_NAME
   371             test_database_name = self.connection.settings_dict['TEST_NAME']
   355         else:
   372         else:
   356             test_database_name = TEST_DATABASE_PREFIX + settings.DATABASE_NAME
   373             test_database_name = TEST_DATABASE_PREFIX + self.connection.settings_dict['NAME']
   357 
   374 
   358         qn = self.connection.ops.quote_name
   375         qn = self.connection.ops.quote_name
   359 
   376 
   360         # Create the test database and connect to it. We need to autocommit
   377         # Create the test database and connect to it. We need to autocommit
   361         # if the database supports it because PostgreSQL doesn't allow
   378         # if the database supports it because PostgreSQL doesn't allow
   401         """
   418         """
   402         Destroy a test database, prompting the user for confirmation if the
   419         Destroy a test database, prompting the user for confirmation if the
   403         database already exists. Returns the name of the test database created.
   420         database already exists. Returns the name of the test database created.
   404         """
   421         """
   405         if verbosity >= 1:
   422         if verbosity >= 1:
   406             print "Destroying test database..."
   423             print "Destroying test database '%s'..." % self.connection.alias
   407         self.connection.close()
   424         self.connection.close()
   408         test_database_name = settings.DATABASE_NAME
   425         test_database_name = self.connection.settings_dict['NAME']
   409         settings.DATABASE_NAME = old_database_name
   426         self.connection.settings_dict['NAME'] = old_database_name
   410         self.connection.settings_dict["DATABASE_NAME"] = old_database_name
       
   411 
   427 
   412         self._destroy_test_db(test_database_name, verbosity)
   428         self._destroy_test_db(test_database_name, verbosity)
   413 
   429 
   414     def _destroy_test_db(self, test_database_name, verbosity):
   430     def _destroy_test_db(self, test_database_name, verbosity):
   415         "Internal implementation - remove the test db tables."
   431         "Internal implementation - remove the test db tables."
   434             self.connection.connection.set_isolation_level(0)
   450             self.connection.connection.set_isolation_level(0)
   435 
   451 
   436     def sql_table_creation_suffix(self):
   452     def sql_table_creation_suffix(self):
   437         "SQL to append to the end of the test table creation statements"
   453         "SQL to append to the end of the test table creation statements"
   438         return ''
   454         return ''
   439