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 import os
from PyQt4 import QtCore 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.exc import InvalidRequestError
from sqlalchemy.orm import scoped_session, sessionmaker from sqlalchemy.orm import scoped_session, sessionmaker, mapper
from sqlalchemy.pool import NullPool from sqlalchemy.pool import NullPool
from openlp.core.utils import AppLocation, delete_file 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)) autocommit=auto_commit, bind=engine))
return session, metadata 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): def delete_database(plugin_name, db_file_name=None):
""" """
Remove a database file from the system. 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) AppLocation.get_section_data_path(plugin_name), plugin_name)
return delete_file(db_file_path) return delete_file(db_file_path)
class BaseModel(object): class BaseModel(object):
""" """
BaseModel provides a base object with a set of generic functions BaseModel provides a base object with a set of generic functions
@ -94,11 +131,18 @@ class BaseModel(object):
return instance return instance
class Metadata(BaseModel):
"""
Provides a class for the metadata table.
"""
pass
class Manager(object): class Manager(object):
""" """
Provide generic object persistence management 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 Runs the initialisation process that includes creating the connection
to the database and the tables if they don't exist. to the database and the tables if they don't exist.
@ -109,6 +153,9 @@ class Manager(object):
``init_schema`` ``init_schema``
The init_schema function for this database The init_schema function for this database
``upgrade_schema``
The upgrade_schema function for this database
``db_file_name`` ``db_file_name``
The file name to use for this database. Defaults to None resulting The file name to use for this database. Defaults to None resulting
in the plugin_name being used. 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 hostname').toString()),
unicode(settings.value(u'db database').toString())) unicode(settings.value(u'db database').toString()))
settings.endGroup() settings.endGroup()
if upgrade_schema:
upgrade_schema(self.db_url)
self.session = init_schema(self.db_url) self.session = init_schema(self.db_url)
def save_object(self, object_instance, commit=True): 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 import Column, ForeignKey, Table, types
from sqlalchemy.orm import mapper, relation 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): class Author(BaseModel):
""" """
@ -70,6 +71,14 @@ class Topic(BaseModel):
""" """
pass pass
def upgrade_schema(url):
"""
Upgrades the songs database.
``url``
The database to upgrade
"""
upgrade_db(url, upgrade)
def init_schema(url): def init_schema(url):
""" """
@ -111,10 +120,6 @@ def init_schema(url):
* file_name * file_name
* type * type
**media_files_songs Table**
* media_file_id
* song_id
**song_books Table** **song_books Table**
The *song_books* table holds a list of books that a congregation gets 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 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 # Definition of the "authors" table
authors_table = Table(u'authors', metadata, 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'first_name', types.Unicode(128)),
Column(u'last_name', types.Unicode(128)), Column(u'last_name', types.Unicode(128)),
Column(u'display_name', types.Unicode(255), index=True, nullable=False) 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 # Definition of the "media_files" table
media_files_table = Table(u'media_files', metadata, 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'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 # Definition of the "song_books" table
song_books_table = Table(u'song_books', metadata, 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'name', types.Unicode(128), nullable=False),
Column(u'publisher', types.Unicode(128)) Column(u'publisher', types.Unicode(128))
) )
# Definition of the "songs" table # Definition of the "songs" table
songs_table = Table(u'songs', metadata, songs_table = Table(u'songs', metadata,
Column(u'id', types.Integer, primary_key=True), Column(u'id', types.Integer(), primary_key=True),
Column(u'song_book_id', types.Integer, Column(u'song_book_id', types.Integer(),
ForeignKey(u'song_books.id'), default=None), ForeignKey(u'song_books.id'), default=None),
Column(u'title', types.Unicode(255), nullable=False), Column(u'title', types.Unicode(255), nullable=False),
Column(u'alternate_title', types.Unicode(255)), Column(u'alternate_title', types.Unicode(255)),
@ -202,31 +210,23 @@ def init_schema(url):
# Definition of the "topics" table # Definition of the "topics" table
topics_table = Table(u'topics', metadata, 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) Column(u'name', types.Unicode(128), index=True, nullable=False)
) )
# Definition of the "authors_songs" table # Definition of the "authors_songs" table
authors_songs_table = Table(u'authors_songs', metadata, 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), ForeignKey(u'authors.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 "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,
ForeignKey(u'songs.id'), primary_key=True) ForeignKey(u'songs.id'), primary_key=True)
) )
# Definition of the "songs_topics" table # Definition of the "songs_topics" table
songs_topics_table = Table(u'songs_topics', metadata, 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), 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) ForeignKey(u'topics.id'), primary_key=True)
) )
@ -238,8 +238,7 @@ def init_schema(url):
'authors': relation(Author, backref='songs', 'authors': relation(Author, backref='songs',
secondary=authors_songs_table, lazy=False), secondary=authors_songs_table, lazy=False),
'book': relation(Book, backref='songs'), 'book': relation(Book, backref='songs'),
'media_files': relation(MediaFile, backref='songs', 'media_files': relation(MediaFile, backref='songs'),
secondary=media_files_songs_table),
'topics': relation(Topic, backref='songs', 'topics': relation(Topic, backref='songs',
secondary=songs_topics_table) 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.lib.ui import UiStrings, base_action, icon_action
from openlp.core.utils.actions import ActionList from openlp.core.utils.actions import ActionList
from openlp.plugins.songs.lib import clean_song, SongMediaItem, SongsTab 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.importer import SongFormat
from openlp.plugins.songs.lib.olpimport import OpenLPSongImport from openlp.plugins.songs.lib.olpimport import OpenLPSongImport
@ -58,8 +58,8 @@ class SongsPlugin(Plugin):
Create and set up the Songs plugin. Create and set up the Songs plugin.
""" """
Plugin.__init__(self, u'songs', plugin_helpers, SongMediaItem, SongsTab) Plugin.__init__(self, u'songs', plugin_helpers, SongMediaItem, SongsTab)
self.manager = Manager(u'songs', init_schema, upgrade_schema=upgrade_schema)
self.weight = -10 self.weight = -10
self.manager = Manager(u'songs', init_schema)
self.icon_path = u':/plugins/plugin_songs.png' self.icon_path = u':/plugins/plugin_songs.png'
self.icon = build_icon(self.icon_path) self.icon = build_icon(self.icon_path)