Django

Code

Changeset 3760

Show
Ignore:
Timestamp:
09/14/06 15:18:24 (2 years ago)
Author:
jpellerin
Message:

[multi-db] Fixed bugs in handling of pending references. Fixed dropping of test database, and ensured that it drops even if syncdb() fails.

Files:

Legend:

Unmodified
Added
Removed
Modified
Copied
Moved
  • django/branches/multiple-db-support/django/core/management.py

    r3757 r3760  
    7474    # named connection, if there are any named connections 
    7575    connection_output = {} 
    76     pending_references = {} 
     76    pending = {} 
    7777    final_output = [] 
    7878     
    7979    app_models = models.get_models(app, creation_order=True) 
    8080    for model in app_models: 
     81 
     82        print "Get create sql for model", model 
     83         
    8184        opts = model._meta 
    8285        connection_name = model_connection_name(model) 
     
    110113            tables = [] 
    111114 
    112         installed_models = [ model for model in 
     115        installed_models = [ m for m in 
    113116                             manager.get_installed_models(tables) 
    114                              if model not in app_models ] 
     117                             if m not in app_models ] 
    115118        models_output = set(installed_models)  
    116119        builder = creation.builder 
    117120        builder.models_already_seen.update(models_output) 
    118         model_output, references = builder.get_create_table(model, style
     121        model_output, pending = builder.get_create_table(model, style, pending
    119122        output.extend(model_output) 
    120         for refto, refs in references.items(): 
    121             try: 
    122                 pending_references[refto].extend(refs) 
    123             except KeyError: 
    124                 pending_references[refto] = refs 
    125         if model in pending_references: 
    126             output.extend(pending_references.pop(model)) 
    127123 
    128124        # Create the many-to-many join tables. 
     
    132128 
    133129    final_output = _collate(connection_output) 
     130     
    134131    # Handle references to tables that are from other apps 
    135132    # but don't exist physically 
    136     not_installed_models = set(pending_references.keys()) 
     133    not_installed_models = set(pending.keys()) 
    137134    if not_installed_models: 
    138135        alter_sql = [] 
    139136        for model in not_installed_models: 
    140             alter_sql.extend(['-- ' + sql 
    141                               for sql in pending_references.pop(model)]) 
     137            builder = model._default_manager.db.builder.get_creation_module().builder 
     138             
     139            for rel_class, f in pending[model]: 
     140                sql = builder._ref_sql(model, rel_class, f, style) 
     141                alter_sql.extend(['-- ', str(sql)]) 
    142142        if alter_sql: 
    143143            final_output.append('-- The following references should be added ' 
     
    407407            # Don't re-install already-installed models 
    408408            if not model in models_installed: 
    409                 new_pending = manager.install(initial_data=initial_data) 
     409                pending = manager.install(initial_data=initial_data, 
     410                                          pending=pending) 
    410411                created_models.append(model) 
    411                 for dep_model, statements in new_pending.items(): 
    412                     pending.setdefault(dep_model, []).extend(statements) 
    413             # Execute any pending statements that were waiting for this model 
    414             if model in pending: 
    415                 for statement in pending.pop(model): 
    416                     statement.execute()                 
     412                 
    417413        if pending:             
    418             for model, statements in pending.items(): 
    419                 manager = model._default_manager  
    420                 tables = manager.get_table_list() 
    421                 models_installed = manager.get_installed_models(tables) 
     414            models_installed = manager.get_installed_models(tables) 
     415 
     416            for model in pending.keys(): 
    422417                if model in models_installed: 
    423                     for statement in statements: 
    424                         statement.execute() 
     418                    for rel_class, f in pending[model]: 
     419                        manager = model._default_manager  
     420                        for statement in manager.get_pending(rel_class, f): 
     421                            statement.execute() 
     422                    pending.pop(model) 
    425423                else: 
    426424                    raise Exception("%s is not installed, but there are " 
  • django/branches/multiple-db-support/django/db/backends/ansi/sql.py

    r3581 r3760  
    5151        self.tables = None 
    5252         
    53     def get_create_table(self, model, style=None): 
     53    def get_create_table(self, model, style=None, pending=None): 
    5454        """Construct and return the SQL expression(s) needed to create the 
    5555        table for the given model, and any constraints on that 
     
    6262        if style is None: 
    6363            style = default_style 
     64        if pending is None: 
     65            pending = {} 
    6466        self.models_already_seen.add(model) 
    6567         
     
    7173        table_output = [] 
    7274 
    73         # pending field references, keyed by the model class 
    74         # they reference 
    75         pending_references = {} 
    76  
    77         # pending statements to execute, keyed by 
    78         # the model class they reference 
    79         pending = {} 
    8075        for f in opts.fields: 
    8176            if isinstance(f, models.ForeignKey): 
     
    109104                        # We haven't yet created the table to which this field 
    110105                        # is related, so save it for later. 
    111                         pending_references.setdefault(f.rel.to, []).append(f
     106                        pending.setdefault(f.rel.to, []).append((model, f)
    112107                table_output.append(' '.join(field_output)) 
    113108        if opts.order_with_respect_to: 
     
    129124        create = [BoundStatement('\n'.join(full_statement), db.connection)] 
    130125 
    131         if (pending_references and 
     126        # Pull out any pending statements for me 
     127        if (pending and 
    132128            backend.supports_constraints): 
    133             for rel_class, cols in pending_references.items(): 
    134                 for f in cols: 
    135                     rel_opts = rel_class._meta 
    136                     r_table = rel_opts.db_table 
    137                     r_col = f.column 
    138                     table = opts.db_table 
    139                     col = opts.get_field(f.rel.field_name).column 
    140                     # For MySQL, r_name must be unique in the first 64  
    141                     # characters. So we are careful with character usage here. 
    142                     r_name = '%s_refs_%s_%x' % (col, r_col, 
    143                                                 abs(hash((r_table, table)))) 
    144                     sql = style.SQL_KEYWORD('ALTER TABLE') + ' %s ADD CONSTRAINT %s FOREIGN KEY (%s) REFERENCES %s (%s);' % \ 
    145                         (quote_name(table), quote_name(r_name), 
    146                         quote_name(r_col), quote_name(r_table), quote_name(col)) 
    147                     pending.setdefault(rel_class, []).append( 
    148                         BoundStatement(sql, db.connection)) 
     129            if model in pending: 
     130                for rel_class, f in pending[model]: 
     131                    create.append(self.get_ref_sql(model, rel_class, f, 
     132                                                   style=style)) 
     133                # What was pending for me is now no longer pending 
     134                pending.pop(model) 
    149135        return (create, pending)     
    150136 
     
    323309                                          and 'IntegerField' \ 
    324310                                          or f.get_internal_type() 
    325      
     311 
     312    def get_ref_sql(self, model, rel_class, f, style=None): 
     313        """Get sql statement for a reference between model and rel_class on 
     314        field f. 
     315        """ 
     316        if style is None: 
     317            style = default_style 
     318         
     319        db = model._default_manager.db 
     320        qn = db.backend.quote_name 
     321        opts = model._meta 
     322        rel_opts = rel_class._meta 
     323        table = rel_opts.db_table 
     324        r_col = f.column 
     325        r_table = opts.db_table 
     326        col = opts.get_field(f.rel.field_name).column 
     327        # For MySQL, r_name must be unique in the first 64  
     328        # characters. So we are careful with character usage here. 
     329        r_name = '%s_refs_%s_%x' % (col, r_col, 
     330                                    abs(hash((r_table, table)))) 
     331        sql = style.SQL_KEYWORD('ALTER TABLE') + \ 
     332              ' %s ADD CONSTRAINT %s FOREIGN KEY (%s) REFERENCES %s (%s);' % \ 
     333              (qn(table), qn(r_name), 
     334               qn(r_col), qn(r_table), qn(col)) 
     335        return BoundStatement(sql, db.connection) 
     336         
    326337    def get_references(self): 
    327338        """Fill (if needed) and return the reference cache. 
  • django/branches/multiple-db-support/django/db/models/manager.py

    r3667 r3760  
    119119    ####################### 
    120120 
    121     def install(self, initial_data=False): 
     121    def install(self, initial_data=False, pending=None): 
    122122        """Install my model's table, indexes and (if requested) initial data. 
    123123 
     
    128128        install time.) 
    129129        """ 
     130        if pending is None: 
     131            pending = {} 
    130132        builder = self.db.get_creation_module().builder 
    131         run, pending = builder.get_create_table(self.model
     133        run, pending = builder.get_create_table(self.model, pending=pending
    132134        run += builder.get_create_indexes(self.model) 
    133135        many_many = builder.get_create_many_to_many(self.model) 
     
    144146            self.load_initial_data() 
    145147        return pending 
     148 
     149    def get_pending(self, rel_class, f): 
     150        builder = self.db.get_creation_module().builder 
     151        return builder.get_ref_sql(self.model, rel_class, f) 
    146152 
    147153    def load_initial_data(self): 
  • django/branches/multiple-db-support/django/test/simple.py

    r3755 r3760  
    7979    old_name = settings.DATABASE_NAME 
    8080    create_test_db(verbosity) 
    81     management.syncdb(verbosity, interactive=False) 
    82     unittest.TextTestRunner(verbosity=verbosity).run(suite) 
    83     destroy_test_db(old_name, verbosity) 
    84      
    85     teardown_test_environment() 
     81    try: 
     82        management.syncdb(verbosity, interactive=False)         
     83        unittest.TextTestRunner(verbosity=verbosity).run(suite) 
     84    finally: 
     85        destroy_test_db(old_name, verbosity) 
     86        teardown_test_environment() 
  • django/branches/multiple-db-support/django/test/utils.py

    r3739 r3760  
    6767        _set_autocommit(connection) 
    6868        try: 
    69             cursor.execute("CREATE DATABASE %s" % qn(db_name)) 
     69            cursor.execute("CREATE DATABASE %s" % qn(TEST_DATABASE_NAME)) 
    7070        except Exception, e:             
    7171            sys.stderr.write("Got an error creating the test database: %s\n" % e) 
    7272            if not autoclobber: 
    73                 confirm = raw_input("It appears the test database, %s, already exists. Type 'yes' to delete it, or 'no' to cancel: " % db_name
     73                confirm = raw_input("It appears the test database, %s, already exists. Type 'yes' to delete it, or 'no' to cancel: " % TEST_DATABASE_NAME
    7474            if autoclobber or confirm == 'yes': 
    7575                try: 
    7676                    if verbosity >= 1: 
    7777                        print "Destroying old test database..."                 
    78                     cursor.execute("DROP DATABASE %s" % qn(db_name)) 
     78                    cursor.execute("DROP DATABASE %s" % qn(TEST_DATABASE_NAME)) 
    7979                    if verbosity >= 1: 
    8080                        print "Creating test database..." 
    81                     cursor.execute("CREATE DATABASE %s" % qn(db_name)) 
     81                    cursor.execute("CREATE DATABASE %s" % qn(TEST_DATABASE_NAME)) 
    8282                except Exception, e: 
    8383                    sys.stderr.write("Got an error recreating the test database: %s\n" % e) 
     
    119119    if verbosity >= 1: 
    120120        print "Destroying test database..." 
    121     connection.close() 
     121    for cnx in connections.keys(): 
     122        connections[cnx].close() 
    122123    TEST_DATABASE_NAME = settings.DATABASE_NAME 
    123124    settings.DATABASE_NAME = old_database_name 
     
    125126    if settings.DATABASE_ENGINE != "sqlite3": 
    126127        settings.OTHER_DATABASES = old_databases 
     128        for cnx in connections.keys(): 
     129            connections[cnx].connection.cursor() 
    127130        cursor = connection.cursor() 
    128131        _set_autocommit(connection) 
  • django/branches/multiple-db-support/tests/modeltests/multiple_databases/models.py

    r3712 r3760  
    7979>>> from django.conf import settings 
    8080 
    81 # The default connection is in there, but let's ignore it 
     81# Connections are referenced by name 
     82>>> connections['_a'] 
     83Connection: ... 
     84>>> connections['_b'] 
     85Connection: ... 
     86 
     87# Let's see what connections are available.The default connection is 
     88# in there, but let's ignore it 
    8289 
    8390>>> non_default = connections.keys() 
     
    8693>>> non_default 
    8794['_a', '_b'] 
    88  
    89 # Each connection references its settings 
    90 >>> connections['_a'].settings.DATABASE_NAME == settings.OTHER_DATABASES['_a']['DATABASE_NAME'] 
    91 True 
    92 >>> connections['_b'].settings.DATABASE_NAME == settings.OTHER_DATABASES['_b']['DATABASE_NAME'] 
    93 True 
    9495     
    9596# Invalid connection names raise ImproperlyConfigured 
  • django/branches/multiple-db-support/tests/regressiontests/ansi_sql/models.py

    r3712 r3760  
    2121>>> builder.models_already_seen = set() 
    2222>>> builder.get_create_table(Mod) 
    23 ([BoundStatement('CREATE TABLE "ansi_sql_mod" (..."car_id" integer NOT NULL,...);')], {<class 'regressiontests.ansi_sql.models.Car'>: [BoundStatement('ALTER TABLE "ansi_sql_mod" ADD CONSTRAINT ... FOREIGN KEY ("car_id") REFERENCES "ansi_sql_car" ("id");')]}) 
     23([BoundStatement('CREATE TABLE "ansi_sql_mod" (..."car_id" integer NOT NULL,...);')], {<class 'regressiontests.ansi_sql.models.Car'>: [(<class 'regressiontests.ansi_sql.models.Mod'>, <django.db.models.fields.related.ForeignKey...>)]}) 
    2424>>> builder.models_already_seen = set() 
    2525>>> builder.get_create_table(Car) 
     
    4646# >>> builder.get_initialdata_path = othertests_sql 
    4747>>> builder.get_initialdata(Car) 
    48 [BoundStatement('insert into ansi_sql_car (...)...values (...);')] 
     48[BoundStatement("insert into ansi_sql_car (...)...values (...);...")] 
    4949 
    5050# test drop 
  • django/branches/multiple-db-support/tests/regressiontests/ansi_sql/sql/car.sql

    r3712 r3760  
    11insert into ansi_sql_car (make, model, year, condition) 
    2        values ("Chevy", "Impala", 1966, "mint"); 
     2       values ('Chevy', 'Impala', 1966, 'mint'); 
  • django/branches/multiple-db-support/tests/regressiontests/manager_schema_manipulation/tests.py

    r3712 r3760  
    124124>>> result = PA.objects.install() 
    125125>>> result 
    126 {<class 'regressiontests.manager_schema_manipulation.tests.PC'>: [BoundStatement('ALTER TABLE "msm_pa" ADD CONSTRAINT "id_refs_c_id..." FOREIGN KEY ("c_id") REFERENCES "msm_pc" ("id");')]} 
     126{<class 'regressiontests.manager_schema_manipulation.tests.PC'>: [(<class 'regressiontests.manager_schema_manipulation.tests.PA'>, <django.db.models.fields.related.ForeignKey ...>)]} 
    127127 
    128128# NOTE: restore real constraint flag