Ticket #3163: sql_generation_required-trunk6635.diff

File sql_generation_required-trunk6635.diff, 13.0 KB (added by honeyman, 8 years ago)

sql_generation_required implementation (svn diff, Django trunk 6635)

  • django/db/models/options.py

     
    1515
    1616DEFAULT_NAMES = ('verbose_name', 'db_table', 'ordering',
    1717                 'unique_together', 'permissions', 'get_latest_by',
    18                  'order_with_respect_to', 'app_label', 'db_tablespace')
     18                 'order_with_respect_to', 'app_label', 'db_tablespace', 'sql_generation_required')
    1919
    2020class Options(object):
    2121    def __init__(self, meta):
     
    3030        self.get_latest_by = None
    3131        self.order_with_respect_to = None
    3232        self.db_tablespace = None
     33        self.sql_generation_required = True
    3334        self.admin = None
    3435        self.meta = meta
    3536        self.pk = None
  • django/core/management/sql.py

     
    1313    cursor = connection.cursor()
    1414    return get_introspection_module().get_table_list(cursor)
    1515
    16 def django_table_list(only_existing=False):
     16def django_table_list(only_existing=False, filter_if_sql_generation_not_required=False):
    1717    """
    1818    Returns a list of all table names that have associated Django models and
    1919    are in INSTALLED_APPS.
    2020
    2121    If only_existing is True, the resulting list will only include the tables
    2222    that actually exist in the database.
     23
     24    If filter_if_sql_generation_not_required is True, then all tables with associated Django models
     25    which have Meta option sql_generation_required=False will not be added to the list.
    2326    """
    2427    from django.db import models
    2528    tables = []
    2629    for app in models.get_apps():
    2730        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.many_to_many])
     31            if (not filter_if_sql_generation_not_required) or (model._meta.sql_generation_required):
     32                tables.append(model._meta.db_table)
     33                tables.extend([f.m2m_db_table() for f in model._meta.many_to_many])
    3034    if only_existing:
    3135        existing = table_list()
    3236        tables = [t for t in tables if t in existing]
     
    4549        converter = lambda x: x
    4650    return set([m for m in all_models if converter(m._meta.db_table) in map(converter, table_list)])
    4751
    48 def sequence_list():
    49     "Returns a list of information about all DB sequences for all models in all apps."
     52def sequence_list(filter_if_sql_generation_not_required=False):
     53    """
     54    Returns a list of information about all DB sequences for all models in all apps.
     55
     56    If filter_if_sql_generation_not_required is True, then only the sequences for the Django models
     57    which have Meta option sql_generation_required=False will be added to the list.
     58    """
    5059    from django.db import models
    5160
    5261    apps = models.get_apps()
     
    5463
    5564    for app in apps:
    5665        for model in models.get_models(app):
    57             for f in model._meta.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_if_sql_generation_not_required) or (model._meta.sql_generation_required):
     67                for f in model._meta.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.
    6171
    6272            for f in model._meta.many_to_many:
    6373                sequence_list.append({'table': f.m2m_db_table(), 'column': None})
     
    155165    for model in app_models:
    156166        if cursor and table_name_converter(model._meta.db_table) in table_names:
    157167            # Drop the table now
    158             output.append('%s %s;' % (style.SQL_KEYWORD('DROP TABLE'),
    159                 style.SQL_TABLE(qn(model._meta.db_table))))
     168            if model._meta.sql_generation_required:
     169                output.append('%s %s;' % (style.SQL_KEYWORD('DROP TABLE'),
     170                    style.SQL_TABLE(qn(model._meta.db_table))))
    160171            if connection.features.supports_constraints and model in references_to_delete:
    161172                for rel_class, f in references_to_delete[model]:
    162173                    table = rel_class._meta.db_table
     
    164175                    r_table = model._meta.db_table
    165176                    r_col = model._meta.get_field(f.rel.field_name).column
    166177                    r_name = '%s_refs_%s_%x' % (col, r_col, abs(hash((table, r_table))))
    167                     output.append('%s %s %s %s;' % \
    168                         (style.SQL_KEYWORD('ALTER TABLE'),
    169                         style.SQL_TABLE(qn(table)),
    170                         style.SQL_KEYWORD(connection.ops.drop_foreignkey_sql()),
    171                         style.SQL_FIELD(truncate_name(r_name, connection.ops.max_name_length()))))
     178                    if rel_class._meta.sql_generation_required:
     179                        output.append('%s %s %s %s;' % \
     180                            (style.SQL_KEYWORD('ALTER TABLE'),
     181                            style.SQL_TABLE(qn(table)),
     182                            style.SQL_KEYWORD(connection.ops.drop_foreignkey_sql()),
     183                            style.SQL_FIELD(truncate_name(r_name, connection.ops.max_name_length()))))
    172184                del references_to_delete[model]
    173185            if model._meta.has_auto_field:
    174186                ds = connection.ops.drop_sequence_sql(model._meta.db_table)
     
    203215def sql_flush(style, only_django=False):
    204216    """
    205217    Returns a list of the SQL statements used to flush the database.
    206    
     218
    207219    If only_django is True, then only table names that have associated Django
    208220    models and are in INSTALLED_APPS will be included.
    209221    """
    210222    from django.db import connection
    211223    if only_django:
    212         tables = django_table_list()
     224        tables = django_table_list(filter_if_sql_generation_not_required=True)
    213225    else:
    214226        tables = table_list()
    215     statements = connection.ops.sql_flush(style, tables, sequence_list())
     227    statements = connection.ops.sql_flush(style, tables, sequence_list(filter_if_sql_generation_not_required=True))
    216228    return statements
    217229
    218230def sql_custom(app):
     
    291303        table_output.append(style.SQL_KEYWORD('UNIQUE') + ' (%s)' % \
    292304            ", ".join([qn(style.SQL_FIELD(opts.get_field(f).column)) for f in field_constraints]))
    293305
    294     full_statement = [style.SQL_KEYWORD('CREATE TABLE') + ' ' + style.SQL_TABLE(qn(opts.db_table)) + ' (']
    295     for i, line in enumerate(table_output): # Combine and add commas.
    296         full_statement.append('    %s%s' % (line, i < len(table_output)-1 and ',' or ''))
    297     full_statement.append(')')
    298     if opts.db_tablespace and connection.features.supports_tablespaces:
    299         full_statement.append(connection.ops.tablespace_sql(opts.db_tablespace))
    300     full_statement.append(';')
    301     final_output.append('\n'.join(full_statement))
     306    # Now build up the CREATE TABLE section but only if the model requires it
     307    if opts.sql_generation_required:
     308        full_statement = [style.SQL_KEYWORD('CREATE TABLE') + ' ' + style.SQL_TABLE(qn(opts.db_table)) + ' (']
     309        for i, line in enumerate(table_output): # Combine and add commas.
     310            full_statement.append('    %s%s' % (line, i < len(table_output)-1 and ',' or ''))
     311        full_statement.append(')')
     312        if opts.db_tablespace and connection.features.supports_tablespaces:
     313            full_statement.append(connection.ops.tablespace_sql(opts.db_tablespace))
     314        full_statement.append(';')
     315        final_output.append('\n'.join(full_statement))
    302316
    303     if opts.has_auto_field:
    304         # Add any extra SQL needed to support auto-incrementing primary keys.
    305         auto_column = opts.auto_field.db_column or opts.auto_field.name
    306         autoinc_sql = connection.ops.autoinc_sql(opts.db_table, auto_column)
    307         if autoinc_sql:
    308             for stmt in autoinc_sql:
    309                 final_output.append(stmt)
     317        if opts.has_auto_field:
     318            # Add any extra SQL needed to support auto-incrementing primary keys.
     319            auto_column = opts.auto_field.db_column or opts.auto_field.name
     320            autoinc_sql = connection.ops.autoinc_sql(opts.db_table, auto_column)
     321            if autoinc_sql:
     322                for stmt in autoinc_sql:
     323                    final_output.append(stmt)
    310324
    311325    return final_output, pending_references
    312326
     
    331345                # For MySQL, r_name must be unique in the first 64 characters.
    332346                # So we are careful with character usage here.
    333347                r_name = '%s_refs_%s_%x' % (r_col, col, abs(hash((r_table, table))))
    334                 final_output.append(style.SQL_KEYWORD('ALTER TABLE') + ' %s ADD CONSTRAINT %s FOREIGN KEY (%s) REFERENCES %s (%s)%s;' % \
    335                     (qn(r_table), truncate_name(r_name, connection.ops.max_name_length()),
    336                     qn(r_col), qn(table), qn(col),
    337                     connection.ops.deferrable_sql()))
     348                if rel_opts.sql_generation_required:
     349                    final_output.append(style.SQL_KEYWORD('ALTER TABLE') + ' %s ADD CONSTRAINT %s FOREIGN KEY (%s) REFERENCES %s (%s)%s;' % \
     350                        (qn(r_table), truncate_name(r_name, connection.ops.max_name_length()),
     351                        qn(r_col), qn(table), qn(col),
     352                        connection.ops.deferrable_sql()))
    338353            del pending_references[model]
    339354    return final_output
    340355
     
    426441    output = []
    427442
    428443    qn = connection.ops.quote_name
    429     for f in model._meta.fields:
    430         if f.db_index and not ((f.primary_key or f.unique) and connection.features.autoindexes_primary_keys):
    431             unique = f.unique and 'UNIQUE ' or ''
    432             tablespace = f.db_tablespace or model._meta.db_tablespace
    433             if tablespace and connection.features.supports_tablespaces:
    434                 tablespace_sql = ' ' + connection.ops.tablespace_sql(tablespace)
    435             else:
    436                 tablespace_sql = ''
    437             output.append(
    438                 style.SQL_KEYWORD('CREATE %sINDEX' % unique) + ' ' + \
    439                 style.SQL_TABLE(qn('%s_%s' % (model._meta.db_table, f.column))) + ' ' + \
    440                 style.SQL_KEYWORD('ON') + ' ' + \
    441                 style.SQL_TABLE(qn(model._meta.db_table)) + ' ' + \
    442                 "(%s)" % style.SQL_FIELD(qn(f.column)) + \
    443                 "%s;" % tablespace_sql
    444             )
     444    if model._meta.sql_generation_required:
     445        for f in model._meta.fields:
     446            if f.db_index and not ((f.primary_key or f.unique) and connection.features.autoindexes_primary_keys):
     447                unique = f.unique and 'UNIQUE ' or ''
     448                tablespace = f.db_tablespace or model._meta.db_tablespace
     449                if tablespace and connection.features.supports_tablespaces:
     450                    tablespace_sql = ' ' + connection.ops.tablespace_sql(tablespace)
     451                else:
     452                    tablespace_sql = ''
     453                output.append(
     454                    style.SQL_KEYWORD('CREATE %sINDEX' % unique) + ' ' + \
     455                    style.SQL_TABLE(qn('%s_%s' % (model._meta.db_table, f.column))) + ' ' + \
     456                    style.SQL_KEYWORD('ON') + ' ' + \
     457                    style.SQL_TABLE(qn(model._meta.db_table)) + ' ' + \
     458                    "(%s)" % style.SQL_FIELD(qn(f.column)) + \
     459                    "%s;" % tablespace_sql
     460                )
    445461    return output
    446462
    447463def emit_post_sync_signal(created_models, verbosity, interactive):
  • docs/model-api.txt

     
    11491149that aren't allowed in Python variable names -- notably, the hyphen --
    11501150that's OK. Django quotes column and table names behind the scenes.
    11511151
     1152``sql_generation_required``
     1153---------------------------
     1154
     1155**New in Django development version**
     1156
     1157Marks this model as requiring SQL operations when calling ``manage.py``::
     1158
     1159    sql_generation_required = True
     1160
     1161If this isn't given, Django will use ``sql_generation_required = True``
     1162what means that the operations like ``manage.py sqlreset``, ``manage.py syncdb``
     1163and others will regenerate the appropriate table when needed.
     1164If the option is set to False, the appropriate table will not be touched with
     1165any SQL operations.
     1166
     1167This is useful for databases where some DB tables are controlled by Django models,
     1168but some DB tables and views are created via raw SQL and should not be affected
     1169by any ``manage.py`` actions.
     1170Note that if the initial SQL data is provided (see `Providing initial SQL
     1171data`_ below), it still will be present in the output of
     1172``sqlall``/``sqlcustom`` commands.
     1173
    11521174``db_tablespace``
    11531175-----------------
    11541176
Back to Top