diff --git a/django/core/management/commands/sqlmigrate.py b/django/core/management/commands/sqlmigrate.py index 5652d082ea..46b9ba9929 100644 --- a/django/core/management/commands/sqlmigrate.py +++ b/django/core/management/commands/sqlmigrate.py @@ -19,6 +19,14 @@ class Command(BaseCommand): ) help = "Prints the SQL statements for the named migration." + output_transaction = True + + 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): @@ -50,5 +58,4 @@ class Command(BaseCommand): # for it plan = [(executor.loader.graph.nodes[targets[0]], options.get("backwards", False))] 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 5ac9fbb12c..1641ed2e17 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 339c9a481e..327427e94c 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 03fbef028f..7d464d92d4 100644 --- a/docs/ref/django-admin.txt +++ b/docs/ref/django-admin.txt @@ -1127,6 +1127,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 c24a6e7a2d..1d405bec1c 100644 --- a/tests/user_commands/tests.py +++ b/tests/user_commands/tests.py @@ -74,6 +74,11 @@ class CommandTests(SimpleTestCase): if current_path is not None: os.environ['PATH'] = current_path + 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):