diff --git a/django/core/management/commands/sqlmigrate.py b/django/core/management/commands/sqlmigrate.py index e2e421d526..2358cd5259 100644 --- a/django/core/management/commands/sqlmigrate.py +++ b/django/core/management/commands/sqlmigrate.py @@ -10,6 +10,8 @@ from django.db.migrations.loader import AmbiguityError class Command(BaseCommand): help = "Prints the SQL statements for the named migration." + output_transaction = True + def add_arguments(self, parser): parser.add_argument('app_label', help='App label of the application containing the migration.') @@ -21,6 +23,13 @@ class Command(BaseCommand): parser.add_argument('--backwards', action='store_true', dest='backwards', default=False, help='Creates SQL to unapply the migration, rather than to apply it') + def execute(self, *args, **options): + # sqlmigrate doesn't support coloring its output but we need to force + # no_color=True so that the BEGIN/COMMIT statements added by + # output_transaction don't get colored either. + options['no_color'] = True + return super(Command, self).execute(*args, **options) + def handle(self, *args, **options): # Get the database we're operating from connection = connections[options['database']] @@ -46,5 +55,4 @@ class Command(BaseCommand): # for it plan = [(executor.loader.graph.nodes[targets[0]], options['backwards'])] sql_statements = executor.collect_sql(plan) - for statement in sql_statements: - self.stdout.write(statement) + return '\n'.join(sql_statements) diff --git a/docs/intro/tutorial01.txt b/docs/intro/tutorial01.txt index b99adbb7f6..ca6042f618 100644 --- a/docs/intro/tutorial01.txt +++ b/docs/intro/tutorial01.txt @@ -480,6 +480,7 @@ readability): .. code-block:: sql + BEGIN; CREATE TABLE polls_question ( "id" serial NOT NULL PRIMARY KEY, "question_text" varchar(200) NOT NULL, @@ -500,6 +501,7 @@ readability): FOREIGN KEY ("question_id") REFERENCES "polls_question" ("id") DEFERRABLE INITIALLY DEFERRED; + COMMIT; Note the following: diff --git a/docs/ref/contrib/gis/tutorial.txt b/docs/ref/contrib/gis/tutorial.txt index a53cc100d9..a8c68f3b8e 100644 --- a/docs/ref/contrib/gis/tutorial.txt +++ b/docs/ref/contrib/gis/tutorial.txt @@ -285,6 +285,7 @@ This command should produce the following output: .. code-block:: sql + BEGIN; CREATE TABLE "world_worldborder" ( "id" serial NOT NULL PRIMARY KEY, "name" varchar(50) NOT NULL, @@ -302,6 +303,7 @@ This command should produce the following output: ) ; CREATE INDEX "world_worldborder_mpoly_id" ON "world_worldborder" USING GIST ( "mpoly" ); + COMMIT; .. note:: diff --git a/docs/ref/django-admin.txt b/docs/ref/django-admin.txt index 85d0ab06fe..5188c10dde 100644 --- a/docs/ref/django-admin.txt +++ b/docs/ref/django-admin.txt @@ -1136,6 +1136,8 @@ Prints the SQL for the named migration. This requires an active database connection, which it will use to resolve constraint names; this means you must generate the SQL against a copy of the database you wish to later apply it on. +Note that ``sqlmigrate`` doesn't colorize its output. + The :djadminopt:`--database` option can be used to specify the database for which to generate the SQL. diff --git a/tests/migrations/test_commands.py b/tests/migrations/test_commands.py index 2090135ffc..8fe2a7e4a5 100644 --- a/tests/migrations/test_commands.py +++ b/tests/migrations/test_commands.py @@ -93,6 +93,13 @@ class MigrateTests(MigrationTestBase): """ Makes sure that sqlmigrate does something. """ + # Make sure the output is wrapped in a transaction + stdout = six.StringIO() + call_command("sqlmigrate", "migrations", "0001", stdout=stdout) + output = stdout.getvalue().lower() + self.assertIn("begin;", output) + self.assertIn("commit;", output) + # Test forwards. All the databases agree on CREATE TABLE, at least. stdout = six.StringIO() call_command("sqlmigrate", "migrations", "0001", stdout=stdout) diff --git a/tests/user_commands/management/commands/transaction.py b/tests/user_commands/management/commands/transaction.py new file mode 100644 index 0000000000..ca531b2b97 --- /dev/null +++ b/tests/user_commands/management/commands/transaction.py @@ -0,0 +1,10 @@ +from django.core.management.base import BaseCommand + + +class Command(BaseCommand): + help = "Say hello." + args = '' + output_transaction = True + + def handle(self, *args, **options): + return 'Hello!' diff --git a/tests/user_commands/tests.py b/tests/user_commands/tests.py index 19383e6af4..390e95060e 100644 --- a/tests/user_commands/tests.py +++ b/tests/user_commands/tests.py @@ -134,6 +134,11 @@ class CommandTests(SimpleTestCase): with self.assertRaises(CommandError): management.call_command('hal', stdout=out) + def test_output_transaction(self): + out = StringIO() + management.call_command('transaction', stdout=out, no_color=True) + self.assertEqual(out.getvalue(), 'BEGIN;\nHello!\n\nCOMMIT;\n') + class UtilsTests(SimpleTestCase):