web/lib/django/core/management/sql.py
changeset 29 cc9b7e14412b
parent 0 0d40e90630ef
equal deleted inserted replaced
28:b758351d191f 29:cc9b7e14412b
     1 from django.core.management.base import CommandError
       
     2 import os
     1 import os
     3 import re
     2 import re
     4 
     3 
     5 try:
     4 from django.conf import settings
     6     set
     5 from django.contrib.contenttypes import generic
     7 except NameError:
     6 from django.core.management.base import CommandError
     8     from sets import Set as set   # Python 2.3 fallback
     7 from django.dispatch import dispatcher
       
     8 from django.db import models
       
     9 from django.db.models import get_models
       
    10 from django.db.backends.util import truncate_name
     9 
    11 
    10 def sql_create(app, style):
    12 def sql_create(app, style, connection):
    11     "Returns a list of the CREATE TABLE SQL statements for the given app."
    13     "Returns a list of the CREATE TABLE SQL statements for the given app."
    12     from django.db import connection, models
       
    13     from django.conf import settings
       
    14 
    14 
    15     if settings.DATABASE_ENGINE == 'dummy':
    15     if connection.settings_dict['ENGINE'] == 'django.db.backends.dummy':
    16         # This must be the "dummy" database backend, which means the user
    16         # This must be the "dummy" database backend, which means the user
    17         # hasn't set DATABASE_ENGINE.
    17         # hasn't set ENGINE for the databse.
    18         raise CommandError("Django doesn't know which syntax to use for your SQL statements,\n" +
    18         raise CommandError("Django doesn't know which syntax to use for your SQL statements,\n" +
    19             "because you haven't specified the DATABASE_ENGINE setting.\n" +
    19             "because you haven't specified the ENGINE setting for the database.\n" +
    20             "Edit your settings file and change DATABASE_ENGINE to something like 'postgresql' or 'mysql'.")
    20             "Edit your settings file and change DATBASES['default']['ENGINE'] to something like\n" +
       
    21             "'django.db.backends.postgresql' or 'django.db.backends.mysql'")
    21 
    22 
    22     # Get installed models, so we generate REFERENCES right.
    23     # Get installed models, so we generate REFERENCES right.
    23     # We trim models from the current app so that the sqlreset command does not
    24     # We trim models from the current app so that the sqlreset command does not
    24     # generate invalid SQL (leaving models out of known_models is harmless, so
    25     # generate invalid SQL (leaving models out of known_models is harmless, so
    25     # we can be conservative).
    26     # we can be conservative).
    26     app_models = models.get_models(app)
    27     app_models = models.get_models(app, include_auto_created=True)
    27     final_output = []
    28     final_output = []
    28     tables = connection.introspection.table_names()
    29     tables = connection.introspection.table_names()
    29     known_models = set([model for model in connection.introspection.installed_models(tables) if model not in app_models])
    30     known_models = set([model for model in connection.introspection.installed_models(tables) if model not in app_models])
    30     pending_references = {}
    31     pending_references = {}
    31 
    32 
    38                 final_output.extend(connection.creation.sql_for_pending_references(refto, style, pending_references))
    39                 final_output.extend(connection.creation.sql_for_pending_references(refto, style, pending_references))
    39         final_output.extend(connection.creation.sql_for_pending_references(model, style, pending_references))
    40         final_output.extend(connection.creation.sql_for_pending_references(model, style, pending_references))
    40         # Keep track of the fact that we've created the table for this model.
    41         # Keep track of the fact that we've created the table for this model.
    41         known_models.add(model)
    42         known_models.add(model)
    42 
    43 
    43     # Create the many-to-many join tables.
       
    44     for model in app_models:
       
    45         final_output.extend(connection.creation.sql_for_many_to_many(model, style))
       
    46 
       
    47     # Handle references to tables that are from other apps
    44     # Handle references to tables that are from other apps
    48     # but don't exist physically.
    45     # but don't exist physically.
    49     not_installed_models = set(pending_references.keys())
    46     not_installed_models = set(pending_references.keys())
    50     if not_installed_models:
    47     if not_installed_models:
    51         alter_sql = []
    48         alter_sql = []
    56             final_output.append('-- The following references should be added but depend on non-existent tables:')
    53             final_output.append('-- The following references should be added but depend on non-existent tables:')
    57             final_output.extend(alter_sql)
    54             final_output.extend(alter_sql)
    58 
    55 
    59     return final_output
    56     return final_output
    60 
    57 
    61 def sql_delete(app, style):
    58 def sql_delete(app, style, connection):
    62     "Returns a list of the DROP TABLE SQL statements for the given app."
    59     "Returns a list of the DROP TABLE SQL statements for the given app."
    63     from django.db import connection, models
       
    64     from django.db.backends.util import truncate_name
       
    65     from django.contrib.contenttypes import generic
       
    66 
    60 
    67     # This should work even if a connection isn't available
    61     # This should work even if a connection isn't available
    68     try:
    62     try:
    69         cursor = connection.cursor()
    63         cursor = connection.cursor()
    70     except:
    64     except:
    80 
    74 
    81     # Output DROP TABLE statements for standard application tables.
    75     # Output DROP TABLE statements for standard application tables.
    82     to_delete = set()
    76     to_delete = set()
    83 
    77 
    84     references_to_delete = {}
    78     references_to_delete = {}
    85     app_models = models.get_models(app)
    79     app_models = models.get_models(app, include_auto_created=True)
    86     for model in app_models:
    80     for model in app_models:
    87         if cursor and connection.introspection.table_name_converter(model._meta.db_table) in table_names:
    81         if cursor and connection.introspection.table_name_converter(model._meta.db_table) in table_names:
    88             # The table exists, so it needs to be dropped
    82             # The table exists, so it needs to be dropped
    89             opts = model._meta
    83             opts = model._meta
    90             for f in opts.local_fields:
    84             for f in opts.local_fields:
    95 
    89 
    96     for model in app_models:
    90     for model in app_models:
    97         if connection.introspection.table_name_converter(model._meta.db_table) in table_names:
    91         if connection.introspection.table_name_converter(model._meta.db_table) in table_names:
    98             output.extend(connection.creation.sql_destroy_model(model, references_to_delete, style))
    92             output.extend(connection.creation.sql_destroy_model(model, references_to_delete, style))
    99 
    93 
   100     # Output DROP TABLE statements for many-to-many tables.
       
   101     for model in app_models:
       
   102         opts = model._meta
       
   103         for f in opts.local_many_to_many:
       
   104             if cursor and connection.introspection.table_name_converter(f.m2m_db_table()) in table_names:
       
   105                 output.extend(connection.creation.sql_destroy_many_to_many(model, f, style))
       
   106 
       
   107     # Close database connection explicitly, in case this output is being piped
    94     # Close database connection explicitly, in case this output is being piped
   108     # directly into a database client, to avoid locking issues.
    95     # directly into a database client, to avoid locking issues.
   109     if cursor:
    96     if cursor:
   110         cursor.close()
    97         cursor.close()
   111         connection.close()
    98         connection.close()
   112 
    99 
   113     return output[::-1] # Reverse it, to deal with table dependencies.
   100     return output[::-1] # Reverse it, to deal with table dependencies.
   114 
   101 
   115 def sql_reset(app, style):
   102 def sql_reset(app, style, connection):
   116     "Returns a list of the DROP TABLE SQL, then the CREATE TABLE SQL, for the given module."
   103     "Returns a list of the DROP TABLE SQL, then the CREATE TABLE SQL, for the given module."
   117     return sql_delete(app, style) + sql_all(app, style)
   104     return sql_delete(app, style, connection) + sql_all(app, style, connection)
   118 
   105 
   119 def sql_flush(style, only_django=False):
   106 def sql_flush(style, connection, only_django=False):
   120     """
   107     """
   121     Returns a list of the SQL statements used to flush the database.
   108     Returns a list of the SQL statements used to flush the database.
   122 
   109 
   123     If only_django is True, then only table names that have associated Django
   110     If only_django is True, then only table names that have associated Django
   124     models and are in INSTALLED_APPS will be included.
   111     models and are in INSTALLED_APPS will be included.
   125     """
   112     """
   126     from django.db import connection
       
   127     if only_django:
   113     if only_django:
   128         tables = connection.introspection.django_table_names(only_existing=True)
   114         tables = connection.introspection.django_table_names(only_existing=True)
   129     else:
   115     else:
   130         tables = connection.introspection.table_names()
   116         tables = connection.introspection.table_names()
   131     statements = connection.ops.sql_flush(style, tables, connection.introspection.sequence_list())
   117     statements = connection.ops.sql_flush(style, tables, connection.introspection.sequence_list())
   132     return statements
   118     return statements
   133 
   119 
   134 def sql_custom(app, style):
   120 def sql_custom(app, style, connection):
   135     "Returns a list of the custom table modifying SQL statements for the given app."
   121     "Returns a list of the custom table modifying SQL statements for the given app."
   136     from django.db.models import get_models
       
   137     output = []
   122     output = []
   138 
   123 
   139     app_models = get_models(app)
   124     app_models = get_models(app)
   140     app_dir = os.path.normpath(os.path.join(os.path.dirname(app.__file__), 'sql'))
   125     app_dir = os.path.normpath(os.path.join(os.path.dirname(app.__file__), 'sql'))
   141 
   126 
   142     for model in app_models:
   127     for model in app_models:
   143         output.extend(custom_sql_for_model(model, style))
   128         output.extend(custom_sql_for_model(model, style, connection))
   144 
   129 
   145     return output
   130     return output
   146 
   131 
   147 def sql_indexes(app, style):
   132 def sql_indexes(app, style, connection):
   148     "Returns a list of the CREATE INDEX SQL statements for all models in the given app."
   133     "Returns a list of the CREATE INDEX SQL statements for all models in the given app."
   149     from django.db import connection, models
       
   150     output = []
   134     output = []
   151     for model in models.get_models(app):
   135     for model in models.get_models(app):
   152         output.extend(connection.creation.sql_indexes_for_model(model, style))
   136         output.extend(connection.creation.sql_indexes_for_model(model, style))
   153     return output
   137     return output
   154 
   138 
   155 def sql_all(app, style):
   139 def sql_all(app, style, connection):
   156     "Returns a list of CREATE TABLE SQL, initial-data inserts, and CREATE INDEX SQL for the given module."
   140     "Returns a list of CREATE TABLE SQL, initial-data inserts, and CREATE INDEX SQL for the given module."
   157     return sql_create(app, style) + sql_custom(app, style) + sql_indexes(app, style)
   141     return sql_create(app, style, connection) + sql_custom(app, style, connection) + sql_indexes(app, style, connection)
   158 
   142 
   159 def custom_sql_for_model(model, style):
   143 def custom_sql_for_model(model, style, connection):
   160     from django.db import models
       
   161     from django.conf import settings
       
   162 
       
   163     opts = model._meta
   144     opts = model._meta
   164     app_dir = os.path.normpath(os.path.join(os.path.dirname(models.get_app(model._meta.app_label).__file__), 'sql'))
   145     app_dir = os.path.normpath(os.path.join(os.path.dirname(models.get_app(model._meta.app_label).__file__), 'sql'))
   165     output = []
   146     output = []
   166 
   147 
   167     # Post-creation SQL should come before any initial SQL data is loaded.
   148     # Post-creation SQL should come before any initial SQL data is loaded.
   168     # However, this should not be done for fields that are part of a a parent
   149     # However, this should not be done for models that are unmanaged or
   169     # model (via model inheritance).
   150     # for fields that are part of a parent model (via model inheritance).
   170     nm = opts.init_name_map()
   151     if opts.managed:
   171     post_sql_fields = [f for f in opts.local_fields if hasattr(f, 'post_create_sql')]
   152         post_sql_fields = [f for f in opts.local_fields if hasattr(f, 'post_create_sql')]
   172     for f in post_sql_fields:
   153         for f in post_sql_fields:
   173         output.extend(f.post_create_sql(style, model._meta.db_table))
   154             output.extend(f.post_create_sql(style, model._meta.db_table))
   174 
   155 
   175     # Some backends can't execute more than one SQL statement at a time,
   156     # Some backends can't execute more than one SQL statement at a time,
   176     # so split into separate statements.
   157     # so split into separate statements.
   177     statements = re.compile(r";[ \t]*$", re.M)
   158     statements = re.compile(r";[ \t]*$", re.M)
   178 
   159 
   179     # Find custom SQL, if it's available.
   160     # Find custom SQL, if it's available.
   180     sql_files = [os.path.join(app_dir, "%s.%s.sql" % (opts.object_name.lower(), settings.DATABASE_ENGINE)),
   161     backend_name = connection.settings_dict['ENGINE'].split('.')[-1]
       
   162     sql_files = [os.path.join(app_dir, "%s.%s.sql" % (opts.object_name.lower(), backend_name)),
   181                  os.path.join(app_dir, "%s.sql" % opts.object_name.lower())]
   163                  os.path.join(app_dir, "%s.sql" % opts.object_name.lower())]
   182     for sql_file in sql_files:
   164     for sql_file in sql_files:
   183         if os.path.exists(sql_file):
   165         if os.path.exists(sql_file):
   184             fp = open(sql_file, 'U')
   166             fp = open(sql_file, 'U')
   185             for statement in statements.split(fp.read().decode(settings.FILE_CHARSET)):
   167             for statement in statements.split(fp.read().decode(settings.FILE_CHARSET)):
   190             fp.close()
   172             fp.close()
   191 
   173 
   192     return output
   174     return output
   193 
   175 
   194 
   176 
   195 def emit_post_sync_signal(created_models, verbosity, interactive):
   177 def emit_post_sync_signal(created_models, verbosity, interactive, db):
   196     from django.db import models
       
   197     from django.dispatch import dispatcher
       
   198     # Emit the post_sync signal for every application.
   178     # Emit the post_sync signal for every application.
   199     for app in models.get_apps():
   179     for app in models.get_apps():
   200         app_name = app.__name__.split('.')[-2]
   180         app_name = app.__name__.split('.')[-2]
   201         if verbosity >= 2:
   181         if verbosity >= 2:
   202             print "Running post-sync handlers for application", app_name
   182             print "Running post-sync handlers for application", app_name
   203         models.signals.post_syncdb.send(sender=app, app=app,
   183         models.signals.post_syncdb.send(sender=app, app=app,
   204             created_models=created_models, verbosity=verbosity,
   184             created_models=created_models, verbosity=verbosity,
   205             interactive=interactive)
   185             interactive=interactive, db=db)