web/lib/django/core/management/sql.py
changeset 0 0d40e90630ef
child 29 cc9b7e14412b
equal deleted inserted replaced
-1:000000000000 0:0d40e90630ef
       
     1 from django.core.management.base import CommandError
       
     2 import os
       
     3 import re
       
     4 
       
     5 try:
       
     6     set
       
     7 except NameError:
       
     8     from sets import Set as set   # Python 2.3 fallback
       
     9 
       
    10 def sql_create(app, style):
       
    11     "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 
       
    15     if settings.DATABASE_ENGINE == 'dummy':
       
    16         # This must be the "dummy" database backend, which means the user
       
    17         # hasn't set DATABASE_ENGINE.
       
    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" +
       
    20             "Edit your settings file and change DATABASE_ENGINE to something like 'postgresql' or 'mysql'.")
       
    21 
       
    22     # Get installed models, so we generate REFERENCES right.
       
    23     # 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     # we can be conservative).
       
    26     app_models = models.get_models(app)
       
    27     final_output = []
       
    28     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     pending_references = {}
       
    31 
       
    32     for model in app_models:
       
    33         output, references = connection.creation.sql_create_model(model, style, known_models)
       
    34         final_output.extend(output)
       
    35         for refto, refs in references.items():
       
    36             pending_references.setdefault(refto, []).extend(refs)
       
    37             if refto in known_models:
       
    38                 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         # Keep track of the fact that we've created the table for this model.
       
    41         known_models.add(model)
       
    42 
       
    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
       
    48     # but don't exist physically.
       
    49     not_installed_models = set(pending_references.keys())
       
    50     if not_installed_models:
       
    51         alter_sql = []
       
    52         for model in not_installed_models:
       
    53             alter_sql.extend(['-- ' + sql for sql in
       
    54                 connection.creation.sql_for_pending_references(model, style, pending_references)])
       
    55         if alter_sql:
       
    56             final_output.append('-- The following references should be added but depend on non-existent tables:')
       
    57             final_output.extend(alter_sql)
       
    58 
       
    59     return final_output
       
    60 
       
    61 def sql_delete(app, style):
       
    62     "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 
       
    67     # This should work even if a connection isn't available
       
    68     try:
       
    69         cursor = connection.cursor()
       
    70     except:
       
    71         cursor = None
       
    72 
       
    73     # Figure out which tables already exist
       
    74     if cursor:
       
    75         table_names = connection.introspection.get_table_list(cursor)
       
    76     else:
       
    77         table_names = []
       
    78 
       
    79     output = []
       
    80 
       
    81     # Output DROP TABLE statements for standard application tables.
       
    82     to_delete = set()
       
    83 
       
    84     references_to_delete = {}
       
    85     app_models = models.get_models(app)
       
    86     for model in app_models:
       
    87         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
       
    89             opts = model._meta
       
    90             for f in opts.local_fields:
       
    91                 if f.rel and f.rel.to not in to_delete:
       
    92                     references_to_delete.setdefault(f.rel.to, []).append( (model, f) )
       
    93 
       
    94             to_delete.add(model)
       
    95 
       
    96     for model in app_models:
       
    97         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))
       
    99 
       
   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
       
   108     # directly into a database client, to avoid locking issues.
       
   109     if cursor:
       
   110         cursor.close()
       
   111         connection.close()
       
   112 
       
   113     return output[::-1] # Reverse it, to deal with table dependencies.
       
   114 
       
   115 def sql_reset(app, style):
       
   116     "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)
       
   118 
       
   119 def sql_flush(style, only_django=False):
       
   120     """
       
   121     Returns a list of the SQL statements used to flush the database.
       
   122 
       
   123     If only_django is True, then only table names that have associated Django
       
   124     models and are in INSTALLED_APPS will be included.
       
   125     """
       
   126     from django.db import connection
       
   127     if only_django:
       
   128         tables = connection.introspection.django_table_names(only_existing=True)
       
   129     else:
       
   130         tables = connection.introspection.table_names()
       
   131     statements = connection.ops.sql_flush(style, tables, connection.introspection.sequence_list())
       
   132     return statements
       
   133 
       
   134 def sql_custom(app, style):
       
   135     "Returns a list of the custom table modifying SQL statements for the given app."
       
   136     from django.db.models import get_models
       
   137     output = []
       
   138 
       
   139     app_models = get_models(app)
       
   140     app_dir = os.path.normpath(os.path.join(os.path.dirname(app.__file__), 'sql'))
       
   141 
       
   142     for model in app_models:
       
   143         output.extend(custom_sql_for_model(model, style))
       
   144 
       
   145     return output
       
   146 
       
   147 def sql_indexes(app, style):
       
   148     "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 = []
       
   151     for model in models.get_models(app):
       
   152         output.extend(connection.creation.sql_indexes_for_model(model, style))
       
   153     return output
       
   154 
       
   155 def sql_all(app, style):
       
   156     "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)
       
   158 
       
   159 def custom_sql_for_model(model, style):
       
   160     from django.db import models
       
   161     from django.conf import settings
       
   162 
       
   163     opts = model._meta
       
   164     app_dir = os.path.normpath(os.path.join(os.path.dirname(models.get_app(model._meta.app_label).__file__), 'sql'))
       
   165     output = []
       
   166 
       
   167     # 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
       
   169     # model (via model inheritance).
       
   170     nm = opts.init_name_map()
       
   171     post_sql_fields = [f for f in opts.local_fields if hasattr(f, 'post_create_sql')]
       
   172     for f in post_sql_fields:
       
   173         output.extend(f.post_create_sql(style, model._meta.db_table))
       
   174 
       
   175     # Some backends can't execute more than one SQL statement at a time,
       
   176     # so split into separate statements.
       
   177     statements = re.compile(r";[ \t]*$", re.M)
       
   178 
       
   179     # 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)),
       
   181                  os.path.join(app_dir, "%s.sql" % opts.object_name.lower())]
       
   182     for sql_file in sql_files:
       
   183         if os.path.exists(sql_file):
       
   184             fp = open(sql_file, 'U')
       
   185             for statement in statements.split(fp.read().decode(settings.FILE_CHARSET)):
       
   186                 # Remove any comments from the file
       
   187                 statement = re.sub(ur"--.*([\n\Z]|$)", "", statement)
       
   188                 if statement.strip():
       
   189                     output.append(statement + u";")
       
   190             fp.close()
       
   191 
       
   192     return output
       
   193 
       
   194 
       
   195 def emit_post_sync_signal(created_models, verbosity, interactive):
       
   196     from django.db import models
       
   197     from django.dispatch import dispatcher
       
   198     # Emit the post_sync signal for every application.
       
   199     for app in models.get_apps():
       
   200         app_name = app.__name__.split('.')[-2]
       
   201         if verbosity >= 2:
       
   202             print "Running post-sync handlers for application", app_name
       
   203         models.signals.post_syncdb.send(sender=app, app=app,
       
   204             created_models=created_models, verbosity=verbosity,
       
   205             interactive=interactive)