Database upgrading, revision 1.

This commit is contained in:
Raoul Snyman 2011-08-25 11:02:59 +02:00
parent 2cf0743222
commit 7e572cd0c2
4 changed files with 142 additions and 31 deletions

View File

@ -31,9 +31,9 @@ import logging
import os
from PyQt4 import QtCore
from sqlalchemy import create_engine, MetaData
from sqlalchemy import Table, MetaData, Column, types, create_engine
from sqlalchemy.exc import InvalidRequestError
from sqlalchemy.orm import scoped_session, sessionmaker
from sqlalchemy.orm import scoped_session, sessionmaker, mapper
from sqlalchemy.pool import NullPool
from openlp.core.utils import AppLocation, delete_file
@ -59,6 +59,42 @@ def init_db(url, auto_flush=True, auto_commit=False):
autocommit=auto_commit, bind=engine))
return session, metadata
def upgrade_db(url, upgrade):
"""
Upgrade a database.
``url``
The url of the database to upgrade.
``upgrade``
The python module that contains the upgrade instructions.
"""
session, metadata = init_db(url)
tables = upgrade.upgrade_setup(metadata)
metadata_table = Table(u'metadata', metadata,
Column(u'key', types.Unicode(64), primary_key=True),
Column(u'value', types.UnicodeText(), default=None)
)
metadata_table.create(checkfirst=True)
mapper(Metadata, metadata_table)
version_meta = session.query(Metadata).get(u'version')
if version_meta is None:
version_meta = Metadata.populate(key=u'version', value=u'0')
version = 0
else:
version = int(version_meta.value)
version += 1
while hasattr(upgrade, u'upgrade_%d' % version):
log.debug(u'Running upgrade_%d', version)
do_upgrade = getattr(upgrade, u'upgrade_%d' % version)
if not do_upgrade(session, metadata, tables):
break
version += 1
version_meta.value = unicode(version)
session.add(version_meta)
session.commit()
def delete_database(plugin_name, db_file_name=None):
"""
Remove a database file from the system.
@ -79,6 +115,7 @@ def delete_database(plugin_name, db_file_name=None):
AppLocation.get_section_data_path(plugin_name), plugin_name)
return delete_file(db_file_path)
class BaseModel(object):
"""
BaseModel provides a base object with a set of generic functions
@ -94,11 +131,18 @@ class BaseModel(object):
return instance
class Metadata(BaseModel):
"""
Provides a class for the metadata table.
"""
pass
class Manager(object):
"""
Provide generic object persistence management
"""
def __init__(self, plugin_name, init_schema, db_file_name=None):
def __init__(self, plugin_name, init_schema, db_file_name=None, upgrade_schema=None):
"""
Runs the initialisation process that includes creating the connection
to the database and the tables if they don't exist.
@ -109,6 +153,9 @@ class Manager(object):
``init_schema``
The init_schema function for this database
``upgrade_schema``
The upgrade_schema function for this database
``db_file_name``
The file name to use for this database. Defaults to None resulting
in the plugin_name being used.
@ -134,6 +181,8 @@ class Manager(object):
unicode(settings.value(u'db hostname').toString()),
unicode(settings.value(u'db database').toString()))
settings.endGroup()
if upgrade_schema:
upgrade_schema(self.db_url)
self.session = init_schema(self.db_url)
def save_object(self, object_instance, commit=True):

View File

@ -32,7 +32,8 @@ the Songs plugin
from sqlalchemy import Column, ForeignKey, Table, types
from sqlalchemy.orm import mapper, relation
from openlp.core.lib.db import BaseModel, init_db
from openlp.core.lib.db import BaseModel, init_db, upgrade_db
from openlp.plugins.songs.lib import upgrade
class Author(BaseModel):
"""
@ -70,6 +71,14 @@ class Topic(BaseModel):
"""
pass
def upgrade_schema(url):
"""
Upgrades the songs database.
``url``
The database to upgrade
"""
upgrade_db(url, upgrade)
def init_schema(url):
"""
@ -111,10 +120,6 @@ def init_schema(url):
* file_name
* type
**media_files_songs Table**
* media_file_id
* song_id
**song_books Table**
The *song_books* table holds a list of books that a congregation gets
their songs from, or old hymnals now no longer used. This table has the
@ -162,7 +167,7 @@ def init_schema(url):
# Definition of the "authors" table
authors_table = Table(u'authors', metadata,
Column(u'id', types.Integer, primary_key=True),
Column(u'id', types.Integer(), primary_key=True),
Column(u'first_name', types.Unicode(128)),
Column(u'last_name', types.Unicode(128)),
Column(u'display_name', types.Unicode(255), index=True, nullable=False)
@ -170,22 +175,25 @@ def init_schema(url):
# Definition of the "media_files" table
media_files_table = Table(u'media_files', metadata,
Column(u'id', types.Integer, primary_key=True),
Column(u'id', types.Integer(), primary_key=True),
Column(u'song_id', types.Integer(), ForeignKey(u'songs.id'),
default=None),
Column(u'file_name', types.Unicode(255), nullable=False),
Column(u'type', types.Unicode(64), nullable=False, default=u'audio')
Column(u'type', types.Unicode(64), nullable=False, default=u'audio'),
Column(u'weight', types.Integer(), default=0)
)
# Definition of the "song_books" table
song_books_table = Table(u'song_books', metadata,
Column(u'id', types.Integer, primary_key=True),
Column(u'id', types.Integer(), primary_key=True),
Column(u'name', types.Unicode(128), nullable=False),
Column(u'publisher', types.Unicode(128))
)
# Definition of the "songs" table
songs_table = Table(u'songs', metadata,
Column(u'id', types.Integer, primary_key=True),
Column(u'song_book_id', types.Integer,
Column(u'id', types.Integer(), primary_key=True),
Column(u'song_book_id', types.Integer(),
ForeignKey(u'song_books.id'), default=None),
Column(u'title', types.Unicode(255), nullable=False),
Column(u'alternate_title', types.Unicode(255)),
@ -202,31 +210,23 @@ def init_schema(url):
# Definition of the "topics" table
topics_table = Table(u'topics', metadata,
Column(u'id', types.Integer, primary_key=True),
Column(u'id', types.Integer(), primary_key=True),
Column(u'name', types.Unicode(128), index=True, nullable=False)
)
# Definition of the "authors_songs" table
authors_songs_table = Table(u'authors_songs', metadata,
Column(u'author_id', types.Integer,
Column(u'author_id', types.Integer(),
ForeignKey(u'authors.id'), primary_key=True),
Column(u'song_id', types.Integer,
ForeignKey(u'songs.id'), primary_key=True)
)
# Definition of the "media_files_songs" table
media_files_songs_table = Table(u'media_files_songs', metadata,
Column(u'media_file_id', types.Integer,
ForeignKey(u'media_files.id'), primary_key=True),
Column(u'song_id', types.Integer,
Column(u'song_id', types.Integer(),
ForeignKey(u'songs.id'), primary_key=True)
)
# Definition of the "songs_topics" table
songs_topics_table = Table(u'songs_topics', metadata,
Column(u'song_id', types.Integer,
Column(u'song_id', types.Integer(),
ForeignKey(u'songs.id'), primary_key=True),
Column(u'topic_id', types.Integer,
Column(u'topic_id', types.Integer(),
ForeignKey(u'topics.id'), primary_key=True)
)
@ -238,8 +238,7 @@ def init_schema(url):
'authors': relation(Author, backref='songs',
secondary=authors_songs_table, lazy=False),
'book': relation(Book, backref='songs'),
'media_files': relation(MediaFile, backref='songs',
secondary=media_files_songs_table),
'media_files': relation(MediaFile, backref='songs'),
'topics': relation(Topic, backref='songs',
secondary=songs_topics_table)
})

View File

@ -0,0 +1,63 @@
# -*- coding: utf-8 -*-
# vim: autoindent shiftwidth=4 expandtab textwidth=80 tabstop=4 softtabstop=4
###############################################################################
# OpenLP - Open Source Lyrics Projection #
# --------------------------------------------------------------------------- #
# Copyright (c) 2008-2011 Raoul Snyman #
# Portions copyright (c) 2008-2011 Tim Bentley, Gerald Britton, Jonathan #
# Corwin, Michael Gorven, Scott Guerrieri, Matthias Hub, Meinert Jordan, #
# Armin Köhler, Joshua Miller, Stevan Pettit, Andreas Preikschat, Mattias #
# Põldaru, Christian Richter, Philip Ridout, Simon Scudder, Jeffrey Smith, #
# Maikel Stuivenberg, Martin Thompson, Jon Tibble, Frode Woldsund #
# --------------------------------------------------------------------------- #
# This program is free software; you can redistribute it and/or modify it #
# under the terms of the GNU General Public License as published by the Free #
# Software Foundation; version 2 of the License. #
# #
# This program is distributed in the hope that it will be useful, but WITHOUT #
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or #
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for #
# more details. #
# #
# You should have received a copy of the GNU General Public License along #
# with this program; if not, write to the Free Software Foundation, Inc., 59 #
# Temple Place, Suite 330, Boston, MA 02111-1307 USA #
###############################################################################
"""
The :mod:`upgrade` module provides a way for the database and schema that is the backend for
the Songs plugin
"""
from sqlalchemy import Column, ForeignKey, Table, types
from migrate import changeset
from migrate.changeset.constraint import ForeignKeyConstraint
def upgrade_setup(metadata):
"""
Set up the latest revision all tables, with reflection, needed for the
upgrade process. If you want to drop a table, you need to remove it from
here, and add it to your upgrade function.
"""
tables = {
u'authors': Table(u'authors', metadata, autoload=True),
u'media_files': Table(u'media_files', metadata, autoload=True),
u'song_books': Table(u'song_books', metadata, autoload=True),
u'songs': Table(u'songs', metadata, autoload=True),
u'topics': Table(u'topics', metadata, autoload=True),
u'authors_songs': Table(u'authors_songs', metadata, autoload=True),
u'songs_topics': Table(u'songs_topics', metadata, autoload=True)
}
return tables
def upgrade_1(session, metadata, tables):
Table(u'media_files_songs', metadata, autoload=True).drop(checkfirst=True)
Column(u'song_id', types.Integer(), default=None)\
.create(table=tables[u'media_files'], populate_default=True)
Column(u'weight', types.Integer(), default=0)\
.create(table=tables[u'media_files'], populate_default=True)
if metadata.bind.url.get_dialect().name != 'sqlite':
ForeignKeyConstraint([u'song_id'], [u'songs.id'],
table=tables[u'media_files']).create()
return True

View File

@ -37,7 +37,7 @@ from openlp.core.lib.db import Manager
from openlp.core.lib.ui import UiStrings, base_action, icon_action
from openlp.core.utils.actions import ActionList
from openlp.plugins.songs.lib import clean_song, SongMediaItem, SongsTab
from openlp.plugins.songs.lib.db import init_schema, Song
from openlp.plugins.songs.lib.db import init_schema, upgrade_schema, Song
from openlp.plugins.songs.lib.importer import SongFormat
from openlp.plugins.songs.lib.olpimport import OpenLPSongImport
@ -58,8 +58,8 @@ class SongsPlugin(Plugin):
Create and set up the Songs plugin.
"""
Plugin.__init__(self, u'songs', plugin_helpers, SongMediaItem, SongsTab)
self.manager = Manager(u'songs', init_schema, upgrade_schema=upgrade_schema)
self.weight = -10
self.manager = Manager(u'songs', init_schema)
self.icon_path = u':/plugins/plugin_songs.png'
self.icon = build_icon(self.icon_path)