diff --git a/docs/src/piccolo/migrations/running.rst b/docs/src/piccolo/migrations/running.rst index 74bc9cd50..8979a70b1 100644 --- a/docs/src/piccolo/migrations/running.rst +++ b/docs/src/piccolo/migrations/running.rst @@ -23,16 +23,36 @@ If you have multiple apps you can run them all using: piccolo migrations forwards all +.. _FakeMigration: + Fake ~~~~ We can 'fake' running a migration - we record that it ran in the database without actually running it. +There are two ways to do this - by passing in the ``--fake`` flag on the +command line: + .. code-block:: bash piccolo migrations forwards my_app 2022-09-04T19:44:09 --fake +Or by setting ``fake=True`` on the ``MigrationManager`` within the migration +file. + +.. code-block:: python + + async def forwards(): + manager = MigrationManager( + migration_id=ID, + app_name="app", + description=DESCRIPTION, + fake=True + ) + ... + + This is useful if we started from an existing database using ``piccolo schema generate``, and the initial migration we generated is for tables which already exist, hence we fake run it. diff --git a/docs/src/piccolo/tutorials/migrate_existing_project.rst b/docs/src/piccolo/tutorials/migrate_existing_project.rst index 7ead3dcbd..e4810d25a 100644 --- a/docs/src/piccolo/tutorials/migrate_existing_project.rst +++ b/docs/src/piccolo/tutorials/migrate_existing_project.rst @@ -99,7 +99,7 @@ This will create a new file in ``my_app/piccolo_migrations``: piccolo migrations new my_app --auto These tables already exist in the database, as it's an existing project, so -you need to fake apply this initial migration: +you need to :ref:`fake apply ` this initial migration: .. code-block:: bash diff --git a/piccolo/apps/migrations/auto/migration_manager.py b/piccolo/apps/migrations/auto/migration_manager.py index e8f4931cb..ededa3a25 100644 --- a/piccolo/apps/migrations/auto/migration_manager.py +++ b/piccolo/apps/migrations/auto/migration_manager.py @@ -165,6 +165,7 @@ class MigrationManager: raw_backwards: t.List[t.Union[t.Callable, AsyncFunction]] = field( default_factory=list ) + fake: bool = False def add_table( self, diff --git a/piccolo/apps/migrations/commands/forwards.py b/piccolo/apps/migrations/commands/forwards.py index 62278060d..560f117b2 100644 --- a/piccolo/apps/migrations/commands/forwards.py +++ b/piccolo/apps/migrations/commands/forwards.py @@ -72,18 +72,19 @@ async def run_migrations(self, app_config: AppConfig) -> MigrationResult: print(f"🚀 Running {n} migration{'s' if n != 1 else ''}:") for _id in subset: - if self.fake: - print(f"- {_id}: faked! ⏭️") - else: - migration_module = migration_modules[_id] - response = await migration_module.forwards() + migration_module = migration_modules[_id] + response = await migration_module.forwards() - if isinstance(response, MigrationManager): + if isinstance(response, MigrationManager): + if self.fake or response.fake: + print(f"- {_id}: faked! ⏭️") + else: if self.preview: response.preview = True await response.run() - print("ok! ✔️") + print("ok! ✔️") + if not self.preview: await Migration.insert().add( Migration(name=_id, app_name=app_config.app_name) diff --git a/tests/apps/migrations/commands/test_forwards_backwards.py b/tests/apps/migrations/commands/test_forwards_backwards.py index c3c2c6592..e88747eda 100644 --- a/tests/apps/migrations/commands/test_forwards_backwards.py +++ b/tests/apps/migrations/commands/test_forwards_backwards.py @@ -192,7 +192,7 @@ def test_forwards_no_migrations(self, print_: MagicMock): @engines_only("postgres") def test_forwards_fake(self): """ - Test running the migrations if they've already run. + Make sure migrations can be faked on the command line. """ run_sync(forwards(app_name="music", migration_id="all", fake=True)) @@ -214,9 +214,40 @@ def test_forwards_fake(self): "2021-09-06T13:58:23:024723", "2021-11-13T14:01:46:114725", "2024-05-28T23:15:41:018844", + "2024-06-19T18:11:05:793132", ], ) + @engines_only("postgres") + @patch("piccolo.apps.migrations.commands.forwards.print") + def test_hardcoded_fake_migrations(self, print_: MagicMock): + """ + Make sure that migrations that have been hardcoded as fake aren't + executed, even without the ``--fake`` command line flag. + + See tests/example_apps/music/piccolo_migrations/music_2024_06_19t18_11_05_793132.py + + """ # noqa: E501 + run_sync(forwards(app_name="music", migration_id="all")) + + # The migration which is hardcoded as fake: + migration_name = "2024-06-19T18:11:05:793132" + + self.assertTrue( + Migration.exists() + .where(Migration.name == migration_name) + .run_sync() + ) + + self.assertNotIn( + call("Running fake migration"), + print_.mock_calls, + ) + self.assertIn( + call(f"- {migration_name}: faked! ⏭️"), + print_.mock_calls, + ) + def tearDown(self): for table_class in TABLE_CLASSES + [Migration]: table_class.alter().drop_table( diff --git a/tests/example_apps/music/piccolo_migrations/music_2024_06_19t18_11_05_793132.py b/tests/example_apps/music/piccolo_migrations/music_2024_06_19t18_11_05_793132.py new file mode 100644 index 000000000..5d884210d --- /dev/null +++ b/tests/example_apps/music/piccolo_migrations/music_2024_06_19t18_11_05_793132.py @@ -0,0 +1,20 @@ +from piccolo.apps.migrations.auto.migration_manager import MigrationManager + +ID = "2024-06-19T18:11:05:793132" +VERSION = "1.11.0" +DESCRIPTION = "An example fake migration" + + +async def forwards(): + manager = MigrationManager( + migration_id=ID, app_name="", description=DESCRIPTION, fake=True + ) + + def run(): + # This should never run, as this migrations is `fake=True`. It's here + # for testing purposes (to make sure it never gets triggered). + print("Running fake migration") + + manager.add_raw(run) + + return manager