From: Mike Bayer Date: Wed, 16 May 2012 16:16:34 +0000 (-0400) Subject: - [feature] Added support for "relative" migration X-Git-Tag: rel_0_3_3~2 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=7bdc7e43325612739d049202cf24e5a0e5be5724;p=thirdparty%2Fsqlalchemy%2Falembic.git - [feature] Added support for "relative" migration identifiers, i.e. "alembic upgrade +2", "alembic downgrade -1". Courtesy Atsushi Odagiri for this feature. --- diff --git a/CHANGES b/CHANGES index e49a77f5..093c5f64 100644 --- a/CHANGES +++ b/CHANGES @@ -10,6 +10,11 @@ EnvironmentContext.configure(), allowing for the configuration of the version table name. #34 +- [feature] Added support for "relative" migration + identifiers, i.e. "alembic upgrade +2", + "alembic downgrade -1". Courtesy + Atsushi Odagiri for this feature. + 0.3.2 ===== - [feature] Basic support for Oracle added, diff --git a/alembic/script.py b/alembic/script.py index bef7dfb5..4718e798 100644 --- a/alembic/script.py +++ b/alembic/script.py @@ -12,6 +12,7 @@ _legacy_rev = re.compile(r'([a-f0-9]+)\.py$') _mod_def_re = re.compile(r'(upgrade|downgrade)_([a-z0-9]+)') _slug_re = re.compile(r'\w+') _default_file_template = "%(rev)s_%(slug)s" +_relative_destination = re.compile(r'(?:\+|-)\d+') class ScriptDirectory(object): """Provides operations upon an Alembic script directory. @@ -130,6 +131,26 @@ class ScriptDirectory(object): The iterator yields :class:`.Script` objects. """ + if upper is not None and _relative_destination.match(upper): + relative = int(upper) + revs = list(self._iterate_revisions("head", lower)) + revs = revs[-relative:] + if len(revs) != abs(relative): + raise util.CommandError("Relative revision %s didn't " + "produce %d migrations" % (upper, abs(relative))) + return iter(revs) + elif lower is not None and _relative_destination.match(lower): + relative = int(lower) + revs = list(self._iterate_revisions(upper, "base")) + revs = revs[0:-relative] + if len(revs) != abs(relative): + raise util.CommandError("Relative revision %s didn't " + "produce %d migrations" % (lower, abs(relative))) + return iter(revs) + else: + return self._iterate_revisions(upper, lower) + + def _iterate_revisions(self, upper, lower): lower = self.get_revision(lower) upper = self.get_revision(upper) script = upper diff --git a/docs/build/tutorial.rst b/docs/build/tutorial.rst index 019697a0..af3380ce 100644 --- a/docs/build/tutorial.rst +++ b/docs/build/tutorial.rst @@ -347,6 +347,17 @@ Running again to ``head``:: We've now added the ``last_transaction_date`` column to the database. +Relative Migration Identifiers +============================== + +As of 0.3.3, relative upgrades/downgrades are also supported. To move two versions from the current, a decimal value "+N" can be supplied:: + + $ alembic upgrade +2 + +Negative values are accepted for downgrades:: + + $ alembic downgrade -1 + Getting Information =================== @@ -373,6 +384,7 @@ If we wanted to upgrade directly to ``ae1027a6acf`` we could say:: Alembic will stop and let you know if more than one version starts with that prefix. + Downgrading =========== @@ -393,6 +405,7 @@ Back to nothing - and up again:: INFO [alembic.context] Running upgrade None -> 1975ea83b712 INFO [alembic.context] Running upgrade 1975ea83b712 -> ae1027a6acf + Auto Generating Migrations =========================== diff --git a/tests/test_revision_paths.py b/tests/test_revision_paths.py index 127fda8a..dedfa8bc 100644 --- a/tests/test_revision_paths.py +++ b/tests/test_revision_paths.py @@ -1,4 +1,5 @@ -from tests import clear_staging_env, staging_env, eq_, ne_ +from tests import clear_staging_env, staging_env, eq_, ne_, \ + assert_raises_message from alembic import util @@ -35,6 +36,44 @@ def test_upgrade_path(): ] ) +def test_relative_upgrade_path(): + eq_( + env._upgrade_revs("+2", a.revision), + [ + (b.module.upgrade, a.revision, b.revision), + (c.module.upgrade, b.revision, c.revision), + ] + ) + + eq_( + env._upgrade_revs("+1", a.revision), + [ + (b.module.upgrade, a.revision, b.revision), + ] + ) + + eq_( + env._upgrade_revs("+3", b.revision), + [ + (c.module.upgrade, b.revision, c.revision), + (d.module.upgrade, c.revision, d.revision), + (e.module.upgrade, d.revision, e.revision), + ] + ) + +def test_invalid_relative_upgrade_path(): + assert_raises_message( + util.CommandError, + "Relative revision -2 didn't produce 2 migrations", + env._upgrade_revs, "-2", b.revision + ) + + assert_raises_message( + util.CommandError, + r"Relative revision \+5 didn't produce 5 migrations", + env._upgrade_revs, "+5", b.revision + ) + def test_downgrade_path(): eq_( @@ -53,3 +92,33 @@ def test_downgrade_path(): (a.module.downgrade, a.revision, a.down_revision), ] ) + +def test_relative_downgrade_path(): + eq_( + env._downgrade_revs("-1", c.revision), + [ + (c.module.downgrade, c.revision, c.down_revision), + ] + ) + + eq_( + env._downgrade_revs("-3", e.revision), + [ + (e.module.downgrade, e.revision, e.down_revision), + (d.module.downgrade, d.revision, d.down_revision), + (c.module.downgrade, c.revision, c.down_revision), + ] + ) + +def test_invalid_relative_downgrade_path(): + assert_raises_message( + util.CommandError, + "Relative revision -5 didn't produce 5 migrations", + env._downgrade_revs, "-5", b.revision + ) + + assert_raises_message( + util.CommandError, + r"Relative revision \+2 didn't produce 2 migrations", + env._downgrade_revs, "+2", b.revision + )