updated migrate.versioning.repository tests and docs, update README
This commit is contained in:
parent
d44be57771
commit
bc1edc5c1b
16
README
16
README
@ -1,19 +1,25 @@
|
|||||||
SQLAlchemy-migrate is a tool and a set of APIs for managing changes to
|
Inspired by Ruby on Rails' migrations, Migrate provides a way to deal with database schema changes in `SQLAlchemy <http://sqlalchemy.org>`_ projects.
|
||||||
database schemas.
|
|
||||||
|
Migrate extends SQLAlchemy to have database changeset handling. It provides a database change repository mechanism which can be used from the command line as well as from inside python code.
|
||||||
|
|
||||||
Help
|
Help
|
||||||
----
|
----
|
||||||
|
|
||||||
Help is available at the project page [1] and at the public users
|
Sphinx documentation is available at the project page [1].
|
||||||
mailing list [2].
|
|
||||||
|
Users and developers can be found at #sqlalchemy-migrate on Freenode IRC network
|
||||||
|
and at the public users mailing list [2].
|
||||||
|
|
||||||
New releases and major changes are announced at the public announce
|
New releases and major changes are announced at the public announce
|
||||||
mailing list [3] and at the Python package index [4].
|
mailing list [3] and at the Python package index [4].
|
||||||
|
|
||||||
[1] http://code.google.com/p/sqlalchemy-migrate/
|
Homepage is located at [5]
|
||||||
|
|
||||||
|
[1] http://packages.python.org/sqlalchemy-migrate/
|
||||||
[2] http://groups.google.com/group/migrate-users
|
[2] http://groups.google.com/group/migrate-users
|
||||||
[3] http://groups.google.com/group/migrate-announce
|
[3] http://groups.google.com/group/migrate-announce
|
||||||
[4] http://pypi.python.org/pypi/sqlalchemy-migrate
|
[4] http://pypi.python.org/pypi/sqlalchemy-migrate
|
||||||
|
[5] http://code.google.com/p/sqlalchemy-migrate/
|
||||||
|
|
||||||
Tests and Bugs
|
Tests and Bugs
|
||||||
--------------
|
--------------
|
||||||
|
@ -248,7 +248,7 @@ def drop_version_control(url, repository, **opts):
|
|||||||
|
|
||||||
|
|
||||||
def manage(file, **opts):
|
def manage(file, **opts):
|
||||||
"""%prog manage FILENAME VARIABLES...
|
"""%prog manage FILENAME [VARIABLES...]
|
||||||
|
|
||||||
Creates a script that runs Migrate with a set of default values.
|
Creates a script that runs Migrate with a set of default values.
|
||||||
|
|
||||||
@ -263,7 +263,7 @@ def manage(file, **opts):
|
|||||||
python manage.py version
|
python manage.py version
|
||||||
%prog version --repository=/path/to/repository
|
%prog version --repository=/path/to/repository
|
||||||
"""
|
"""
|
||||||
return repository.manage(file, **opts)
|
return Repository.create_manage_file(file, **opts)
|
||||||
|
|
||||||
|
|
||||||
def compare_model_to_db(url, model, repository, **opts):
|
def compare_model_to_db(url, model, repository, **opts):
|
||||||
|
@ -80,7 +80,7 @@ class Repository(pathed.Pathed):
|
|||||||
"""
|
"""
|
||||||
Ensure the target path is a valid repository.
|
Ensure the target path is a valid repository.
|
||||||
|
|
||||||
:raises: :exc:`InvalidRepositoryError` if not valid
|
:raises: :exc:`InvalidRepositoryError <migrate.versioning.exceptions.InvalidRepositoryError>`
|
||||||
"""
|
"""
|
||||||
# Ensure the existance of required files
|
# Ensure the existance of required files
|
||||||
try:
|
try:
|
||||||
@ -90,6 +90,7 @@ class Repository(pathed.Pathed):
|
|||||||
except exceptions.PathNotFoundError, e:
|
except exceptions.PathNotFoundError, e:
|
||||||
raise exceptions.InvalidRepositoryError(path)
|
raise exceptions.InvalidRepositoryError(path)
|
||||||
|
|
||||||
|
# TODO: what are those options?
|
||||||
@classmethod
|
@classmethod
|
||||||
def prepare_config(cls, pkg, rsrc, name, **opts):
|
def prepare_config(cls, pkg, rsrc, name, **opts):
|
||||||
"""
|
"""
|
||||||
@ -99,13 +100,12 @@ class Repository(pathed.Pathed):
|
|||||||
defaults = dict(
|
defaults = dict(
|
||||||
version_table = 'migrate_version',
|
version_table = 'migrate_version',
|
||||||
repository_id = name,
|
repository_id = name,
|
||||||
required_dbs=[], )
|
required_dbs = [])
|
||||||
for key, val in defaults.iteritems():
|
|
||||||
if (key not in opts) or (opts[key] is None):
|
defaults.update(opts)
|
||||||
opts[key]=val
|
|
||||||
|
|
||||||
tmpl = resource_string(pkg, rsrc)
|
tmpl = resource_string(pkg, rsrc)
|
||||||
ret = string.Template(tmpl).substitute(opts)
|
ret = string.Template(tmpl).substitute(defaults)
|
||||||
return ret
|
return ret
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
@ -127,40 +127,46 @@ class Repository(pathed.Pathed):
|
|||||||
fd.close()
|
fd.close()
|
||||||
# Create a management script
|
# Create a management script
|
||||||
manager = os.path.join(path, 'manage.py')
|
manager = os.path.join(path, 'manage.py')
|
||||||
manage(manager, repository=path)
|
Repository.create_manage_file(manager, repository=path)
|
||||||
except:
|
except:
|
||||||
log.error("There was an error creating your repository")
|
log.error("There was an error creating your repository")
|
||||||
return cls(path)
|
return cls(path)
|
||||||
|
|
||||||
def create_script(self, description, **k):
|
def create_script(self, description, **k):
|
||||||
|
""""""
|
||||||
self.versions.create_new_python_version(description, **k)
|
self.versions.create_new_python_version(description, **k)
|
||||||
|
|
||||||
def create_script_sql(self, database, **k):
|
def create_script_sql(self, database, **k):
|
||||||
|
""""""
|
||||||
self.versions.create_new_sql_version(database, **k)
|
self.versions.create_new_sql_version(database, **k)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def latest(self):
|
def latest(self):
|
||||||
|
""""""
|
||||||
return self.versions.latest
|
return self.versions.latest
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def version_table(self):
|
def version_table(self):
|
||||||
|
""""""
|
||||||
return self.config.get('db_settings', 'version_table')
|
return self.config.get('db_settings', 'version_table')
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def id(self):
|
def id(self):
|
||||||
|
""""""
|
||||||
return self.config.get('db_settings', 'repository_id')
|
return self.config.get('db_settings', 'repository_id')
|
||||||
|
|
||||||
def version(self, *p, **k):
|
def version(self, *p, **k):
|
||||||
|
""""""
|
||||||
return self.versions.version(*p, **k)
|
return self.versions.version(*p, **k)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def clear(cls):
|
def clear(cls):
|
||||||
|
""""""
|
||||||
super(Repository, cls).clear()
|
super(Repository, cls).clear()
|
||||||
version.Collection.clear()
|
version.Collection.clear()
|
||||||
|
|
||||||
def changeset(self, database, start, end=None):
|
def changeset(self, database, start, end=None):
|
||||||
"""
|
"""Create a changeset to migrate this dbms from ver. start to end/latest.
|
||||||
Create a changeset to migrate this dbms from ver. start to end/latest.
|
|
||||||
"""
|
"""
|
||||||
start = version.VerNum(start)
|
start = version.VerNum(start)
|
||||||
if end is None:
|
if end is None:
|
||||||
@ -181,13 +187,19 @@ class Repository(pathed.Pathed):
|
|||||||
return ret
|
return ret
|
||||||
|
|
||||||
|
|
||||||
def manage(file, **opts):
|
@classmethod
|
||||||
"""Create a project management script"""
|
def create_manage_file(cls, file_, **opts):
|
||||||
|
"""Create a project management script (manage.py)
|
||||||
|
|
||||||
|
:param file_: Destination file to be written
|
||||||
|
:param **opts: Options that are passed to template
|
||||||
|
"""
|
||||||
|
vars_ = ",".join(["%s='%s'" % var for var in opts.iteritems()])
|
||||||
|
|
||||||
pkg, rsrc = template.manage(as_pkg=True)
|
pkg, rsrc = template.manage(as_pkg=True)
|
||||||
tmpl = resource_string(pkg, rsrc)
|
tmpl = resource_string(pkg, rsrc)
|
||||||
vars = ",".join(["%s='%s'" % vars for vars in opts.iteritems()])
|
result = tmpl % dict(defaults=vars_)
|
||||||
result = tmpl % dict(defaults=vars)
|
|
||||||
|
|
||||||
fd = open(file, 'w')
|
fd = open(file_, 'w')
|
||||||
fd.write(result)
|
fd.write(result)
|
||||||
fd.close()
|
fd.close()
|
||||||
|
11
setup.py
11
setup.py
@ -1,5 +1,7 @@
|
|||||||
#!/usr/bin/python
|
#!/usr/bin/python
|
||||||
|
|
||||||
|
import os
|
||||||
|
|
||||||
try:
|
try:
|
||||||
from setuptools import setup, find_packages
|
from setuptools import setup, find_packages
|
||||||
except ImportError:
|
except ImportError:
|
||||||
@ -14,6 +16,7 @@ except ImportError:
|
|||||||
|
|
||||||
test_requirements = ['nose >= 0.10']
|
test_requirements = ['nose >= 0.10']
|
||||||
required_deps = ['sqlalchemy >= 0.5', 'decorator']
|
required_deps = ['sqlalchemy >= 0.5', 'decorator']
|
||||||
|
readme_file = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'README')
|
||||||
|
|
||||||
setup(
|
setup(
|
||||||
name = "sqlalchemy-migrate",
|
name = "sqlalchemy-migrate",
|
||||||
@ -21,12 +24,7 @@ setup(
|
|||||||
packages = find_packages(exclude=['test*']),
|
packages = find_packages(exclude=['test*']),
|
||||||
include_package_data = True,
|
include_package_data = True,
|
||||||
description = "Database schema migration for SQLAlchemy",
|
description = "Database schema migration for SQLAlchemy",
|
||||||
long_description = """
|
long_description = readme_file.read(),
|
||||||
Inspired by Ruby on Rails' migrations, Migrate provides a way to deal with database schema changes in `SQLAlchemy <http://sqlalchemy.org>`_ projects.
|
|
||||||
|
|
||||||
Migrate extends SQLAlchemy to have database changeset handling. It provides a database change repository mechanism which can be used from the command line as well as from inside python code.
|
|
||||||
""",
|
|
||||||
|
|
||||||
install_requires = required_deps,
|
install_requires = required_deps,
|
||||||
tests_require = test_requirements,
|
tests_require = test_requirements,
|
||||||
extras_require = {
|
extras_require = {
|
||||||
@ -38,7 +36,6 @@ Migrate extends SQLAlchemy to have database changeset handling. It provides a da
|
|||||||
maintainer = "Jan Dittberner",
|
maintainer = "Jan Dittberner",
|
||||||
maintainer_email = "jan@dittberner.info",
|
maintainer_email = "jan@dittberner.info",
|
||||||
license = "MIT",
|
license = "MIT",
|
||||||
|
|
||||||
entry_points = """
|
entry_points = """
|
||||||
[console_scripts]
|
[console_scripts]
|
||||||
migrate = migrate.versioning.shell:main
|
migrate = migrate.versioning.shell:main
|
||||||
|
@ -1,22 +1,33 @@
|
|||||||
from test import fixture
|
#!/usr/bin/env python
|
||||||
from migrate.versioning.repository import *
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
import os
|
||||||
|
import shutil
|
||||||
|
|
||||||
from migrate.versioning import exceptions
|
from migrate.versioning import exceptions
|
||||||
import os,shutil
|
from migrate.versioning.repository import *
|
||||||
|
from nose.tools import raises
|
||||||
|
|
||||||
|
from test import fixture
|
||||||
|
|
||||||
|
|
||||||
class TestRepository(fixture.Pathed):
|
class TestRepository(fixture.Pathed):
|
||||||
def test_create(self):
|
def test_create(self):
|
||||||
"""Repositories are created successfully"""
|
"""Repositories are created successfully"""
|
||||||
path = self.tmp_repos()
|
path = self.tmp_repos()
|
||||||
name = 'repository_name'
|
name = 'repository_name'
|
||||||
|
|
||||||
# Creating a repository that doesn't exist should succeed
|
# Creating a repository that doesn't exist should succeed
|
||||||
repos=Repository.create(path,name)
|
repo = Repository.create(path, name)
|
||||||
config_path=repos.config.path
|
config_path = repo.config.path
|
||||||
manage_path=os.path.join(repos.path,'manage.py')
|
manage_path = os.path.join(repo.path, 'manage.py')
|
||||||
self.assert_(repos)
|
self.assert_(repo)
|
||||||
|
|
||||||
# Files should actually be created
|
# Files should actually be created
|
||||||
self.assert_(os.path.exists(path))
|
self.assert_(os.path.exists(path))
|
||||||
self.assert_(os.path.exists(config_path))
|
self.assert_(os.path.exists(config_path))
|
||||||
self.assert_(os.path.exists(manage_path))
|
self.assert_(os.path.exists(manage_path))
|
||||||
|
|
||||||
# Can't create it again: it already exists
|
# Can't create it again: it already exists
|
||||||
self.assertRaises(exceptions.PathFoundError, Repository.create, path, name)
|
self.assertRaises(exceptions.PathFoundError, Repository.create, path, name)
|
||||||
return path
|
return path
|
||||||
@ -26,12 +37,13 @@ class TestRepository(fixture.Pathed):
|
|||||||
# Create a repository to load
|
# Create a repository to load
|
||||||
path = self.test_create()
|
path = self.test_create()
|
||||||
repos = Repository(path)
|
repos = Repository(path)
|
||||||
|
|
||||||
self.assert_(repos)
|
self.assert_(repos)
|
||||||
self.assert_(repos.config)
|
self.assert_(repos.config)
|
||||||
self.assert_(repos.config.get('db_settings', 'version_table'))
|
self.assert_(repos.config.get('db_settings', 'version_table'))
|
||||||
|
|
||||||
# version_table's default isn't none
|
# version_table's default isn't none
|
||||||
self.assertNotEquals(repos.config.get('db_settings', 'version_table'), 'None')
|
self.assertNotEquals(repos.config.get('db_settings', 'version_table'), 'None')
|
||||||
from nose.tools import raises
|
|
||||||
|
|
||||||
def test_load_notfound(self):
|
def test_load_notfound(self):
|
||||||
"""Nonexistant repositories shouldn't be loaded"""
|
"""Nonexistant repositories shouldn't be loaded"""
|
||||||
@ -50,29 +62,31 @@ class TestRepository(fixture.Pathed):
|
|||||||
|
|
||||||
class TestVersionedRepository(fixture.Pathed):
|
class TestVersionedRepository(fixture.Pathed):
|
||||||
"""Tests on an existing repository with a single python script"""
|
"""Tests on an existing repository with a single python script"""
|
||||||
script_cls = script.PythonScript
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
super(TestVersionedRepository, self).setUp()
|
super(TestVersionedRepository, self).setUp()
|
||||||
Repository.clear()
|
Repository.clear()
|
||||||
self.path_repos = self.tmp_repos()
|
self.path_repos = self.tmp_repos()
|
||||||
# Create repository, script
|
|
||||||
Repository.create(self.path_repos, 'repository_name')
|
Repository.create(self.path_repos, 'repository_name')
|
||||||
|
|
||||||
def test_version(self):
|
def test_version(self):
|
||||||
"""We should correctly detect the version of a repository"""
|
"""We should correctly detect the version of a repository"""
|
||||||
repos = Repository(self.path_repos)
|
repos = Repository(self.path_repos)
|
||||||
|
|
||||||
# Get latest version, or detect if a specified version exists
|
# Get latest version, or detect if a specified version exists
|
||||||
self.assertEquals(repos.latest, 0)
|
self.assertEquals(repos.latest, 0)
|
||||||
# repos.latest isn't an integer, but a VerNum
|
# repos.latest isn't an integer, but a VerNum
|
||||||
# (so we can't just assume the following tests are correct)
|
# (so we can't just assume the following tests are correct)
|
||||||
self.assert_(repos.latest >= 0)
|
self.assert_(repos.latest >= 0)
|
||||||
self.assert_(repos.latest < 1)
|
self.assert_(repos.latest < 1)
|
||||||
|
|
||||||
# Create a script and test again
|
# Create a script and test again
|
||||||
repos.create_script('')
|
repos.create_script('')
|
||||||
self.assertEquals(repos.latest, 1)
|
self.assertEquals(repos.latest, 1)
|
||||||
self.assert_(repos.latest >= 0)
|
self.assert_(repos.latest >= 0)
|
||||||
self.assert_(repos.latest >= 1)
|
self.assert_(repos.latest >= 1)
|
||||||
self.assert_(repos.latest < 2)
|
self.assert_(repos.latest < 2)
|
||||||
|
|
||||||
# Create a new script and test again
|
# Create a new script and test again
|
||||||
repos.create_script('')
|
repos.create_script('')
|
||||||
self.assertEquals(repos.latest, 2)
|
self.assertEquals(repos.latest, 2)
|
||||||
@ -80,16 +94,20 @@ class TestVersionedRepository(fixture.Pathed):
|
|||||||
self.assert_(repos.latest >= 1)
|
self.assert_(repos.latest >= 1)
|
||||||
self.assert_(repos.latest >= 2)
|
self.assert_(repos.latest >= 2)
|
||||||
self.assert_(repos.latest < 3)
|
self.assert_(repos.latest < 3)
|
||||||
|
|
||||||
def test_source(self):
|
def test_source(self):
|
||||||
"""Get a script object by version number and view its source"""
|
"""Get a script object by version number and view its source"""
|
||||||
# Load repository and commit script
|
# Load repository and commit script
|
||||||
repos=Repository(self.path_repos)
|
repo = Repository(self.path_repos)
|
||||||
repos.create_script('')
|
repo.create_script('')
|
||||||
|
|
||||||
# Get script object
|
# Get script object
|
||||||
source=repos.version(1).script().source()
|
source = repo.version(1).script().source()
|
||||||
|
|
||||||
# Source is valid: script must have an upgrade function
|
# Source is valid: script must have an upgrade function
|
||||||
# (not a very thorough test, but should be plenty)
|
# (not a very thorough test, but should be plenty)
|
||||||
self.assert_(source.find('def upgrade') >= 0)
|
self.assert_(source.find('def upgrade') >= 0)
|
||||||
|
|
||||||
def test_latestversion(self):
|
def test_latestversion(self):
|
||||||
"""Repository.version() (no params) returns the latest version"""
|
"""Repository.version() (no params) returns the latest version"""
|
||||||
repos = Repository(self.path_repos)
|
repos = Repository(self.path_repos)
|
||||||
@ -165,3 +183,4 @@ class TestVersionedRepository(fixture.Pathed):
|
|||||||
self.assert_(os.path.exists('%s/versions/1000.py' % self.path_repos))
|
self.assert_(os.path.exists('%s/versions/1000.py' % self.path_repos))
|
||||||
self.assert_(os.path.exists('%s/versions/1001.py' % self.path_repos))
|
self.assert_(os.path.exists('%s/versions/1001.py' % self.path_repos))
|
||||||
|
|
||||||
|
# TODO: test manage file
|
||||||
|
Loading…
x
Reference in New Issue
Block a user