New migration utility code.... I haven't tested this! ;)

I think it's looking right though.
 - Provides MigrationManager which should have plenty of utilities for
   doing migrations hopefully correctly :)
 - Provides RegisterMigration which should be able to decorate
   migrations and register them in doing so
This commit is contained in:
Christopher Allan Webber 2011-07-09 14:50:41 -05:00
parent 9980c5f4f4
commit 51dcfb5682

View File

@ -37,6 +37,11 @@ from mongokit import ObjectId
from mediagoblin.db.indexes import ACTIVE_INDEXES, DEPRECATED_INDEXES
################
# Indexing tools
################
def add_new_indexes(database, active_indexes=ACTIVE_INDEXES):
"""
Add any new indexes to the database.
@ -99,3 +104,134 @@ def remove_deprecated_indexes(database, deprecated_indexes=DEPRECATED_INDEXES):
indexes_removed.append((collection_name, index_name))
return indexes_removed
#################
# Migration tools
#################
# The default migration registry...
#
# Don't set this yourself! RegisterMigration will automatically fill
# this with stuff via decorating methods in migrations.py
MIGRATIONS = {}
class RegisterMigration(object):
def __init__(self, migration_number, migration_registry=MIGRATIONS):
self.migration_number = migration_number
self.migration_registry = migration_registry
def __call__(self, migration):
self.migration_registry[self.migration_number] = migration
return migration
class MigrationManager(object):
"""
Migration handling tool.
Takes information about a database, lets you update the database
to the latest migrations, etc.
"""
def __init__(self, database, migration_registry=MIGRATIONS):
"""
Args:
- database: database we're going to migrate
- migration_registry: where we should find all migrations to
run
"""
self.database = database
self.migration_registry = migration_registry
self._sorted_migrations = None
@property
def sorted_migrations(self):
"""
Sort migrations if necessary and store in self._sorted_migrations
"""
if not self._sorted_migrations:
self._sorted_migrations = sorted(
self.migration_registry.items(),
# sort on the key... the migration number
key=lambda migration_tuple: migration_tuple[0])
return self._sorted_migrations
def latest_migration(self):
"""
Return a tuple like:
(migration_number, migration_func)
Where migration_number is the number of the latest migration
and migration func is the actual function that would be run.
"""
return self.sorted_migrations[-1]
def set_current_migration(self, migration_number=None):
"""
Set the migration in the database to migration_number
"""
# Add the mediagoblin migration if necessary
self.database['app_metadata'].update(
{'_id': 'mediagoblin'},
{'$set': {'current_migration': migration_number}},
upsert=True)
def database_current_migration(self, install_if_missing=True):
"""
Return the current migration in the database.
"""
mgoblin_metadata = self.database['app_metadata'].find_one(
{'_id': 'mediagoblin'})
if not mgoblin_metadata:
if install_if_missing:
latest_migration = self.latest_migration()
self.set_current_migration(latest_migration)
return latest_migration
else:
return None
else:
return mgoblin_metadata['current_migration']
def database_at_latest_migration(self):
"""
See if the database is at the latest migration.
Returns a boolean.
"""
current_migration = self.database_current_migration()
return current_migration == self.latest_migration()
def migrations_to_run(self):
"""
Get a list of migrations to run still, if any.
"""
db_current_migration = self.database_current_migration()
return [
(migration_number, migration_func)
for migration_number, migration_func in self.sorted_migrations
if migration_number > db_current_migration]
def iteratively_migrate(self):
"""
Iteratively run all migrations.
Useful if you need to print some message about each migration
after you run it.
Each time you loop over this, it'll return the migration
number and migration function.
"""
for migration_number, migration_func in self.migrations_to_run():
migration_func(self.database)
self.set_current_migration(migration_number)
yield migration_number, migration_func
def run_outdated_migrations(self):
"""
Install all migrations that need to be installed, quietly.
"""
for migration_number, migration_func in self.iteratively_migrate():
# No need to say anything... we're just migrating iteratively.
pass