summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMike Bayer <mike_mp@zzzcomputing.com>2012-05-16 12:16:34 -0400
committerMike Bayer <mike_mp@zzzcomputing.com>2012-05-16 12:16:34 -0400
commit7bdc7e43325612739d049202cf24e5a0e5be5724 (patch)
treec6b37fea2a363eee28ac4a4d612cdae42f56be3f
parent6ed983fc075450d874557e81feb0237d7a28a222 (diff)
downloadalembic-7bdc7e43325612739d049202cf24e5a0e5be5724.tar.gz
- [feature] Added support for "relative" migration
identifiers, i.e. "alembic upgrade +2", "alembic downgrade -1". Courtesy Atsushi Odagiri for this feature.
-rw-r--r--CHANGES5
-rw-r--r--alembic/script.py21
-rw-r--r--docs/build/tutorial.rst13
-rw-r--r--tests/test_revision_paths.py71
4 files changed, 109 insertions, 1 deletions
diff --git a/CHANGES b/CHANGES
index e49a77f..093c5f6 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 bef7dfb..4718e79 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 019697a..af3380c 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 127fda8..dedfa8b 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
+ )