From 70b44584ae4a81e53d39481781c63aec23b23884 Mon Sep 17 00:00:00 2001 From: Christopher Allan Webber Date: Thu, 29 Dec 2011 11:15:55 -0600 Subject: [PATCH] Big ol' start of the SQL migrations system. Things definitely don't work yet, but should be heading in the right direction. --- mediagoblin/db/sql/migrations.py | 17 +++++ mediagoblin/db/sql/models.py | 21 +++++++ mediagoblin/db/sql/util.py | 59 +++++++++++++++++ mediagoblin/gmg_commands/dbupdate.py | 84 +++++++++++++++++++++++++ mediagoblin/media_types/image/models.py | 17 +++++ mediagoblin/media_types/video/models.py | 16 +++++ 6 files changed, 214 insertions(+) create mode 100644 mediagoblin/db/sql/migrations.py create mode 100644 mediagoblin/db/sql/util.py create mode 100644 mediagoblin/gmg_commands/dbupdate.py create mode 100644 mediagoblin/media_types/image/models.py create mode 100644 mediagoblin/media_types/video/models.py diff --git a/mediagoblin/db/sql/migrations.py b/mediagoblin/db/sql/migrations.py new file mode 100644 index 00000000..67a02c96 --- /dev/null +++ b/mediagoblin/db/sql/migrations.py @@ -0,0 +1,17 @@ +# GNU MediaGoblin -- federated, autonomous media hosting +# Copyright (C) 2011 MediaGoblin contributors. See AUTHORS. +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# 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 Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . + +MIGRATIONS = [] diff --git a/mediagoblin/db/sql/models.py b/mediagoblin/db/sql/models.py index 9abd8ec7..3573bc3f 100644 --- a/mediagoblin/db/sql/models.py +++ b/mediagoblin/db/sql/models.py @@ -216,6 +216,27 @@ class MediaComment(Base): get_author = relationship(User) +MODELS = [ + User, MediaEntry, Tag, MediaTag, MediaComment] + + +###################################################### +# Special, migrations-tracking table +# +# Not listed in MODELS because this is special and not +# really migrated, but used for migrations (for now) +###################################################### + +class MigrationData(Base): + __tablename__ = "migrations" + + id = Column(Integer, primary_key=True) + name = Column(Unicode, nullable=False, unique=True) + version = Column(Integer, nullable=False, default=0) + +###################################################### + + def show_table_init(): from sqlalchemy import create_engine engine = create_engine('sqlite:///:memory:', echo=True) diff --git a/mediagoblin/db/sql/util.py b/mediagoblin/db/sql/util.py new file mode 100644 index 00000000..a0cea111 --- /dev/null +++ b/mediagoblin/db/sql/util.py @@ -0,0 +1,59 @@ +# GNU MediaGoblin -- federated, autonomous media hosting +# Copyright (C) 2011 MediaGoblin contributors. See AUTHORS. +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# 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 Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . + +class MigrationManager(object): + pass + + +class RegisterMigration(object): + """ + Tool for registering migrations + + Call like: + + @RegisterMigration(33) + def update_dwarves(database): + [...] + + This will register your migration with the default migration + registry. Alternately, to specify a very specific + migration_registry, you can pass in that as the second argument. + + Note, the number of your migration should NEVER be 0 or less than + 0. 0 is the default "no migrations" state! + """ + def __init__(self, migration_number, migration_registry): + assert migration_number > 0, "Migration number must be > 0!" + assert migration_number not in migration_registry, \ + "Duplicate migration numbers detected! That's not allowed!" + + self.migration_number = migration_number + self.migration_registry = migration_registry + + def __call__(self, migration): + self.migration_registry[self.migration_number] = migration + return migration + + +def assure_migrations_table_setup(db): + """ + Make sure the migrations table is set up in the database. + """ + from mediagoblin.db.sql.models import MigrationData + + if not MigrationData.__table__.exists(db): + MigrationData.metadata.create_all( + db, tables=[MigrationData.__table__]) diff --git a/mediagoblin/gmg_commands/dbupdate.py b/mediagoblin/gmg_commands/dbupdate.py new file mode 100644 index 00000000..52819c6d --- /dev/null +++ b/mediagoblin/gmg_commands/dbupdate.py @@ -0,0 +1,84 @@ +# GNU MediaGoblin -- federated, autonomous media hosting +# Copyright (C) 2011 MediaGoblin contributors. See AUTHORS. +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# 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 Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . + +from mediagoblin.db.sql.open import setup_connection_and_db_from_config +from mediagoblin.db.sql.util import ( + MigrationManager, assure_migrations_table_setup) +from mediagoblin.init import setup_global_and_app_config +from mediagoblin.tools.common import import_component + + +class DatabaseData(object): + def __init__(self, name, models, migrations): + self.name = name + self.models = models + self.migrations = migrations + + def make_migration_manager(self, db): + return MigrationManager( + self.name, self.models, self.migrations, db) + + +def gather_database_data(self, media_types): + """ + Gather all database data relevant to the extensions we have + installed so we can do migrations and table initialization. + + Returns a list of DatabaseData objects. + """ + managed_dbdata = [] + + # Add main first + from mediagoblin.db.sql.models import MODELS as MAIN_MODELS + from mediagoblin.db.sql.migrations import MIGRATIONS as MAIN_MIGRATIONS + + managed_dbdata.append( + DatabaseData( + '__main__', MAIN_MODELS, MAIN_MIGRATIONS)) + + # Then get all registered media managers (eventually, plugins) + for media_type in media_types: + models = import_component('%s.models:MODELS' % media_type) + migrations = import_component('%s.migrations:MIGRATIONS' % media_type) + managed_dbdata.append( + DatabaseData(media_type, models, migrations)) + + return managed_dbdata + + +def dbupdate(args): + """ + Initialize or migrate the database as specified by the config file. + + Will also initialize or migrate all extensions (media types, and + in the future, plugins) + """ + globa_config, app_config = setup_global_and_app_config(args.conf_file) + + # Gather information from all media managers / projects + dbdatas = gather_database_data(app_config['media_types']) + + # Set up the database + connection, db = setup_connection_and_db_from_config(app_config) + + # If migrations table not made, make it! + assure_migrations_table_setup(db) + + # Setup media managers for all dbdata, run init/migrate and print info + # For each component, create/migrate tables + for dbdata in dbdatas: + migration_manager = dbdata.make_migration_manager(db) + migration_manager.init_or_migrate() diff --git a/mediagoblin/media_types/image/models.py b/mediagoblin/media_types/image/models.py new file mode 100644 index 00000000..96b5cdf2 --- /dev/null +++ b/mediagoblin/media_types/image/models.py @@ -0,0 +1,17 @@ +from mediagoblin.db.sql.models import Base + +from sqlalchemy import ( + Column, Integer, Unicode, UnicodeText, DateTime, Boolean, ForeignKey, + UniqueConstraint) + + +class ImageData(Base): + __tablename__ = "image__data" + + id = Column(Integer, primary_key=True) + width = Column(Integer) + height = Column(Integer) + + +DATA_MODEL = ImageData +MODELS = [ImageData] diff --git a/mediagoblin/media_types/video/models.py b/mediagoblin/media_types/video/models.py new file mode 100644 index 00000000..c44f1919 --- /dev/null +++ b/mediagoblin/media_types/video/models.py @@ -0,0 +1,16 @@ +from mediagoblin.db.sql.models import Base + +from sqlalchemy import ( + Column, Integer, Unicode, UnicodeText, DateTime, Boolean, ForeignKey, + UniqueConstraint) + + +class VideoData(Base): + __tablename__ = "video__data" + + id = Column(Integer, primary_key=True) + integer + + +DATA_MODEL = VideoData +MODELS = [VideoData] -- 2.25.1