diff --git a/django/core/management.py b/django/core/management.py index 0c69c61f8e..a22384782c 100644 --- a/django/core/management.py +++ b/django/core/management.py @@ -200,42 +200,31 @@ def _get_many_to_many_sql_for_model(klass): def get_sql_delete(app): "Returns a list of the DROP TABLE SQL statements for the given app." - from django.db import backend, connection, models, transaction - + from django.db import backend, connection, models, get_introspection_module + introspection = get_introspection_module() + + # This should work even if a connecton isn't available try: cursor = connection.cursor() except: cursor = None - - # Determine whether the admin log table exists. It only exists if the - # person has installed the admin app. - try: - if cursor is not None: - # Check whether the table exists. - cursor.execute("SELECT 1 FROM %s LIMIT 1" % backend.quote_name('django_admin_log')) - except: - # The table doesn't exist, so it doesn't need to be dropped. - transaction.rollback_unless_managed() - admin_log_exists = False + + # Figure out which tables already exist + if cursor: + table_names = introspection.get_table_list(cursor) else: - admin_log_exists = True + table_names = [] output = [] - + # Output DROP TABLE statements for standard application tables. to_delete = set() references_to_delete = {} app_models = models.get_models(app) for klass in app_models: - try: - if cursor is not None: - # Check whether the table exists. - cursor.execute("SELECT 1 FROM %s LIMIT 1" % backend.quote_name(klass._meta.db_table)) - except: - # The table doesn't exist, so it doesn't need to be dropped. - transaction.rollback_unless_managed() - else: + if cursor and klass._meta.db_table in table_names: + # The table exists, so it needs to be dropped opts = klass._meta for f in opts.fields: if f.rel and f.rel.to not in to_delete: @@ -244,61 +233,57 @@ def get_sql_delete(app): to_delete.add(klass) for klass in app_models: - try: - if cursor is not None: - # Check whether the table exists. - cursor.execute("SELECT 1 FROM %s LIMIT 1" % backend.quote_name(klass._meta.db_table)) - except: - # The table doesn't exist, so it doesn't need to be dropped. - transaction.rollback_unless_managed() - else: - output.append("DROP TABLE %s;" % backend.quote_name(klass._meta.db_table)) - if backend.supports_constraints and references_to_delete.has_key(klass): - for rel_class, f in references_to_delete[klass]: - table = rel_class._meta.db_table - col = f.column - r_table = klass._meta.db_table - r_col = klass._meta.get_field(f.rel.field_name).column - output.append('ALTER TABLE %s DROP CONSTRAINT %s;' % \ - (backend.quote_name(table), - backend.quote_name("%s_referencing_%s_%s" % (col, r_table, r_col)))) - del references_to_delete[klass] + if cursor and klass._meta.db_table in table_names: + # Drop the able now + output.append("DROP TABLE %s;" % backend.quote_name(klass._meta.db_table)) + if backend.supports_constraints and references_to_delete.has_key(klass): + for rel_class, f in references_to_delete[klass]: + table = rel_class._meta.db_table + col = f.column + r_table = klass._meta.db_table + r_col = klass._meta.get_field(f.rel.field_name).column + output.append('ALTER TABLE %s DROP CONSTRAINT %s;' % \ + (backend.quote_name(table), + backend.quote_name("%s_referencing_%s_%s" % (col, r_table, r_col)))) + del references_to_delete[klass] # Output DROP TABLE statements for many-to-many tables. for klass in app_models: opts = klass._meta for f in opts.many_to_many: - try: - if cursor is not None: - cursor.execute("SELECT 1 FROM %s LIMIT 1" % backend.quote_name(f.m2m_db_table())) - except: - transaction.rollback_unless_managed() - else: + if cursor and f.m2m_db_table() in table_names: output.append("DROP TABLE %s;" % backend.quote_name(f.m2m_db_table())) app_label = app_models[0]._meta.app_label # Delete from django_package, auth_permission, django_content_type. - # TODO: fix this when permissions are activated again - # output.append("DELETE FROM %s WHERE %s = '%s';" % \ - # (backend.quote_name('auth_permission'), backend.quote_name('package'), app_label)) - output.append("DELETE FROM %s WHERE %s = '%s';" % \ - (backend.quote_name('django_content_type'), backend.quote_name('app_label'), app_label)) + if cursor and "django_content_type" in table_names: - # Delete from the admin log. - if cursor is not None: - cursor.execute("SELECT %s FROM %s WHERE %s = %%s" % \ - (backend.quote_name('id'), backend.quote_name('django_content_type'), - backend.quote_name('package')), [app_label]) - if admin_log_exists: - for row in cursor.fetchall(): + # Grab a list of affected content-types + cursor.execute("SELECT id FROM django_content_type WHERE app_label = %s", [app_label]) + affected_content_types = [r[0] for r in cursor.fetchall()] + + # Remember do this this business in reverse order since the returned + # values are reversed below + output.append("DELETE FROM %s WHERE %s = '%s';" % \ + (backend.quote_name('django_content_type'), backend.quote_name('app_label'), app_label)) + + if "auth_permission" in table_names: + for ctype_id in affected_content_types: output.append("DELETE FROM %s WHERE %s = %s;" % \ - (backend.quote_name('django_admin_log'), backend.quote_name('content_type_id'), row[0])) + (backend.quote_name("auth_permission"), backend.quote_name("content_type_id"), ctype_id)) + # Delete from the admin log. + if "django_admin_log" in table_names: + for ctype_id in affected_content_types: + output.append("DELETE FROM %s WHERE %s = %s;" % \ + (backend.quote_name("django_admin_log"), backend.quote_name("content_type_id"), ctype_id)) + # Close database connection explicitly, in case this output is being piped # directly into a database client, to avoid locking issues. - cursor.close() - connection.close() + if cursor: + cursor.close() + connection.close() return output[::-1] # Reverse it, to deal with table dependencies. get_sql_delete.help_doc = "Prints the DROP TABLE SQL statements for the given app name(s)." @@ -492,13 +477,7 @@ def reset(app): app_name = app.__name__.split('.')[-2] # First, try validating the models. - s = StringIO() - num_errors = get_validation_errors(s, app) - if num_errors: - sys.stderr.write("Error: %s couldn't be installed, because there were errors in your model:\n" % app_name) - s.seek(0) - sys.stderr.write(s.read()) - sys.exit(1) + _check_for_validation_errors(app) sql_list = get_sql_reset(app) confirm = raw_input("""