adding sqlalchemy-migrate glue
* add a product and producttype table (addresses #1) * add a person table and reference to customers table (fixes #8) * use sqlalchemy-migrate's API to setup database and add configuration for the sqlalchemy-migrate calls to development.ini and the paste_deploy template (fixes #7) git-svn-id: file:///var/www/wwwusers/usr01/svn/pyalchemybiz/trunk@7 389c73d4-bf09-4d3d-a15e-f94a37d0667a
This commit is contained in:
parent
ab91d92af3
commit
1228fcef3c
16 changed files with 183 additions and 12 deletions
4
data/dbrepo/README
Normal file
4
data/dbrepo/README
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
This is a database migration repository.
|
||||||
|
|
||||||
|
More information at
|
||||||
|
http://code.google.com/p/sqlalchemy-migrate/
|
0
data/dbrepo/__init__.py
Normal file
0
data/dbrepo/__init__.py
Normal file
4
data/dbrepo/manage.py
Normal file
4
data/dbrepo/manage.py
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
#!/usr/bin/env python
|
||||||
|
from migrate.versioning.shell import main
|
||||||
|
|
||||||
|
main(repository='data/dbrepo')
|
20
data/dbrepo/migrate.cfg
Normal file
20
data/dbrepo/migrate.cfg
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
[db_settings]
|
||||||
|
# Used to identify which repository this database is versioned under.
|
||||||
|
# You can use the name of your project.
|
||||||
|
repository_id=pyalchemybiz
|
||||||
|
|
||||||
|
# The name of the database table used to track the schema version.
|
||||||
|
# This name shouldn't already be used by your project.
|
||||||
|
# If this is changed once a database is under version control, you'll need to
|
||||||
|
# change the table name in each database too.
|
||||||
|
version_table=migrate_version
|
||||||
|
|
||||||
|
# When committing a change script, Migrate will attempt to generate the
|
||||||
|
# sql for all supported databases; normally, if one of them fails - probably
|
||||||
|
# because you don't have that database installed - it is ignored and the
|
||||||
|
# commit continues, perhaps ending successfully.
|
||||||
|
# Databases in this list MUST compile successfully during a commit, or the
|
||||||
|
# entire commit will fail. List the databases your application will actually
|
||||||
|
# be using to ensure your updates to that database work properly.
|
||||||
|
# This must be a list; example: ['postgres','sqlite']
|
||||||
|
required_dbs=[]
|
29
data/dbrepo/versions/001_Add_initial_tables.py
Normal file
29
data/dbrepo/versions/001_Add_initial_tables.py
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
from sqlalchemy import MetaData, Table, Column, ForeignKey, types
|
||||||
|
from migrate import *
|
||||||
|
|
||||||
|
def upgrade():
|
||||||
|
# Upgrade operations go here. Don't create your own engine; use the engine
|
||||||
|
# named 'migrate_engine' imported from migrate.
|
||||||
|
meta = MetaData(bind=migrate_engine)
|
||||||
|
t_product_type = Table(
|
||||||
|
'producttype', meta,
|
||||||
|
Column('id', types.Integer, primary_key=True),
|
||||||
|
Column('name', types.Unicode(40), nullable=False),
|
||||||
|
Column('description', types.UnicodeText(), nullable=False))
|
||||||
|
t_product_type.create()
|
||||||
|
t_product = Table(
|
||||||
|
'product', meta,
|
||||||
|
Column('id', types.Integer, primary_key=True),
|
||||||
|
Column('name', types.Unicode(100), nullable=False),
|
||||||
|
Column('description', types.UnicodeText(), nullable=False),
|
||||||
|
Column('producttype_id', types.Integer,
|
||||||
|
ForeignKey(t_product_type.c.id), nullable=False))
|
||||||
|
t_product.create()
|
||||||
|
|
||||||
|
def downgrade():
|
||||||
|
# Operations to reverse the above upgrade go here.
|
||||||
|
meta = MetaData(bind=migrate_engine)
|
||||||
|
t_product = Table('product', meta, autoload=True)
|
||||||
|
t_product.drop()
|
||||||
|
t_product_type = Table('product_type', meta, autoload=True)
|
||||||
|
t_product_type.drop()
|
27
data/dbrepo/versions/002_Add_customer_tables.py
Normal file
27
data/dbrepo/versions/002_Add_customer_tables.py
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
from sqlalchemy import MetaData, Table, Column, ForeignKey, types
|
||||||
|
from migrate import *
|
||||||
|
|
||||||
|
def upgrade():
|
||||||
|
# Upgrade operations go here. Don't create your own engine; use the engine
|
||||||
|
# named 'migrate_engine' imported from migrate.
|
||||||
|
meta = MetaData(bind=migrate_engine)
|
||||||
|
t_person = Table(
|
||||||
|
'person', meta,
|
||||||
|
Column('id', types.Integer, primary_key=True),
|
||||||
|
Column('firstname', types.Unicode(100), nullable=False),
|
||||||
|
Column('lastname', types.Unicode(100), nullable=False))
|
||||||
|
t_person.create()
|
||||||
|
t_customer = Table(
|
||||||
|
'customer', meta,
|
||||||
|
Column('id', types.Integer, primary_key=True),
|
||||||
|
Column('person_id', types.Integer, ForeignKey(t_person.c.id),
|
||||||
|
nullable=False, unique=True))
|
||||||
|
t_customer.create()
|
||||||
|
|
||||||
|
def downgrade():
|
||||||
|
# Operations to reverse the above upgrade go here.
|
||||||
|
meta = MetaData(bind=migrate_engine)
|
||||||
|
t_customer = Table('customer', meta, autoload=True)
|
||||||
|
t_customer.drop()
|
||||||
|
t_person = Table('person', meta, autoload=True)
|
||||||
|
t_person.drop()
|
0
data/dbrepo/versions/__init__.py
Normal file
0
data/dbrepo/versions/__init__.py
Normal file
|
@ -40,6 +40,10 @@ sqlalchemy.default.url = sqlite:///%(here)s/pyalchemybiz.db
|
||||||
sqlalchemy.default.echo = true
|
sqlalchemy.default.echo = true
|
||||||
sqlalchemy.convert_unicode = true
|
sqlalchemy.convert_unicode = true
|
||||||
|
|
||||||
|
# settings for sqlalchemy-migrate
|
||||||
|
migrate.repo.version = 2
|
||||||
|
migrate.repo.dir = %(here)s/data/dbrepo
|
||||||
|
|
||||||
|
|
||||||
# Logging configuration
|
# Logging configuration
|
||||||
[loggers]
|
[loggers]
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
Metadata-Version: 1.0
|
Metadata-Version: 1.0
|
||||||
Name: pyalchemybiz
|
Name: pyalchemybiz
|
||||||
Version: 0.1dev-r5
|
Version: 0.1dev-r6
|
||||||
Summary: python based small business suite.
|
Summary: python based small business suite.
|
||||||
Home-page: http://www.dittberner.info/projects/pyalchemybiz
|
Home-page: http://www.dittberner.info/projects/pyalchemybiz
|
||||||
Author: Jan Dittberner
|
Author: Jan Dittberner
|
||||||
|
|
|
@ -4,8 +4,13 @@ development.ini
|
||||||
setup.cfg
|
setup.cfg
|
||||||
setup.py
|
setup.py
|
||||||
test.ini
|
test.ini
|
||||||
data/templates/base.mako.py
|
data/dbrepo/README
|
||||||
data/templates/customer.mako.py
|
data/dbrepo/__init__.py
|
||||||
|
data/dbrepo/manage.py
|
||||||
|
data/dbrepo/migrate.cfg
|
||||||
|
data/dbrepo/versions/001_Add_initial_tables.py
|
||||||
|
data/dbrepo/versions/002_Add_customer_tables.py
|
||||||
|
data/dbrepo/versions/__init__.py
|
||||||
docs/index.txt
|
docs/index.txt
|
||||||
pyalchemybiz/__init__.py
|
pyalchemybiz/__init__.py
|
||||||
pyalchemybiz/websetup.py
|
pyalchemybiz/websetup.py
|
||||||
|
@ -24,6 +29,7 @@ pyalchemybiz/config/routing.py
|
||||||
pyalchemybiz/controllers/__init__.py
|
pyalchemybiz/controllers/__init__.py
|
||||||
pyalchemybiz/controllers/customer.py
|
pyalchemybiz/controllers/customer.py
|
||||||
pyalchemybiz/controllers/error.py
|
pyalchemybiz/controllers/error.py
|
||||||
|
pyalchemybiz/controllers/index.py
|
||||||
pyalchemybiz/controllers/template.py
|
pyalchemybiz/controllers/template.py
|
||||||
pyalchemybiz/lib/__init__.py
|
pyalchemybiz/lib/__init__.py
|
||||||
pyalchemybiz/lib/app_globals.py
|
pyalchemybiz/lib/app_globals.py
|
||||||
|
@ -32,10 +38,13 @@ pyalchemybiz/lib/helpers.py
|
||||||
pyalchemybiz/model/__init__.py
|
pyalchemybiz/model/__init__.py
|
||||||
pyalchemybiz/model/customer.py
|
pyalchemybiz/model/customer.py
|
||||||
pyalchemybiz/model/meta.py
|
pyalchemybiz/model/meta.py
|
||||||
pyalchemybiz/public/index.html
|
pyalchemybiz/model/person.py
|
||||||
|
pyalchemybiz/model/product.py
|
||||||
pyalchemybiz/templates/base.mako
|
pyalchemybiz/templates/base.mako
|
||||||
pyalchemybiz/templates/customer.mako
|
pyalchemybiz/templates/customer.mako
|
||||||
|
pyalchemybiz/templates/index.mako
|
||||||
pyalchemybiz/tests/__init__.py
|
pyalchemybiz/tests/__init__.py
|
||||||
pyalchemybiz/tests/test_models.py
|
pyalchemybiz/tests/test_models.py
|
||||||
pyalchemybiz/tests/functional/__init__.py
|
pyalchemybiz/tests/functional/__init__.py
|
||||||
pyalchemybiz/tests/functional/test_customer.py
|
pyalchemybiz/tests/functional/test_customer.py
|
||||||
|
pyalchemybiz/tests/functional/test_index.py
|
|
@ -38,6 +38,11 @@ set debug = false
|
||||||
# invalidate the URI when specifying a SQLite db via path name
|
# invalidate the URI when specifying a SQLite db via path name
|
||||||
#sqlalchemy.default.url = sqlite:///%(here)s/pyalchemybiz.db
|
#sqlalchemy.default.url = sqlite:///%(here)s/pyalchemybiz.db
|
||||||
#sqlalchemy.default.echo = false
|
#sqlalchemy.default.echo = false
|
||||||
|
sqlalchemy.default.convert_unicode = true
|
||||||
|
|
||||||
|
# settings for sqlalchemy-migrate
|
||||||
|
migrate.repo.version = 2
|
||||||
|
migrate.repo.dir = %(here)s/data/dbrepo
|
||||||
|
|
||||||
|
|
||||||
# Logging configuration
|
# Logging configuration
|
||||||
|
|
|
@ -3,6 +3,9 @@ import os
|
||||||
|
|
||||||
from pylons import config
|
from pylons import config
|
||||||
from sqlalchemy import engine_from_config
|
from sqlalchemy import engine_from_config
|
||||||
|
from sqlalchemy.exceptions import NoSuchTableError
|
||||||
|
from migrate.versioning.api import db_version
|
||||||
|
|
||||||
from pyalchemybiz.model import init_model
|
from pyalchemybiz.model import init_model
|
||||||
|
|
||||||
import pyalchemybiz.lib.app_globals as app_globals
|
import pyalchemybiz.lib.app_globals as app_globals
|
||||||
|
@ -35,4 +38,17 @@ def load_environment(global_conf, app_conf):
|
||||||
# any Pylons config options)
|
# any Pylons config options)
|
||||||
engine = \
|
engine = \
|
||||||
engine_from_config(config, 'sqlalchemy.default.')
|
engine_from_config(config, 'sqlalchemy.default.')
|
||||||
init_model(engine)
|
|
||||||
|
try:
|
||||||
|
init_model(engine)
|
||||||
|
except Exception:
|
||||||
|
# special handling for calls in websetup.py
|
||||||
|
import inspect
|
||||||
|
frame = inspect.currentframe()
|
||||||
|
try:
|
||||||
|
functions = [current[3] for current in \
|
||||||
|
inspect.getouterframes(frame)]
|
||||||
|
if functions[1] != 'setup_config':
|
||||||
|
raise
|
||||||
|
finally:
|
||||||
|
del frame
|
||||||
|
|
|
@ -1,8 +1,11 @@
|
||||||
|
import logging
|
||||||
import sqlalchemy as sa
|
import sqlalchemy as sa
|
||||||
from sqlalchemy import orm
|
from sqlalchemy import orm
|
||||||
|
|
||||||
from pyalchemybiz.model import meta
|
from pyalchemybiz.model import meta
|
||||||
from pyalchemybiz.model import customer
|
from pyalchemybiz.model import person, customer, product
|
||||||
|
|
||||||
|
log = logging.getLogger(__name__)
|
||||||
|
|
||||||
def init_model(engine):
|
def init_model(engine):
|
||||||
"""Call me before using any of the tables or classes in the model."""
|
"""Call me before using any of the tables or classes in the model."""
|
||||||
|
@ -11,7 +14,19 @@ def init_model(engine):
|
||||||
|
|
||||||
meta.engine = engine
|
meta.engine = engine
|
||||||
meta.Session = orm.scoped_session(sm)
|
meta.Session = orm.scoped_session(sm)
|
||||||
|
person.t_person = sa.Table(
|
||||||
|
'person', meta.metadata, autoload=True, autoload_with=engine)
|
||||||
|
customer.t_customer = sa.Table(
|
||||||
|
'customer', meta.metadata, autoload=True, autoload_with=engine)
|
||||||
|
product.t_producttype = sa.Table(
|
||||||
|
'producttype', meta.metadata, autoload=True, autoload_with=engine)
|
||||||
|
product.t_product = sa.Table(
|
||||||
|
'product', meta.metadata, autoload=True, autoload_with=engine)
|
||||||
|
|
||||||
customer.t_customer = sa.Table('customer', meta.metadata,
|
orm.mapper(person.Person, person.t_person)
|
||||||
autoload=True, autoload_with=engine)
|
|
||||||
orm.mapper(customer.Customer, customer.t_customer)
|
orm.mapper(customer.Customer, customer.t_customer)
|
||||||
|
customer.Customer.person = orm.relation(person.Person)
|
||||||
|
orm.mapper(product.ProductType, product.t_producttype)
|
||||||
|
orm.mapper(product.Product, product.t_product)
|
||||||
|
product.Product.producttype = orm.relation(
|
||||||
|
product.Product, backref=orm.backref('products', lazy='dynamic'))
|
||||||
|
|
5
pyalchemybiz/model/person.py
Normal file
5
pyalchemybiz/model/person.py
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
t_person = None
|
||||||
|
|
||||||
|
class Person(object):
|
||||||
|
def __str__(self):
|
||||||
|
return "%s %s" % (self.firstname, self.lastname)
|
8
pyalchemybiz/model/product.py
Normal file
8
pyalchemybiz/model/product.py
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
t_producttype = None
|
||||||
|
t_product = None
|
||||||
|
|
||||||
|
class ProductType(object):
|
||||||
|
pass
|
||||||
|
|
||||||
|
class Product(object):
|
||||||
|
pass
|
|
@ -4,6 +4,10 @@ import logging
|
||||||
from paste.deploy import appconfig
|
from paste.deploy import appconfig
|
||||||
from pylons import config
|
from pylons import config
|
||||||
|
|
||||||
|
from sqlalchemy.exceptions import NoSuchTableError
|
||||||
|
import sys
|
||||||
|
from migrate.versioning.api import db_version, version_control, upgrade
|
||||||
|
|
||||||
from pyalchemybiz.config.environment import load_environment
|
from pyalchemybiz.config.environment import load_environment
|
||||||
|
|
||||||
log = logging.getLogger(__name__)
|
log = logging.getLogger(__name__)
|
||||||
|
@ -13,10 +17,31 @@ def setup_config(command, filename, section, vars):
|
||||||
conf = appconfig('config:' + filename)
|
conf = appconfig('config:' + filename)
|
||||||
load_environment(conf.global_conf, conf.local_conf)
|
load_environment(conf.global_conf, conf.local_conf)
|
||||||
|
|
||||||
|
repoversion = int(config.get('migrate.repo.version'))
|
||||||
|
repodir = config.get('migrate.repo.dir')
|
||||||
|
dburl = config.get('sqlalchemy.default.url')
|
||||||
|
|
||||||
# Populate the DB on 'paster setup-app'
|
# Populate the DB on 'paster setup-app'
|
||||||
import pyalchemybiz.model as model
|
|
||||||
|
|
||||||
log.info("Setting up database connectivity...")
|
log.info("Setting up database connectivity...")
|
||||||
log.info("Creating tables...")
|
log.info("Desired database repository version: %d" % repoversion)
|
||||||
model.meta.metadata.create_all(bind=model.meta.engine)
|
log.info("Desired database repository directory: %s" % repodir)
|
||||||
|
|
||||||
|
try:
|
||||||
|
dbversion = int(db_version(dburl, repodir))
|
||||||
|
except NoSuchTableError:
|
||||||
|
version_control(dburl, repodir)
|
||||||
|
dbversion = int(db_version(dburl, repodir))
|
||||||
|
except Exception, e:
|
||||||
|
log.error(e)
|
||||||
|
raise e
|
||||||
|
log.info("detected db version %d" % dbversion)
|
||||||
|
if dbversion < repoversion:
|
||||||
|
upgrade(dburl, repodir, repoversion)
|
||||||
|
elif dbversion > repoversion:
|
||||||
|
log.error("The database at %s is already versioned and its version " +
|
||||||
|
"%d is greater than the required version %d",
|
||||||
|
dburl, dbversion, repoversion)
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
log.info("Successfully set up.")
|
log.info("Successfully set up.")
|
||||||
|
|
Loading…
Reference in a new issue