Ticket #2720: any-order2720.diff

File any-order2720.diff, 7.2 KB (added by yary h <spm-django@…>, 18 years ago)

works for any ordering and for M2M references across different apps

  • django/core/management.py

     
    112112        final_output.extend(_get_sql_for_pending_references(model, pending_references))
    113113        # Keep track of the fact that we've created the table for this model.
    114114        known_models.add(model)
     115        # Create the many-to-many join tables.
     116        final_output.extend(_get_many_to_many_sql_for_model(model,known_models,pending_references))
    115117
    116     # Create the many-to-many join tables.
    117     for model in app_models:
    118         final_output.extend(_get_many_to_many_sql_for_model(model))
    119 
    120118    # Handle references to tables that are from other apps
    121119    # but don't exist physically
    122120    not_installed_models = set(pending_references.keys())
     
    145143    opts = model._meta
    146144    final_output = []
    147145    table_output = []
     146    constraints = []
    148147    pending_references = {}
    149148    for f in opts.fields:
    150149        if isinstance(f, (models.ForeignKey, models.OneToOneField)):
     
    165164                field_output.append(style.SQL_KEYWORD('PRIMARY KEY'))
    166165            if f.rel:
    167166                if f.rel.to in known_models:
    168                     field_output.append(style.SQL_KEYWORD('REFERENCES') + ' ' + \
     167                    ref_text=style.SQL_KEYWORD('REFERENCES') + ' ' + \
    169168                        style.SQL_TABLE(backend.quote_name(f.rel.to._meta.db_table)) + ' (' + \
    170169                        style.SQL_FIELD(backend.quote_name(f.rel.to._meta.get_field(f.rel.field_name).column)) + ')'
    171                     )
     170                    field_output.append(ref_text)
     171                    constraints.append('FOREIGN KEY (%s) %s' % \
     172                                           (style.SQL_FIELD(backend.quote_name(f.column)),
     173                                            ref_text))
    172174                else:
    173175                    # We haven't yet created the table to which this field
    174176                    # is related, so save it for later.
     
    178180        table_output.append(style.SQL_FIELD(backend.quote_name('_order')) + ' ' + \
    179181            style.SQL_COLTYPE(data_types['IntegerField']) + ' ' + \
    180182            style.SQL_KEYWORD('NULL'))
     183    table_output += constraints
    181184    for field_constraints in opts.unique_together:
    182185        table_output.append(style.SQL_KEYWORD('UNIQUE') + ' (%s)' % \
    183186            ", ".join([backend.quote_name(style.SQL_FIELD(opts.get_field(f).column)) for f in field_constraints]))
     
    198201    data_types = get_creation_module().DATA_TYPES
    199202
    200203    final_output = []
    201     if backend.supports_constraints:
     204    if backend.supports_constraints and model in pending_references:
    202205        opts = model._meta
    203         if model in pending_references:
    204             for rel_class, f in pending_references[model]:
    205                 rel_opts = rel_class._meta
    206                 r_table = rel_opts.db_table
     206        table = opts.db_table
     207        for rel_class, f in pending_references[model]:
     208            if type(rel_class) == dict:
     209                r_table = rel_class['table']
     210                r_col = rel_class['col']
     211                col = opts.pk.column
     212            else:
     213                r_table = rel_class._meta.db_table
    207214                r_col = f.column
    208                 table = opts.db_table
    209215                col = opts.get_field(f.rel.field_name).column
    210                 # For MySQL, r_name must be unique in the first 64 characters.
    211                 # So we are careful with character usage here.
    212                 r_name = '%s_refs_%s_%x' % (r_col, col, abs(hash((r_table, table))))
    213                 final_output.append(style.SQL_KEYWORD('ALTER TABLE') + ' %s ADD CONSTRAINT %s FOREIGN KEY (%s) REFERENCES %s (%s);' % \
    214                     (backend.quote_name(r_table), r_name,
    215                     backend.quote_name(r_col), backend.quote_name(table), backend.quote_name(col)))
    216             del pending_references[model]
     216            # For MySQL, r_name must be unique in the first 64 characters.
     217            # So we are careful with character usage here.
     218            r_name = '%s_refs_%s_%x' % (r_col, col, abs(hash((r_table, table))))
     219            final_output.append(style.SQL_KEYWORD('ALTER TABLE') + ' %s ADD CONSTRAINT %s FOREIGN KEY (%s) REFERENCES %s (%s);' % \
     220                (backend.quote_name(r_table), r_name,
     221                backend.quote_name(r_col), backend.quote_name(table), backend.quote_name(col)))
     222        del pending_references[model]
    217223    return final_output
    218224
    219 def _get_many_to_many_sql_for_model(model):
     225def _get_many_to_many_sql_for_model(model,known_models,pending_references):
    220226    from django.db import backend, get_creation_module
    221227    from django.db.models import GenericRel
    222228
     
    224230
    225231    opts = model._meta
    226232    final_output = []
     233
    227234    for f in opts.many_to_many:
    228235        if not isinstance(f.rel, GenericRel):
    229236            table_output = [style.SQL_KEYWORD('CREATE TABLE') + ' ' + \
     
    244251                style.SQL_KEYWORD('NOT NULL REFERENCES'),
    245252                style.SQL_TABLE(backend.quote_name(f.rel.to._meta.db_table)),
    246253                style.SQL_FIELD(backend.quote_name(f.rel.to._meta.pk.column))))
     254            table_output.append('    FOREIGN KEY (%s) REFERENCES %s (%s),' % \
     255                (style.SQL_FIELD(backend.quote_name(f.m2m_column_name())),
     256                style.SQL_TABLE(backend.quote_name(opts.db_table)),
     257                style.SQL_FIELD(backend.quote_name(opts.pk.column))))
     258            if f.rel.to in known_models:
     259                table_output.append('    FOREIGN KEY (%s) REFERENCES %s (%s),' % \
     260                     (style.SQL_FIELD(backend.quote_name(f.m2m_reverse_name())),
     261                      style.SQL_TABLE(backend.quote_name(f.rel.to._meta.db_table)),
     262                      style.SQL_FIELD(backend.quote_name(f.rel.to._meta.pk.column))))
     263            else:
     264                pending_references.setdefault(f.rel.to, []).append(
     265                    ({'table':f.m2m_db_table(),'col':f.m2m_reverse_name()}, f))
    247266            table_output.append('    %s (%s, %s)' % \
    248267                (style.SQL_KEYWORD('UNIQUE'),
    249268                style.SQL_FIELD(backend.quote_name(f.m2m_column_name())),
     
    485504            for statement in sql:
    486505                cursor.execute(statement)
    487506            table_list.append(model._meta.db_table)
     507            sql = _get_many_to_many_sql_for_model(model,seen_models,pending_references)
     508            if sql:
     509                if verbosity >= 2:
     510                    print "Creating many-to-many tables for %s.%s model" % (app_name, model._meta.object_name)
     511                for statement in sql:
     512                    cursor.execute(statement)
    488513
    489         for model in model_list:
    490             if model in created_models:
    491                 sql = _get_many_to_many_sql_for_model(model)
    492                 if sql:
    493                     if verbosity >= 2:
    494                         print "Creating many-to-many tables for %s.%s model" % (app_name, model._meta.object_name)
    495                     for statement in sql:
    496                         cursor.execute(statement)
    497 
    498514        transaction.commit_unless_managed()
    499515
    500516    # Send the post_syncdb signal, so individual apps can do whatever they need
Back to Top