Skip to content

Commit

Permalink
Fixed #22749: Making SQL management commands migration aware.
Browse files Browse the repository at this point in the history
  • Loading branch information
valberg authored and andrewgodwin committed Jun 8, 2014
1 parent 6fd455a commit f70f669
Show file tree
Hide file tree
Showing 7 changed files with 72 additions and 3 deletions.
2 changes: 2 additions & 0 deletions django/core/management/commands/sqlsequencereset.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
from optparse import make_option

from django.core.management.base import AppCommand
from django.core.management.sql import check_for_migrations
from django.db import connections, DEFAULT_DB_ALIAS


Expand All @@ -22,6 +23,7 @@ def handle_app_config(self, app_config, **options):
if app_config.models_module is None:
return
connection = connections[options.get('database')]
check_for_migrations(app_config, connection)
models = app_config.get_models(include_auto_created=True)
statements = connection.ops.sequence_reset_sql(self.style, models)
return '\n'.join(statements)
28 changes: 26 additions & 2 deletions django/core/management/sql.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,19 @@
from django.utils.deprecation import RemovedInDjango19Warning


def check_for_migrations(app_config, connection):
# Inner import, else tests imports it too early as it needs settings
from django.db.migrations.loader import MigrationLoader
loader = MigrationLoader(connection)
if app_config.label in loader.migrated_apps:
raise CommandError("App '%s' has migrations. Only the sqlmigrate and sqlflush commands can be used when an app has migrations." % app_config.label)


def sql_create(app_config, style, connection):
"Returns a list of the CREATE TABLE SQL statements for the given app."

check_for_migrations(app_config, connection)

if connection.settings_dict['ENGINE'] == 'django.db.backends.dummy':
# This must be the "dummy" database backend, which means the user
# hasn't set ENGINE for the database.
Expand Down Expand Up @@ -58,9 +68,11 @@ def sql_create(app_config, style, connection):
return final_output


def sql_delete(app_config, style, connection):
def sql_delete(app_config, style, connection, close_connection=True):
"Returns a list of the DROP TABLE SQL statements for the given app."

check_for_migrations(app_config, connection)

# This should work even if a connection isn't available
try:
cursor = connection.cursor()
Expand Down Expand Up @@ -97,7 +109,7 @@ def sql_delete(app_config, style, connection):
finally:
# Close database connection explicitly, in case this output is being piped
# directly into a database client, to avoid locking issues.
if cursor:
if cursor and close_connection:
cursor.close()
connection.close()

Expand All @@ -122,6 +134,9 @@ def sql_flush(style, connection, only_django=False, reset_sequences=True, allow_

def sql_custom(app_config, style, connection):
"Returns a list of the custom table modifying SQL statements for the given app."

check_for_migrations(app_config, connection)

output = []

app_models = router.get_migratable_models(app_config, connection.alias)
Expand All @@ -134,6 +149,9 @@ def sql_custom(app_config, style, connection):

def sql_indexes(app_config, style, connection):
"Returns a list of the CREATE INDEX SQL statements for all models in the given app."

check_for_migrations(app_config, connection)

output = []
for model in router.get_migratable_models(app_config, connection.alias, include_auto_created=True):
output.extend(connection.creation.sql_indexes_for_model(model, style))
Expand All @@ -142,13 +160,19 @@ def sql_indexes(app_config, style, connection):

def sql_destroy_indexes(app_config, style, connection):
"Returns a list of the DROP INDEX SQL statements for all models in the given app."

check_for_migrations(app_config, connection)

output = []
for model in router.get_migratable_models(app_config, connection.alias, include_auto_created=True):
output.extend(connection.creation.sql_destroy_indexes_for_model(model, style))
return output


def sql_all(app_config, style, connection):

check_for_migrations(app_config, connection)

"Returns a list of CREATE TABLE SQL, initial-data inserts, and CREATE INDEX SQL for the given module."
return sql_create(app_config, style, connection) + sql_custom(app_config, style, connection) + sql_indexes(app_config, style, connection)

Expand Down
Empty file.
33 changes: 33 additions & 0 deletions tests/commands_sql_migrations/migrations/0001_initial.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals

from django.db import models, migrations


class Migration(migrations.Migration):

dependencies = [
]

operations = [
migrations.CreateModel(
name='Comment',
fields=[
('id', models.AutoField(verbose_name='ID', primary_key=True, serialize=False, auto_created=True)),
],
options={
},
bases=(models.Model,),
),
migrations.CreateModel(
name='Book',
fields=[
('id', models.AutoField(verbose_name='ID', primary_key=True, serialize=False, auto_created=True)),
('title', models.CharField(db_index=True, max_length=100)),
('comments', models.ManyToManyField(to='commands_sql_migrations.Comment')),
],
options={
},
bases=(models.Model,),
),
]
Empty file.
10 changes: 10 additions & 0 deletions tests/commands_sql_migrations/models.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
from django.db import models


class Comment(models.Model):
pass


class Book(models.Model):
title = models.CharField(max_length=100, db_index=True)
comments = models.ManyToManyField(Comment)
2 changes: 1 addition & 1 deletion tests/commands_sql_migrations/tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ def test_sql_create(self):
def test_sql_delete(self):
app_config = apps.get_app_config('commands_sql_migrations')
with self.assertRaises(CommandError):
sql_delete(app_config, no_style(), connections[DEFAULT_DB_ALIAS])
sql_delete(app_config, no_style(), connections[DEFAULT_DB_ALIAS], close_connection=False)

def test_sql_indexes(self):
app_config = apps.get_app_config('commands_sql_migrations')
Expand Down

3 comments on commit f70f669

@ppawlak
Copy link

@ppawlak ppawlak commented on f70f669 Aug 4, 2014

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there a technical reason why apps with migrations can't use the sql management command ? It can still be useful to get 'CREATE TABLE' statements for debugging models.

@andrewgodwin
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Because they won't be created with the same backend (unmigrated apps use Creation, migrated uses SchemaEditor) so the SQL won't match. The sqlmigrate command will do the same function for migrated apps (the sql command will be deprecated next release)

@ppawlak
Copy link

@ppawlak ppawlak commented on f70f669 Aug 4, 2014

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the explanation Andrew :)

Please sign in to comment.