From 494d2dc316fc1d849b6a1af97575d293f856a84c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mikul=C3=A1=C5=A1=20Poul?= Date: Sun, 23 Mar 2025 15:30:24 +0000 Subject: [PATCH] Fixed #36274 -- Added support for run_before and atomic in MigrationWriter. --- AUTHORS | 1 + django/db/migrations/writer.py | 11 ++++++++-- tests/migrations/test_writer.py | 39 +++++++++++++++++++++++++++++++++ 3 files changed, 49 insertions(+), 2 deletions(-) diff --git a/AUTHORS b/AUTHORS index 637ca41ad7..b22af9bccb 100644 --- a/AUTHORS +++ b/AUTHORS @@ -751,6 +751,7 @@ answer newbie questions, and generally made Django that much better: Mikhail Korobov Mikko Hellsing Mikołaj Siedlarek + Mikuláš Poul milkomeda Milton Waddams mitakummaa@gmail.com diff --git a/django/db/migrations/writer.py b/django/db/migrations/writer.py index 3dd3014355..e2befd4d4e 100644 --- a/django/db/migrations/writer.py +++ b/django/db/migrations/writer.py @@ -131,6 +131,8 @@ class MigrationWriter: items = { "replaces_str": "", "initial_str": "", + "run_before_str": "", + "atomic_str": "", } imports = set() @@ -189,11 +191,14 @@ class MigrationWriter: "then update the\n# RunPython operations to refer to the local " "versions:\n# %s" ) % "\n# ".join(sorted(migration_imports)) - # If there's a replaces, make a string for it if self.migration.replaces: items["replaces_str"] = ( "\n replaces = %s\n" % self.serialize(self.migration.replaces)[0] ) + if self.migration.run_before: + items["run_before_str"] = ( + "\n run_before = %s\n" % self.serialize(self.migration.run_before)[0] + ) # Hinting that goes into comment if self.include_header: items["migration_header"] = MIGRATION_HEADER_TEMPLATE % { @@ -205,6 +210,8 @@ class MigrationWriter: if self.migration.initial: items["initial_str"] = "\n initial = True\n" + if not self.migration.atomic: + items["atomic_str"] = "\n atomic = False\n" return MIGRATION_TEMPLATE % items @@ -305,7 +312,7 @@ MIGRATION_TEMPLATE = """\ %(migration_header)s%(imports)s class Migration(migrations.Migration): -%(replaces_str)s%(initial_str)s +%(replaces_str)s%(initial_str)s%(atomic_str)s%(run_before_str)s dependencies = [ %(dependencies)s\ ] diff --git a/tests/migrations/test_writer.py b/tests/migrations/test_writer.py index 6e9355d9bc..6afee23da2 100644 --- a/tests/migrations/test_writer.py +++ b/tests/migrations/test_writer.py @@ -1178,3 +1178,42 @@ class WriterTests(SimpleTestCase): output = writer.as_string() self.assertEqual(output.count("import"), 1) self.assertIn("from django.db import migrations, models", output) + + def test_run_before(self): + for run_before, expected_run_before_str in [ + ([("foo", "0001_bar")], " run_before = [('foo', '0001_bar')]\n"), + ( + [("foo", "0001_bar"), ("foo", "0002_baz")], + " run_before = [('foo', '0001_bar'), ('foo', '0002_baz')]\n", + ), + ]: + with self.subTest(run_before=run_before): + migration = type( + "Migration", + (migrations.Migration,), + {"operations": [], "run_before": run_before}, + ) + writer = MigrationWriter(migration) + output = writer.as_string() + self.assertIn(expected_run_before_str, output) + + def test_atomic_is_false(self): + migration = type( + "Migration", + (migrations.Migration,), + {"operations": [], "atomic": False}, + ) + writer = MigrationWriter(migration) + output = writer.as_string() + self.assertIn(" atomic = False\n", output) + + def test_default_attributes(self): + migration = type("Migration", (migrations.Migration,), {}) + writer = MigrationWriter(migration) + output = writer.as_string() + self.assertIn(" dependencies = [\n ]\n", output) + self.assertIn(" operations = [\n ]\n", output) + self.assertNotIn("atomic", output) + self.assertNotIn("initial", output) + self.assertNotIn("run_before", output) + self.assertNotIn("replaces", output)