mirror of
https://github.com/django/django.git
synced 2025-08-04 19:08:28 +00:00
Fixed #25833 -- Added support for non-atomic migrations.
Added the Migration.atomic attribute which can be set to False for non-atomic migrations.
This commit is contained in:
parent
0edb8a146f
commit
f91a04621e
14 changed files with 181 additions and 16 deletions
|
@ -362,6 +362,19 @@ class MigrateTests(MigrationTestBase):
|
|||
# Cleanup by unmigrating everything
|
||||
call_command("migrate", "migrations", "zero", verbosity=0)
|
||||
|
||||
@override_settings(MIGRATION_MODULES={"migrations": "migrations.test_migrations_non_atomic"})
|
||||
def test_sqlmigrate_for_non_atomic_migration(self):
|
||||
"""
|
||||
Transaction wrappers aren't shown for non-atomic migrations.
|
||||
"""
|
||||
out = six.StringIO()
|
||||
call_command("sqlmigrate", "migrations", "0001", stdout=out)
|
||||
output = out.getvalue().lower()
|
||||
queries = [q.strip() for q in output.splitlines()]
|
||||
if connection.ops.start_transaction_sql():
|
||||
self.assertNotIn(connection.ops.start_transaction_sql().lower(), queries)
|
||||
self.assertNotIn(connection.ops.end_transaction_sql().lower(), queries)
|
||||
|
||||
@override_settings(
|
||||
INSTALLED_APPS=[
|
||||
"migrations.migrations_test_apps.migrated_app",
|
||||
|
|
|
@ -100,6 +100,33 @@ class ExecutorTests(MigrationTestBase):
|
|||
self.assertTableNotExists("migrations_author")
|
||||
self.assertTableNotExists("migrations_book")
|
||||
|
||||
@override_settings(MIGRATION_MODULES={"migrations": "migrations.test_migrations_non_atomic"})
|
||||
def test_non_atomic_migration(self):
|
||||
"""
|
||||
Applying a non-atomic migration works as expected.
|
||||
"""
|
||||
executor = MigrationExecutor(connection)
|
||||
with self.assertRaisesMessage(RuntimeError, "Abort migration"):
|
||||
executor.migrate([("migrations", "0001_initial")])
|
||||
self.assertTableExists("migrations_publisher")
|
||||
migrations_apps = executor.loader.project_state(("migrations", "0001_initial")).apps
|
||||
Publisher = migrations_apps.get_model("migrations", "Publisher")
|
||||
self.assertTrue(Publisher.objects.exists())
|
||||
self.assertTableNotExists("migrations_book")
|
||||
|
||||
@override_settings(MIGRATION_MODULES={"migrations": "migrations.test_migrations_atomic_operation"})
|
||||
def test_atomic_operation_in_non_atomic_migration(self):
|
||||
"""
|
||||
An atomic operation is properly rolled back inside a non-atomic
|
||||
migration.
|
||||
"""
|
||||
executor = MigrationExecutor(connection)
|
||||
with self.assertRaisesMessage(RuntimeError, "Abort migration"):
|
||||
executor.migrate([("migrations", "0001_initial")])
|
||||
migrations_apps = executor.loader.project_state(("migrations", "0001_initial")).apps
|
||||
Editor = migrations_apps.get_model("migrations", "Editor")
|
||||
self.assertFalse(Editor.objects.exists())
|
||||
|
||||
@override_settings(MIGRATION_MODULES={
|
||||
"migrations": "migrations.test_migrations",
|
||||
"migrations2": "migrations2.test_migrations_2",
|
||||
|
|
|
@ -0,0 +1,25 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
def raise_error(apps, schema_editor):
|
||||
# Test atomic operation in non-atomic migration is wrapped in transaction
|
||||
Editor = apps.get_model('migrations', 'Editor')
|
||||
Editor.objects.create(name='Test Editor')
|
||||
raise RuntimeError('Abort migration')
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
atomic = False
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
"Editor",
|
||||
[
|
||||
("name", models.CharField(primary_key=True, max_length=255)),
|
||||
],
|
||||
),
|
||||
migrations.RunPython(raise_error, atomic=True),
|
||||
]
|
32
tests/migrations/test_migrations_non_atomic/0001_initial.py
Normal file
32
tests/migrations/test_migrations_non_atomic/0001_initial.py
Normal file
|
@ -0,0 +1,32 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
def raise_error(apps, schema_editor):
|
||||
# Test operation in non-atomic migration is not wrapped in transaction
|
||||
Publisher = apps.get_model('migrations', 'Publisher')
|
||||
Publisher.objects.create(name='Test Publisher')
|
||||
raise RuntimeError('Abort migration')
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
atomic = False
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
"Publisher",
|
||||
[
|
||||
("name", models.CharField(primary_key=True, max_length=255)),
|
||||
],
|
||||
),
|
||||
migrations.RunPython(raise_error),
|
||||
migrations.CreateModel(
|
||||
"Book",
|
||||
[
|
||||
("title", models.CharField(primary_key=True, max_length=255)),
|
||||
("publisher", models.ForeignKey("migrations.Publisher", models.SET_NULL, null=True)),
|
||||
],
|
||||
),
|
||||
]
|
0
tests/migrations/test_migrations_non_atomic/__init__.py
Normal file
0
tests/migrations/test_migrations_non_atomic/__init__.py
Normal file
Loading…
Add table
Add a link
Reference in a new issue