diff options
Diffstat (limited to 'webapp/django/core/management/sql.py')
-rw-r--r-- | webapp/django/core/management/sql.py | 205 |
1 files changed, 205 insertions, 0 deletions
diff --git a/webapp/django/core/management/sql.py b/webapp/django/core/management/sql.py new file mode 100644 index 0000000000..4874a49dde --- /dev/null +++ b/webapp/django/core/management/sql.py @@ -0,0 +1,205 @@ +from django.core.management.base import CommandError +import os +import re + +try: + set +except NameError: + from sets import Set as set # Python 2.3 fallback + +def sql_create(app, style): + "Returns a list of the CREATE TABLE SQL statements for the given app." + from django.db import connection, models + from django.conf import settings + + if settings.DATABASE_ENGINE == 'dummy': + # This must be the "dummy" database backend, which means the user + # hasn't set DATABASE_ENGINE. + raise CommandError("Django doesn't know which syntax to use for your SQL statements,\n" + + "because you haven't specified the DATABASE_ENGINE setting.\n" + + "Edit your settings file and change DATABASE_ENGINE to something like 'postgresql' or 'mysql'.") + + # Get installed models, so we generate REFERENCES right. + # We trim models from the current app so that the sqlreset command does not + # generate invalid SQL (leaving models out of known_models is harmless, so + # we can be conservative). + app_models = models.get_models(app) + final_output = [] + tables = connection.introspection.table_names() + known_models = set([model for model in connection.introspection.installed_models(tables) if model not in app_models]) + pending_references = {} + + for model in app_models: + output, references = connection.creation.sql_create_model(model, style, known_models) + final_output.extend(output) + for refto, refs in references.items(): + pending_references.setdefault(refto, []).extend(refs) + if refto in known_models: + final_output.extend(connection.creation.sql_for_pending_references(refto, style, pending_references)) + final_output.extend(connection.creation.sql_for_pending_references(model, style, pending_references)) + # Keep track of the fact that we've created the table for this model. + known_models.add(model) + + # Create the many-to-many join tables. + for model in app_models: + final_output.extend(connection.creation.sql_for_many_to_many(model, style)) + + # Handle references to tables that are from other apps + # but don't exist physically. + not_installed_models = set(pending_references.keys()) + if not_installed_models: + alter_sql = [] + for model in not_installed_models: + alter_sql.extend(['-- ' + sql for sql in + connection.creation.sql_for_pending_references(model, style, pending_references)]) + if alter_sql: + final_output.append('-- The following references should be added but depend on non-existent tables:') + final_output.extend(alter_sql) + + return final_output + +def sql_delete(app, style): + "Returns a list of the DROP TABLE SQL statements for the given app." + from django.db import connection, models + from django.db.backends.util import truncate_name + from django.contrib.contenttypes import generic + + # This should work even if a connection isn't available + try: + cursor = connection.cursor() + except: + cursor = None + + # Figure out which tables already exist + if cursor: + table_names = connection.introspection.get_table_list(cursor) + else: + table_names = [] + + output = [] + + # Output DROP TABLE statements for standard application tables. + to_delete = set() + + references_to_delete = {} + app_models = models.get_models(app) + for model in app_models: + if cursor and connection.introspection.table_name_converter(model._meta.db_table) in table_names: + # The table exists, so it needs to be dropped + opts = model._meta + for f in opts.local_fields: + if f.rel and f.rel.to not in to_delete: + references_to_delete.setdefault(f.rel.to, []).append( (model, f) ) + + to_delete.add(model) + + for model in app_models: + if connection.introspection.table_name_converter(model._meta.db_table) in table_names: + output.extend(connection.creation.sql_destroy_model(model, references_to_delete, style)) + + # Output DROP TABLE statements for many-to-many tables. + for model in app_models: + opts = model._meta + for f in opts.local_many_to_many: + if cursor and connection.introspection.table_name_converter(f.m2m_db_table()) in table_names: + output.extend(connection.creation.sql_destroy_many_to_many(model, f, style)) + + # Close database connection explicitly, in case this output is being piped + # directly into a database client, to avoid locking issues. + if cursor: + cursor.close() + connection.close() + + return output[::-1] # Reverse it, to deal with table dependencies. + +def sql_reset(app, style): + "Returns a list of the DROP TABLE SQL, then the CREATE TABLE SQL, for the given module." + return sql_delete(app, style) + sql_all(app, style) + +def sql_flush(style, only_django=False): + """ + Returns a list of the SQL statements used to flush the database. + + If only_django is True, then only table names that have associated Django + models and are in INSTALLED_APPS will be included. + """ + from django.db import connection + if only_django: + tables = connection.introspection.django_table_names() + else: + tables = connection.introspection.table_names() + statements = connection.ops.sql_flush(style, tables, connection.introspection.sequence_list()) + return statements + +def sql_custom(app, style): + "Returns a list of the custom table modifying SQL statements for the given app." + from django.db.models import get_models + output = [] + + app_models = get_models(app) + app_dir = os.path.normpath(os.path.join(os.path.dirname(app.__file__), 'sql')) + + for model in app_models: + output.extend(custom_sql_for_model(model, style)) + + return output + +def sql_indexes(app, style): + "Returns a list of the CREATE INDEX SQL statements for all models in the given app." + from django.db import connection, models + output = [] + for model in models.get_models(app): + output.extend(connection.creation.sql_indexes_for_model(model, style)) + return output + +def sql_all(app, style): + "Returns a list of CREATE TABLE SQL, initial-data inserts, and CREATE INDEX SQL for the given module." + return sql_create(app, style) + sql_custom(app, style) + sql_indexes(app, style) + +def custom_sql_for_model(model, style): + from django.db import models + from django.conf import settings + + opts = model._meta + app_dir = os.path.normpath(os.path.join(os.path.dirname(models.get_app(model._meta.app_label).__file__), 'sql')) + output = [] + + # Post-creation SQL should come before any initial SQL data is loaded. + # However, this should not be done for fields that are part of a a parent + # model (via model inheritance). + nm = opts.init_name_map() + post_sql_fields = [f for f in opts.local_fields if hasattr(f, 'post_create_sql')] + for f in post_sql_fields: + output.extend(f.post_create_sql(style, model._meta.db_table)) + + # Some backends can't execute more than one SQL statement at a time, + # so split into separate statements. + statements = re.compile(r";[ \t]*$", re.M) + + # Find custom SQL, if it's available. + sql_files = [os.path.join(app_dir, "%s.%s.sql" % (opts.object_name.lower(), settings.DATABASE_ENGINE)), + os.path.join(app_dir, "%s.sql" % opts.object_name.lower())] + for sql_file in sql_files: + if os.path.exists(sql_file): + fp = open(sql_file, 'U') + for statement in statements.split(fp.read().decode(settings.FILE_CHARSET)): + # Remove any comments from the file + statement = re.sub(ur"--.*([\n\Z]|$)", "", statement) + if statement.strip(): + output.append(statement + u";") + fp.close() + + return output + + +def emit_post_sync_signal(created_models, verbosity, interactive): + from django.db import models + from django.dispatch import dispatcher + # Emit the post_sync signal for every application. + for app in models.get_apps(): + app_name = app.__name__.split('.')[-2] + if verbosity >= 2: + print "Running post-sync handlers for application", app_name + models.signals.post_syncdb.send(sender=app, app=app, + created_models=created_models, verbosity=verbosity, + interactive=interactive) |