From b0c8328e547288028e7e43f0ceb1fa9f7c8dac4a Mon Sep 17 00:00:00 2001 From: Sebastian Spaeth Date: Fri, 30 Nov 2012 10:10:35 +0100 Subject: Move db.sql.models* to db.models* --- docs/source/siteadmin/relnotes.rst | 3 + mediagoblin/admin/views.py | 2 +- mediagoblin/auth/views.py | 2 +- mediagoblin/db/migrations.py | 2 +- mediagoblin/db/models.py | 447 ++++++++++++++++++++++++++++++++ mediagoblin/db/models_v0.py | 320 +++++++++++++++++++++++ mediagoblin/db/open.py | 2 +- mediagoblin/db/sql/models.py | 447 -------------------------------- mediagoblin/db/sql/models_v0.py | 320 ----------------------- mediagoblin/db/util.py | 6 +- mediagoblin/decorators.py | 2 +- mediagoblin/gmg_commands/dbupdate.py | 2 +- mediagoblin/listings/views.py | 2 +- mediagoblin/plugins/oauth/migrations.py | 2 +- mediagoblin/plugins/oauth/models.py | 2 +- mediagoblin/processing/task.py | 2 +- mediagoblin/tests/test_auth.py | 2 +- mediagoblin/tests/test_tests.py | 2 +- mediagoblin/tools/request.py | 2 +- mediagoblin/user_pages/views.py | 2 +- mediagoblin/views.py | 2 +- 21 files changed, 788 insertions(+), 785 deletions(-) create mode 100644 mediagoblin/db/models.py create mode 100644 mediagoblin/db/models_v0.py delete mode 100644 mediagoblin/db/sql/models.py delete mode 100644 mediagoblin/db/sql/models_v0.py diff --git a/docs/source/siteadmin/relnotes.rst b/docs/source/siteadmin/relnotes.rst index 9b45f642..7d480d90 100644 --- a/docs/source/siteadmin/relnotes.rst +++ b/docs/source/siteadmin/relnotes.rst @@ -26,6 +26,9 @@ WIP **Other changed** +* Plugin writers: Internal restructuring led to mediagoblin.db.sql* be + mediagoblin.db.* starting from 0.3.3 + * Dependency list has been reduced not requireing the "webob" package anymore. 0.3.2 diff --git a/mediagoblin/admin/views.py b/mediagoblin/admin/views.py index 073b1e25..22ca74a3 100644 --- a/mediagoblin/admin/views.py +++ b/mediagoblin/admin/views.py @@ -16,7 +16,7 @@ from werkzeug.exceptions import Forbidden -from mediagoblin.db.sql.models import MediaEntry +from mediagoblin.db.models import MediaEntry from mediagoblin.decorators import require_active_login from mediagoblin.tools.response import render_to_response diff --git a/mediagoblin/auth/views.py b/mediagoblin/auth/views.py index efd3e018..43354135 100644 --- a/mediagoblin/auth/views.py +++ b/mediagoblin/auth/views.py @@ -18,7 +18,7 @@ import uuid import datetime from mediagoblin import messages, mg_globals -from mediagoblin.db.sql.models import User +from mediagoblin.db.models import User from mediagoblin.tools.response import render_to_response, redirect, render_404 from mediagoblin.tools.translate import pass_to_ugettext as _ from mediagoblin.auth import lib as auth_lib diff --git a/mediagoblin/db/migrations.py b/mediagoblin/db/migrations.py index 3f0cb6f0..476e2a06 100644 --- a/mediagoblin/db/migrations.py +++ b/mediagoblin/db/migrations.py @@ -24,7 +24,7 @@ from sqlalchemy.ext.declarative import declarative_base from migrate.changeset.constraint import UniqueConstraint from mediagoblin.db.util import RegisterMigration -from mediagoblin.db.sql.models import MediaEntry, Collection, User +from mediagoblin.db.models import MediaEntry, Collection, User MIGRATIONS = {} diff --git a/mediagoblin/db/models.py b/mediagoblin/db/models.py new file mode 100644 index 00000000..4450e38d --- /dev/null +++ b/mediagoblin/db/models.py @@ -0,0 +1,447 @@ +# GNU MediaGoblin -- federated, autonomous media hosting +# Copyright (C) 2011, 2012 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 . + +""" +TODO: indexes on foreignkeys, where useful. +""" + + +import datetime +import sys + +from sqlalchemy import Column, Integer, Unicode, UnicodeText, DateTime, \ + Boolean, ForeignKey, UniqueConstraint, PrimaryKeyConstraint, \ + SmallInteger +from sqlalchemy.orm import relationship, backref +from sqlalchemy.orm.collections import attribute_mapped_collection +from sqlalchemy.sql.expression import desc +from sqlalchemy.ext.associationproxy import association_proxy +from sqlalchemy.util import memoized_property + +from mediagoblin.db.sql.extratypes import PathTupleWithSlashes, JSONEncoded +from mediagoblin.db.sql.base import Base, DictReadAttrProxy +from mediagoblin.db.mixin import UserMixin, MediaEntryMixin, MediaCommentMixin, CollectionMixin, CollectionItemMixin +from mediagoblin.db.sql.base import Session + +# It's actually kind of annoying how sqlalchemy-migrate does this, if +# I understand it right, but whatever. Anyway, don't remove this :P +# +# We could do migration calls more manually instead of relying on +# this import-based meddling... +from migrate import changeset + + +class User(Base, UserMixin): + """ + TODO: We should consider moving some rarely used fields + into some sort of "shadow" table. + """ + __tablename__ = "core__users" + + id = Column(Integer, primary_key=True) + username = Column(Unicode, nullable=False, unique=True) + email = Column(Unicode, nullable=False) + created = Column(DateTime, nullable=False, default=datetime.datetime.now) + pw_hash = Column(Unicode, nullable=False) + email_verified = Column(Boolean, default=False) + status = Column(Unicode, default=u"needs_email_verification", nullable=False) + # Intented to be nullable=False, but migrations would not work for it + # set to nullable=True implicitly. + wants_comment_notification = Column(Boolean, default=True) + verification_key = Column(Unicode) + is_admin = Column(Boolean, default=False, nullable=False) + url = Column(Unicode) + bio = Column(UnicodeText) # ?? + fp_verification_key = Column(Unicode) + fp_token_expire = Column(DateTime) + + ## TODO + # plugin data would be in a separate model + + def __repr__(self): + return '<{0} #{1} {2} {3} "{4}">'.format( + self.__class__.__name__, + self.id, + 'verified' if self.email_verified else 'non-verified', + 'admin' if self.is_admin else 'user', + self.username) + + +class MediaEntry(Base, MediaEntryMixin): + """ + TODO: Consider fetching the media_files using join + """ + __tablename__ = "core__media_entries" + + id = Column(Integer, primary_key=True) + uploader = Column(Integer, ForeignKey(User.id), nullable=False, index=True) + title = Column(Unicode, nullable=False) + slug = Column(Unicode) + created = Column(DateTime, nullable=False, default=datetime.datetime.now, + index=True) + description = Column(UnicodeText) # ?? + media_type = Column(Unicode, nullable=False) + state = Column(Unicode, default=u'unprocessed', nullable=False) + # or use sqlalchemy.types.Enum? + license = Column(Unicode) + collected = Column(Integer, default=0) + + fail_error = Column(Unicode) + fail_metadata = Column(JSONEncoded) + + transcoding_progress = Column(SmallInteger) + + queued_media_file = Column(PathTupleWithSlashes) + + queued_task_id = Column(Unicode) + + __table_args__ = ( + UniqueConstraint('uploader', 'slug'), + {}) + + get_uploader = relationship(User) + + media_files_helper = relationship("MediaFile", + collection_class=attribute_mapped_collection("name"), + cascade="all, delete-orphan" + ) + media_files = association_proxy('media_files_helper', 'file_path', + creator=lambda k, v: MediaFile(name=k, file_path=v) + ) + + attachment_files_helper = relationship("MediaAttachmentFile", + cascade="all, delete-orphan", + order_by="MediaAttachmentFile.created" + ) + attachment_files = association_proxy("attachment_files_helper", "dict_view", + creator=lambda v: MediaAttachmentFile( + name=v["name"], filepath=v["filepath"]) + ) + + tags_helper = relationship("MediaTag", + cascade="all, delete-orphan" + ) + tags = association_proxy("tags_helper", "dict_view", + creator=lambda v: MediaTag(name=v["name"], slug=v["slug"]) + ) + + collections_helper = relationship("CollectionItem", + cascade="all, delete-orphan" + ) + collections = association_proxy("collections_helper", "in_collection") + + ## TODO + # media_data + # fail_error + + def get_comments(self, ascending=False): + order_col = MediaComment.created + if not ascending: + order_col = desc(order_col) + return MediaComment.query.filter_by( + media_entry=self.id).order_by(order_col) + + def url_to_prev(self, urlgen): + """get the next 'newer' entry by this user""" + media = MediaEntry.query.filter( + (MediaEntry.uploader == self.uploader) + & (MediaEntry.state == u'processed') + & (MediaEntry.id > self.id)).order_by(MediaEntry.id).first() + + if media is not None: + return media.url_for_self(urlgen) + + def url_to_next(self, urlgen): + """get the next 'older' entry by this user""" + media = MediaEntry.query.filter( + (MediaEntry.uploader == self.uploader) + & (MediaEntry.state == u'processed') + & (MediaEntry.id < self.id)).order_by(desc(MediaEntry.id)).first() + + if media is not None: + return media.url_for_self(urlgen) + + #@memoized_property + @property + def media_data(self): + session = Session() + + return session.query(self.media_data_table).filter_by( + media_entry=self.id).first() + + def media_data_init(self, **kwargs): + """ + Initialize or update the contents of a media entry's media_data row + """ + session = Session() + + media_data = session.query(self.media_data_table).filter_by( + media_entry=self.id).first() + + # No media data, so actually add a new one + if media_data is None: + media_data = self.media_data_table( + media_entry=self.id, + **kwargs) + session.add(media_data) + # Update old media data + else: + for field, value in kwargs.iteritems(): + setattr(media_data, field, value) + + @memoized_property + def media_data_table(self): + # TODO: memoize this + models_module = self.media_type + '.models' + __import__(models_module) + return sys.modules[models_module].DATA_MODEL + + def __repr__(self): + safe_title = self.title.encode('ascii', 'replace') + + return '<{classname} {id}: {title}>'.format( + classname=self.__class__.__name__, + id=self.id, + title=safe_title) + + +class FileKeynames(Base): + """ + keywords for various places. + currently the MediaFile keys + """ + __tablename__ = "core__file_keynames" + id = Column(Integer, primary_key=True) + name = Column(Unicode, unique=True) + + def __repr__(self): + return "" % (self.id, self.name) + + @classmethod + def find_or_new(cls, name): + t = cls.query.filter_by(name=name).first() + if t is not None: + return t + return cls(name=name) + + +class MediaFile(Base): + """ + TODO: Highly consider moving "name" into a new table. + TODO: Consider preloading said table in software + """ + __tablename__ = "core__mediafiles" + + media_entry = Column( + Integer, ForeignKey(MediaEntry.id), + nullable=False) + name_id = Column(SmallInteger, ForeignKey(FileKeynames.id), nullable=False) + file_path = Column(PathTupleWithSlashes) + + __table_args__ = ( + PrimaryKeyConstraint('media_entry', 'name_id'), + {}) + + def __repr__(self): + return "" % (self.name, self.file_path) + + name_helper = relationship(FileKeynames, lazy="joined", innerjoin=True) + name = association_proxy('name_helper', 'name', + creator=FileKeynames.find_or_new + ) + + +class MediaAttachmentFile(Base): + __tablename__ = "core__attachment_files" + + id = Column(Integer, primary_key=True) + media_entry = Column( + Integer, ForeignKey(MediaEntry.id), + nullable=False) + name = Column(Unicode, nullable=False) + filepath = Column(PathTupleWithSlashes) + created = Column(DateTime, nullable=False, default=datetime.datetime.now) + + @property + def dict_view(self): + """A dict like view on this object""" + return DictReadAttrProxy(self) + + +class Tag(Base): + __tablename__ = "core__tags" + + id = Column(Integer, primary_key=True) + slug = Column(Unicode, nullable=False, unique=True) + + def __repr__(self): + return "" % (self.id, self.slug) + + @classmethod + def find_or_new(cls, slug): + t = cls.query.filter_by(slug=slug).first() + if t is not None: + return t + return cls(slug=slug) + + +class MediaTag(Base): + __tablename__ = "core__media_tags" + + id = Column(Integer, primary_key=True) + media_entry = Column( + Integer, ForeignKey(MediaEntry.id), + nullable=False, index=True) + tag = Column(Integer, ForeignKey(Tag.id), nullable=False, index=True) + name = Column(Unicode) + # created = Column(DateTime, nullable=False, default=datetime.datetime.now) + + __table_args__ = ( + UniqueConstraint('tag', 'media_entry'), + {}) + + tag_helper = relationship(Tag) + slug = association_proxy('tag_helper', 'slug', + creator=Tag.find_or_new + ) + + def __init__(self, name=None, slug=None): + Base.__init__(self) + if name is not None: + self.name = name + if slug is not None: + self.tag_helper = Tag.find_or_new(slug) + + @property + def dict_view(self): + """A dict like view on this object""" + return DictReadAttrProxy(self) + + +class MediaComment(Base, MediaCommentMixin): + __tablename__ = "core__media_comments" + + id = Column(Integer, primary_key=True) + media_entry = Column( + Integer, ForeignKey(MediaEntry.id), nullable=False, index=True) + author = Column(Integer, ForeignKey(User.id), nullable=False) + created = Column(DateTime, nullable=False, default=datetime.datetime.now) + content = Column(UnicodeText, nullable=False) + + get_author = relationship(User) + + +class Collection(Base, CollectionMixin): + __tablename__ = "core__collections" + + id = Column(Integer, primary_key=True) + title = Column(Unicode, nullable=False) + slug = Column(Unicode) + created = Column(DateTime, nullable=False, default=datetime.datetime.now, + index=True) + description = Column(UnicodeText) + creator = Column(Integer, ForeignKey(User.id), nullable=False) + items = Column(Integer, default=0) + + get_creator = relationship(User) + + def get_collection_items(self, ascending=False): + order_col = CollectionItem.position + if not ascending: + order_col = desc(order_col) + return CollectionItem.query.filter_by( + collection=self.id).order_by(order_col) + + +class CollectionItem(Base, CollectionItemMixin): + __tablename__ = "core__collection_items" + + id = Column(Integer, primary_key=True) + media_entry = Column( + Integer, ForeignKey(MediaEntry.id), nullable=False, index=True) + collection = Column(Integer, ForeignKey(Collection.id), nullable=False) + note = Column(UnicodeText, nullable=True) + added = Column(DateTime, nullable=False, default=datetime.datetime.now) + position = Column(Integer) + in_collection = relationship("Collection") + + get_media_entry = relationship(MediaEntry) + + __table_args__ = ( + UniqueConstraint('collection', 'media_entry'), + {}) + + @property + def dict_view(self): + """A dict like view on this object""" + return DictReadAttrProxy(self) + + +class ProcessingMetaData(Base): + __tablename__ = 'core__processing_metadata' + + id = Column(Integer, primary_key=True) + media_entry_id = Column(Integer, ForeignKey(MediaEntry.id), nullable=False, + index=True) + media_entry = relationship(MediaEntry, + backref=backref('processing_metadata', + cascade='all, delete-orphan')) + callback_url = Column(Unicode) + + @property + def dict_view(self): + """A dict like view on this object""" + return DictReadAttrProxy(self) + + +MODELS = [ + User, MediaEntry, Tag, MediaTag, MediaComment, Collection, CollectionItem, MediaFile, FileKeynames, + MediaAttachmentFile, ProcessingMetaData] + + +###################################################### +# 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__ = "core__migrations" + + name = Column(Unicode, primary_key=True) + version = Column(Integer, nullable=False, default=0) + +###################################################### + + +def show_table_init(engine_uri): + if engine_uri is None: + engine_uri = 'sqlite:///:memory:' + from sqlalchemy import create_engine + engine = create_engine(engine_uri, echo=True) + + Base.metadata.create_all(engine) + + +if __name__ == '__main__': + from sys import argv + print repr(argv) + if len(argv) == 2: + uri = argv[1] + else: + uri = None + show_table_init(uri) diff --git a/mediagoblin/db/models_v0.py b/mediagoblin/db/models_v0.py new file mode 100644 index 00000000..06f87d28 --- /dev/null +++ b/mediagoblin/db/models_v0.py @@ -0,0 +1,320 @@ +# GNU MediaGoblin -- federated, autonomous media hosting +# Copyright (C) 2011, 2012 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 . + +""" +TODO: indexes on foreignkeys, where useful. +""" + + +import datetime +import sys + +from sqlalchemy import ( + Column, Integer, Unicode, UnicodeText, DateTime, Boolean, ForeignKey, + UniqueConstraint, PrimaryKeyConstraint, SmallInteger, Float) +from sqlalchemy.ext.declarative import declarative_base +from sqlalchemy.orm import relationship, backref +from sqlalchemy.orm.collections import attribute_mapped_collection +from sqlalchemy.ext.associationproxy import association_proxy +from sqlalchemy.util import memoized_property + +from mediagoblin.db.sql.extratypes import PathTupleWithSlashes, JSONEncoded +from mediagoblin.db.sql.base import GMGTableBase +from mediagoblin.db.sql.base import Session + + +Base_v0 = declarative_base(cls=GMGTableBase) + + +class User(Base_v0): + """ + TODO: We should consider moving some rarely used fields + into some sort of "shadow" table. + """ + __tablename__ = "core__users" + + id = Column(Integer, primary_key=True) + username = Column(Unicode, nullable=False, unique=True) + email = Column(Unicode, nullable=False) + created = Column(DateTime, nullable=False, default=datetime.datetime.now) + pw_hash = Column(Unicode, nullable=False) + email_verified = Column(Boolean, default=False) + status = Column(Unicode, default=u"needs_email_verification", nullable=False) + verification_key = Column(Unicode) + is_admin = Column(Boolean, default=False, nullable=False) + url = Column(Unicode) + bio = Column(UnicodeText) # ?? + fp_verification_key = Column(Unicode) + fp_token_expire = Column(DateTime) + + ## TODO + # plugin data would be in a separate model + + +class MediaEntry(Base_v0): + """ + TODO: Consider fetching the media_files using join + """ + __tablename__ = "core__media_entries" + + id = Column(Integer, primary_key=True) + uploader = Column(Integer, ForeignKey(User.id), nullable=False, index=True) + title = Column(Unicode, nullable=False) + slug = Column(Unicode) + created = Column(DateTime, nullable=False, default=datetime.datetime.now, + index=True) + description = Column(UnicodeText) # ?? + media_type = Column(Unicode, nullable=False) + state = Column(Unicode, default=u'unprocessed', nullable=False) + # or use sqlalchemy.types.Enum? + license = Column(Unicode) + + fail_error = Column(Unicode) + fail_metadata = Column(JSONEncoded) + + queued_media_file = Column(PathTupleWithSlashes) + + queued_task_id = Column(Unicode) + + __table_args__ = ( + UniqueConstraint('uploader', 'slug'), + {}) + + get_uploader = relationship(User) + + media_files_helper = relationship("MediaFile", + collection_class=attribute_mapped_collection("name"), + cascade="all, delete-orphan" + ) + + attachment_files_helper = relationship("MediaAttachmentFile", + cascade="all, delete-orphan", + order_by="MediaAttachmentFile.created" + ) + + tags_helper = relationship("MediaTag", + cascade="all, delete-orphan" + ) + + def media_data_init(self, **kwargs): + """ + Initialize or update the contents of a media entry's media_data row + """ + session = Session() + + media_data = session.query(self.media_data_table).filter_by( + media_entry=self.id).first() + + # No media data, so actually add a new one + if media_data is None: + media_data = self.media_data_table( + media_entry=self.id, + **kwargs) + session.add(media_data) + # Update old media data + else: + for field, value in kwargs.iteritems(): + setattr(media_data, field, value) + + @memoized_property + def media_data_table(self): + # TODO: memoize this + models_module = self.media_type + '.models' + __import__(models_module) + return sys.modules[models_module].DATA_MODEL + + +class FileKeynames(Base_v0): + """ + keywords for various places. + currently the MediaFile keys + """ + __tablename__ = "core__file_keynames" + id = Column(Integer, primary_key=True) + name = Column(Unicode, unique=True) + + def __repr__(self): + return "" % (self.id, self.name) + + @classmethod + def find_or_new(cls, name): + t = cls.query.filter_by(name=name).first() + if t is not None: + return t + return cls(name=name) + + +class MediaFile(Base_v0): + """ + TODO: Highly consider moving "name" into a new table. + TODO: Consider preloading said table in software + """ + __tablename__ = "core__mediafiles" + + media_entry = Column( + Integer, ForeignKey(MediaEntry.id), + nullable=False) + name_id = Column(SmallInteger, ForeignKey(FileKeynames.id), nullable=False) + file_path = Column(PathTupleWithSlashes) + + __table_args__ = ( + PrimaryKeyConstraint('media_entry', 'name_id'), + {}) + + def __repr__(self): + return "" % (self.name, self.file_path) + + name_helper = relationship(FileKeynames, lazy="joined", innerjoin=True) + name = association_proxy('name_helper', 'name', + creator=FileKeynames.find_or_new + ) + + +class MediaAttachmentFile(Base_v0): + __tablename__ = "core__attachment_files" + + id = Column(Integer, primary_key=True) + media_entry = Column( + Integer, ForeignKey(MediaEntry.id), + nullable=False) + name = Column(Unicode, nullable=False) + filepath = Column(PathTupleWithSlashes) + created = Column(DateTime, nullable=False, default=datetime.datetime.now) + + +class Tag(Base_v0): + __tablename__ = "core__tags" + + id = Column(Integer, primary_key=True) + slug = Column(Unicode, nullable=False, unique=True) + + def __repr__(self): + return "" % (self.id, self.slug) + + @classmethod + def find_or_new(cls, slug): + t = cls.query.filter_by(slug=slug).first() + if t is not None: + return t + return cls(slug=slug) + + +class MediaTag(Base_v0): + __tablename__ = "core__media_tags" + + id = Column(Integer, primary_key=True) + media_entry = Column( + Integer, ForeignKey(MediaEntry.id), + nullable=False, index=True) + tag = Column(Integer, ForeignKey(Tag.id), nullable=False, index=True) + name = Column(Unicode) + # created = Column(DateTime, nullable=False, default=datetime.datetime.now) + + __table_args__ = ( + UniqueConstraint('tag', 'media_entry'), + {}) + + tag_helper = relationship(Tag) + slug = association_proxy('tag_helper', 'slug', + creator=Tag.find_or_new + ) + + def __init__(self, name=None, slug=None): + Base_v0.__init__(self) + if name is not None: + self.name = name + if slug is not None: + self.tag_helper = Tag.find_or_new(slug) + + +class MediaComment(Base_v0): + __tablename__ = "core__media_comments" + + id = Column(Integer, primary_key=True) + media_entry = Column( + Integer, ForeignKey(MediaEntry.id), nullable=False, index=True) + author = Column(Integer, ForeignKey(User.id), nullable=False) + created = Column(DateTime, nullable=False, default=datetime.datetime.now) + content = Column(UnicodeText, nullable=False) + + get_author = relationship(User) + + +class ImageData(Base_v0): + __tablename__ = "image__mediadata" + + # The primary key *and* reference to the main media_entry + media_entry = Column(Integer, ForeignKey('core__media_entries.id'), + primary_key=True) + get_media_entry = relationship("MediaEntry", + backref=backref("image__media_data", cascade="all, delete-orphan")) + + width = Column(Integer) + height = Column(Integer) + exif_all = Column(JSONEncoded) + gps_longitude = Column(Float) + gps_latitude = Column(Float) + gps_altitude = Column(Float) + gps_direction = Column(Float) + + +class VideoData(Base_v0): + __tablename__ = "video__mediadata" + + # The primary key *and* reference to the main media_entry + media_entry = Column(Integer, ForeignKey('core__media_entries.id'), + primary_key=True) + get_media_entry = relationship("MediaEntry", + backref=backref("video__media_data", cascade="all, delete-orphan")) + + width = Column(SmallInteger) + height = Column(SmallInteger) + + +class AsciiData(Base_v0): + __tablename__ = "ascii__mediadata" + + # The primary key *and* reference to the main media_entry + media_entry = Column(Integer, ForeignKey('core__media_entries.id'), + primary_key=True) + get_media_entry = relationship("MediaEntry", + backref=backref("ascii__media_data", cascade="all, delete-orphan")) + + +class AudioData(Base_v0): + __tablename__ = "audio__mediadata" + + # The primary key *and* reference to the main media_entry + media_entry = Column(Integer, ForeignKey('core__media_entries.id'), + primary_key=True) + get_media_entry = relationship("MediaEntry", + backref=backref("audio__media_data", cascade="all, delete-orphan")) + + +###################################################### +# 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_v0): + __tablename__ = "core__migrations" + + name = Column(Unicode, primary_key=True) + version = Column(Integer, nullable=False, default=0) + +###################################################### diff --git a/mediagoblin/db/open.py b/mediagoblin/db/open.py index 801c92ea..11b2dc97 100644 --- a/mediagoblin/db/open.py +++ b/mediagoblin/db/open.py @@ -50,7 +50,7 @@ class DatabaseMaster(object): def load_models(app_config): - import mediagoblin.db.sql.models + import mediagoblin.db.models for media_type in app_config['media_types']: _log.debug("Loading %s.models", media_type) diff --git a/mediagoblin/db/sql/models.py b/mediagoblin/db/sql/models.py deleted file mode 100644 index 4450e38d..00000000 --- a/mediagoblin/db/sql/models.py +++ /dev/null @@ -1,447 +0,0 @@ -# GNU MediaGoblin -- federated, autonomous media hosting -# Copyright (C) 2011, 2012 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 . - -""" -TODO: indexes on foreignkeys, where useful. -""" - - -import datetime -import sys - -from sqlalchemy import Column, Integer, Unicode, UnicodeText, DateTime, \ - Boolean, ForeignKey, UniqueConstraint, PrimaryKeyConstraint, \ - SmallInteger -from sqlalchemy.orm import relationship, backref -from sqlalchemy.orm.collections import attribute_mapped_collection -from sqlalchemy.sql.expression import desc -from sqlalchemy.ext.associationproxy import association_proxy -from sqlalchemy.util import memoized_property - -from mediagoblin.db.sql.extratypes import PathTupleWithSlashes, JSONEncoded -from mediagoblin.db.sql.base import Base, DictReadAttrProxy -from mediagoblin.db.mixin import UserMixin, MediaEntryMixin, MediaCommentMixin, CollectionMixin, CollectionItemMixin -from mediagoblin.db.sql.base import Session - -# It's actually kind of annoying how sqlalchemy-migrate does this, if -# I understand it right, but whatever. Anyway, don't remove this :P -# -# We could do migration calls more manually instead of relying on -# this import-based meddling... -from migrate import changeset - - -class User(Base, UserMixin): - """ - TODO: We should consider moving some rarely used fields - into some sort of "shadow" table. - """ - __tablename__ = "core__users" - - id = Column(Integer, primary_key=True) - username = Column(Unicode, nullable=False, unique=True) - email = Column(Unicode, nullable=False) - created = Column(DateTime, nullable=False, default=datetime.datetime.now) - pw_hash = Column(Unicode, nullable=False) - email_verified = Column(Boolean, default=False) - status = Column(Unicode, default=u"needs_email_verification", nullable=False) - # Intented to be nullable=False, but migrations would not work for it - # set to nullable=True implicitly. - wants_comment_notification = Column(Boolean, default=True) - verification_key = Column(Unicode) - is_admin = Column(Boolean, default=False, nullable=False) - url = Column(Unicode) - bio = Column(UnicodeText) # ?? - fp_verification_key = Column(Unicode) - fp_token_expire = Column(DateTime) - - ## TODO - # plugin data would be in a separate model - - def __repr__(self): - return '<{0} #{1} {2} {3} "{4}">'.format( - self.__class__.__name__, - self.id, - 'verified' if self.email_verified else 'non-verified', - 'admin' if self.is_admin else 'user', - self.username) - - -class MediaEntry(Base, MediaEntryMixin): - """ - TODO: Consider fetching the media_files using join - """ - __tablename__ = "core__media_entries" - - id = Column(Integer, primary_key=True) - uploader = Column(Integer, ForeignKey(User.id), nullable=False, index=True) - title = Column(Unicode, nullable=False) - slug = Column(Unicode) - created = Column(DateTime, nullable=False, default=datetime.datetime.now, - index=True) - description = Column(UnicodeText) # ?? - media_type = Column(Unicode, nullable=False) - state = Column(Unicode, default=u'unprocessed', nullable=False) - # or use sqlalchemy.types.Enum? - license = Column(Unicode) - collected = Column(Integer, default=0) - - fail_error = Column(Unicode) - fail_metadata = Column(JSONEncoded) - - transcoding_progress = Column(SmallInteger) - - queued_media_file = Column(PathTupleWithSlashes) - - queued_task_id = Column(Unicode) - - __table_args__ = ( - UniqueConstraint('uploader', 'slug'), - {}) - - get_uploader = relationship(User) - - media_files_helper = relationship("MediaFile", - collection_class=attribute_mapped_collection("name"), - cascade="all, delete-orphan" - ) - media_files = association_proxy('media_files_helper', 'file_path', - creator=lambda k, v: MediaFile(name=k, file_path=v) - ) - - attachment_files_helper = relationship("MediaAttachmentFile", - cascade="all, delete-orphan", - order_by="MediaAttachmentFile.created" - ) - attachment_files = association_proxy("attachment_files_helper", "dict_view", - creator=lambda v: MediaAttachmentFile( - name=v["name"], filepath=v["filepath"]) - ) - - tags_helper = relationship("MediaTag", - cascade="all, delete-orphan" - ) - tags = association_proxy("tags_helper", "dict_view", - creator=lambda v: MediaTag(name=v["name"], slug=v["slug"]) - ) - - collections_helper = relationship("CollectionItem", - cascade="all, delete-orphan" - ) - collections = association_proxy("collections_helper", "in_collection") - - ## TODO - # media_data - # fail_error - - def get_comments(self, ascending=False): - order_col = MediaComment.created - if not ascending: - order_col = desc(order_col) - return MediaComment.query.filter_by( - media_entry=self.id).order_by(order_col) - - def url_to_prev(self, urlgen): - """get the next 'newer' entry by this user""" - media = MediaEntry.query.filter( - (MediaEntry.uploader == self.uploader) - & (MediaEntry.state == u'processed') - & (MediaEntry.id > self.id)).order_by(MediaEntry.id).first() - - if media is not None: - return media.url_for_self(urlgen) - - def url_to_next(self, urlgen): - """get the next 'older' entry by this user""" - media = MediaEntry.query.filter( - (MediaEntry.uploader == self.uploader) - & (MediaEntry.state == u'processed') - & (MediaEntry.id < self.id)).order_by(desc(MediaEntry.id)).first() - - if media is not None: - return media.url_for_self(urlgen) - - #@memoized_property - @property - def media_data(self): - session = Session() - - return session.query(self.media_data_table).filter_by( - media_entry=self.id).first() - - def media_data_init(self, **kwargs): - """ - Initialize or update the contents of a media entry's media_data row - """ - session = Session() - - media_data = session.query(self.media_data_table).filter_by( - media_entry=self.id).first() - - # No media data, so actually add a new one - if media_data is None: - media_data = self.media_data_table( - media_entry=self.id, - **kwargs) - session.add(media_data) - # Update old media data - else: - for field, value in kwargs.iteritems(): - setattr(media_data, field, value) - - @memoized_property - def media_data_table(self): - # TODO: memoize this - models_module = self.media_type + '.models' - __import__(models_module) - return sys.modules[models_module].DATA_MODEL - - def __repr__(self): - safe_title = self.title.encode('ascii', 'replace') - - return '<{classname} {id}: {title}>'.format( - classname=self.__class__.__name__, - id=self.id, - title=safe_title) - - -class FileKeynames(Base): - """ - keywords for various places. - currently the MediaFile keys - """ - __tablename__ = "core__file_keynames" - id = Column(Integer, primary_key=True) - name = Column(Unicode, unique=True) - - def __repr__(self): - return "" % (self.id, self.name) - - @classmethod - def find_or_new(cls, name): - t = cls.query.filter_by(name=name).first() - if t is not None: - return t - return cls(name=name) - - -class MediaFile(Base): - """ - TODO: Highly consider moving "name" into a new table. - TODO: Consider preloading said table in software - """ - __tablename__ = "core__mediafiles" - - media_entry = Column( - Integer, ForeignKey(MediaEntry.id), - nullable=False) - name_id = Column(SmallInteger, ForeignKey(FileKeynames.id), nullable=False) - file_path = Column(PathTupleWithSlashes) - - __table_args__ = ( - PrimaryKeyConstraint('media_entry', 'name_id'), - {}) - - def __repr__(self): - return "" % (self.name, self.file_path) - - name_helper = relationship(FileKeynames, lazy="joined", innerjoin=True) - name = association_proxy('name_helper', 'name', - creator=FileKeynames.find_or_new - ) - - -class MediaAttachmentFile(Base): - __tablename__ = "core__attachment_files" - - id = Column(Integer, primary_key=True) - media_entry = Column( - Integer, ForeignKey(MediaEntry.id), - nullable=False) - name = Column(Unicode, nullable=False) - filepath = Column(PathTupleWithSlashes) - created = Column(DateTime, nullable=False, default=datetime.datetime.now) - - @property - def dict_view(self): - """A dict like view on this object""" - return DictReadAttrProxy(self) - - -class Tag(Base): - __tablename__ = "core__tags" - - id = Column(Integer, primary_key=True) - slug = Column(Unicode, nullable=False, unique=True) - - def __repr__(self): - return "" % (self.id, self.slug) - - @classmethod - def find_or_new(cls, slug): - t = cls.query.filter_by(slug=slug).first() - if t is not None: - return t - return cls(slug=slug) - - -class MediaTag(Base): - __tablename__ = "core__media_tags" - - id = Column(Integer, primary_key=True) - media_entry = Column( - Integer, ForeignKey(MediaEntry.id), - nullable=False, index=True) - tag = Column(Integer, ForeignKey(Tag.id), nullable=False, index=True) - name = Column(Unicode) - # created = Column(DateTime, nullable=False, default=datetime.datetime.now) - - __table_args__ = ( - UniqueConstraint('tag', 'media_entry'), - {}) - - tag_helper = relationship(Tag) - slug = association_proxy('tag_helper', 'slug', - creator=Tag.find_or_new - ) - - def __init__(self, name=None, slug=None): - Base.__init__(self) - if name is not None: - self.name = name - if slug is not None: - self.tag_helper = Tag.find_or_new(slug) - - @property - def dict_view(self): - """A dict like view on this object""" - return DictReadAttrProxy(self) - - -class MediaComment(Base, MediaCommentMixin): - __tablename__ = "core__media_comments" - - id = Column(Integer, primary_key=True) - media_entry = Column( - Integer, ForeignKey(MediaEntry.id), nullable=False, index=True) - author = Column(Integer, ForeignKey(User.id), nullable=False) - created = Column(DateTime, nullable=False, default=datetime.datetime.now) - content = Column(UnicodeText, nullable=False) - - get_author = relationship(User) - - -class Collection(Base, CollectionMixin): - __tablename__ = "core__collections" - - id = Column(Integer, primary_key=True) - title = Column(Unicode, nullable=False) - slug = Column(Unicode) - created = Column(DateTime, nullable=False, default=datetime.datetime.now, - index=True) - description = Column(UnicodeText) - creator = Column(Integer, ForeignKey(User.id), nullable=False) - items = Column(Integer, default=0) - - get_creator = relationship(User) - - def get_collection_items(self, ascending=False): - order_col = CollectionItem.position - if not ascending: - order_col = desc(order_col) - return CollectionItem.query.filter_by( - collection=self.id).order_by(order_col) - - -class CollectionItem(Base, CollectionItemMixin): - __tablename__ = "core__collection_items" - - id = Column(Integer, primary_key=True) - media_entry = Column( - Integer, ForeignKey(MediaEntry.id), nullable=False, index=True) - collection = Column(Integer, ForeignKey(Collection.id), nullable=False) - note = Column(UnicodeText, nullable=True) - added = Column(DateTime, nullable=False, default=datetime.datetime.now) - position = Column(Integer) - in_collection = relationship("Collection") - - get_media_entry = relationship(MediaEntry) - - __table_args__ = ( - UniqueConstraint('collection', 'media_entry'), - {}) - - @property - def dict_view(self): - """A dict like view on this object""" - return DictReadAttrProxy(self) - - -class ProcessingMetaData(Base): - __tablename__ = 'core__processing_metadata' - - id = Column(Integer, primary_key=True) - media_entry_id = Column(Integer, ForeignKey(MediaEntry.id), nullable=False, - index=True) - media_entry = relationship(MediaEntry, - backref=backref('processing_metadata', - cascade='all, delete-orphan')) - callback_url = Column(Unicode) - - @property - def dict_view(self): - """A dict like view on this object""" - return DictReadAttrProxy(self) - - -MODELS = [ - User, MediaEntry, Tag, MediaTag, MediaComment, Collection, CollectionItem, MediaFile, FileKeynames, - MediaAttachmentFile, ProcessingMetaData] - - -###################################################### -# 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__ = "core__migrations" - - name = Column(Unicode, primary_key=True) - version = Column(Integer, nullable=False, default=0) - -###################################################### - - -def show_table_init(engine_uri): - if engine_uri is None: - engine_uri = 'sqlite:///:memory:' - from sqlalchemy import create_engine - engine = create_engine(engine_uri, echo=True) - - Base.metadata.create_all(engine) - - -if __name__ == '__main__': - from sys import argv - print repr(argv) - if len(argv) == 2: - uri = argv[1] - else: - uri = None - show_table_init(uri) diff --git a/mediagoblin/db/sql/models_v0.py b/mediagoblin/db/sql/models_v0.py deleted file mode 100644 index 06f87d28..00000000 --- a/mediagoblin/db/sql/models_v0.py +++ /dev/null @@ -1,320 +0,0 @@ -# GNU MediaGoblin -- federated, autonomous media hosting -# Copyright (C) 2011, 2012 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 . - -""" -TODO: indexes on foreignkeys, where useful. -""" - - -import datetime -import sys - -from sqlalchemy import ( - Column, Integer, Unicode, UnicodeText, DateTime, Boolean, ForeignKey, - UniqueConstraint, PrimaryKeyConstraint, SmallInteger, Float) -from sqlalchemy.ext.declarative import declarative_base -from sqlalchemy.orm import relationship, backref -from sqlalchemy.orm.collections import attribute_mapped_collection -from sqlalchemy.ext.associationproxy import association_proxy -from sqlalchemy.util import memoized_property - -from mediagoblin.db.sql.extratypes import PathTupleWithSlashes, JSONEncoded -from mediagoblin.db.sql.base import GMGTableBase -from mediagoblin.db.sql.base import Session - - -Base_v0 = declarative_base(cls=GMGTableBase) - - -class User(Base_v0): - """ - TODO: We should consider moving some rarely used fields - into some sort of "shadow" table. - """ - __tablename__ = "core__users" - - id = Column(Integer, primary_key=True) - username = Column(Unicode, nullable=False, unique=True) - email = Column(Unicode, nullable=False) - created = Column(DateTime, nullable=False, default=datetime.datetime.now) - pw_hash = Column(Unicode, nullable=False) - email_verified = Column(Boolean, default=False) - status = Column(Unicode, default=u"needs_email_verification", nullable=False) - verification_key = Column(Unicode) - is_admin = Column(Boolean, default=False, nullable=False) - url = Column(Unicode) - bio = Column(UnicodeText) # ?? - fp_verification_key = Column(Unicode) - fp_token_expire = Column(DateTime) - - ## TODO - # plugin data would be in a separate model - - -class MediaEntry(Base_v0): - """ - TODO: Consider fetching the media_files using join - """ - __tablename__ = "core__media_entries" - - id = Column(Integer, primary_key=True) - uploader = Column(Integer, ForeignKey(User.id), nullable=False, index=True) - title = Column(Unicode, nullable=False) - slug = Column(Unicode) - created = Column(DateTime, nullable=False, default=datetime.datetime.now, - index=True) - description = Column(UnicodeText) # ?? - media_type = Column(Unicode, nullable=False) - state = Column(Unicode, default=u'unprocessed', nullable=False) - # or use sqlalchemy.types.Enum? - license = Column(Unicode) - - fail_error = Column(Unicode) - fail_metadata = Column(JSONEncoded) - - queued_media_file = Column(PathTupleWithSlashes) - - queued_task_id = Column(Unicode) - - __table_args__ = ( - UniqueConstraint('uploader', 'slug'), - {}) - - get_uploader = relationship(User) - - media_files_helper = relationship("MediaFile", - collection_class=attribute_mapped_collection("name"), - cascade="all, delete-orphan" - ) - - attachment_files_helper = relationship("MediaAttachmentFile", - cascade="all, delete-orphan", - order_by="MediaAttachmentFile.created" - ) - - tags_helper = relationship("MediaTag", - cascade="all, delete-orphan" - ) - - def media_data_init(self, **kwargs): - """ - Initialize or update the contents of a media entry's media_data row - """ - session = Session() - - media_data = session.query(self.media_data_table).filter_by( - media_entry=self.id).first() - - # No media data, so actually add a new one - if media_data is None: - media_data = self.media_data_table( - media_entry=self.id, - **kwargs) - session.add(media_data) - # Update old media data - else: - for field, value in kwargs.iteritems(): - setattr(media_data, field, value) - - @memoized_property - def media_data_table(self): - # TODO: memoize this - models_module = self.media_type + '.models' - __import__(models_module) - return sys.modules[models_module].DATA_MODEL - - -class FileKeynames(Base_v0): - """ - keywords for various places. - currently the MediaFile keys - """ - __tablename__ = "core__file_keynames" - id = Column(Integer, primary_key=True) - name = Column(Unicode, unique=True) - - def __repr__(self): - return "" % (self.id, self.name) - - @classmethod - def find_or_new(cls, name): - t = cls.query.filter_by(name=name).first() - if t is not None: - return t - return cls(name=name) - - -class MediaFile(Base_v0): - """ - TODO: Highly consider moving "name" into a new table. - TODO: Consider preloading said table in software - """ - __tablename__ = "core__mediafiles" - - media_entry = Column( - Integer, ForeignKey(MediaEntry.id), - nullable=False) - name_id = Column(SmallInteger, ForeignKey(FileKeynames.id), nullable=False) - file_path = Column(PathTupleWithSlashes) - - __table_args__ = ( - PrimaryKeyConstraint('media_entry', 'name_id'), - {}) - - def __repr__(self): - return "" % (self.name, self.file_path) - - name_helper = relationship(FileKeynames, lazy="joined", innerjoin=True) - name = association_proxy('name_helper', 'name', - creator=FileKeynames.find_or_new - ) - - -class MediaAttachmentFile(Base_v0): - __tablename__ = "core__attachment_files" - - id = Column(Integer, primary_key=True) - media_entry = Column( - Integer, ForeignKey(MediaEntry.id), - nullable=False) - name = Column(Unicode, nullable=False) - filepath = Column(PathTupleWithSlashes) - created = Column(DateTime, nullable=False, default=datetime.datetime.now) - - -class Tag(Base_v0): - __tablename__ = "core__tags" - - id = Column(Integer, primary_key=True) - slug = Column(Unicode, nullable=False, unique=True) - - def __repr__(self): - return "" % (self.id, self.slug) - - @classmethod - def find_or_new(cls, slug): - t = cls.query.filter_by(slug=slug).first() - if t is not None: - return t - return cls(slug=slug) - - -class MediaTag(Base_v0): - __tablename__ = "core__media_tags" - - id = Column(Integer, primary_key=True) - media_entry = Column( - Integer, ForeignKey(MediaEntry.id), - nullable=False, index=True) - tag = Column(Integer, ForeignKey(Tag.id), nullable=False, index=True) - name = Column(Unicode) - # created = Column(DateTime, nullable=False, default=datetime.datetime.now) - - __table_args__ = ( - UniqueConstraint('tag', 'media_entry'), - {}) - - tag_helper = relationship(Tag) - slug = association_proxy('tag_helper', 'slug', - creator=Tag.find_or_new - ) - - def __init__(self, name=None, slug=None): - Base_v0.__init__(self) - if name is not None: - self.name = name - if slug is not None: - self.tag_helper = Tag.find_or_new(slug) - - -class MediaComment(Base_v0): - __tablename__ = "core__media_comments" - - id = Column(Integer, primary_key=True) - media_entry = Column( - Integer, ForeignKey(MediaEntry.id), nullable=False, index=True) - author = Column(Integer, ForeignKey(User.id), nullable=False) - created = Column(DateTime, nullable=False, default=datetime.datetime.now) - content = Column(UnicodeText, nullable=False) - - get_author = relationship(User) - - -class ImageData(Base_v0): - __tablename__ = "image__mediadata" - - # The primary key *and* reference to the main media_entry - media_entry = Column(Integer, ForeignKey('core__media_entries.id'), - primary_key=True) - get_media_entry = relationship("MediaEntry", - backref=backref("image__media_data", cascade="all, delete-orphan")) - - width = Column(Integer) - height = Column(Integer) - exif_all = Column(JSONEncoded) - gps_longitude = Column(Float) - gps_latitude = Column(Float) - gps_altitude = Column(Float) - gps_direction = Column(Float) - - -class VideoData(Base_v0): - __tablename__ = "video__mediadata" - - # The primary key *and* reference to the main media_entry - media_entry = Column(Integer, ForeignKey('core__media_entries.id'), - primary_key=True) - get_media_entry = relationship("MediaEntry", - backref=backref("video__media_data", cascade="all, delete-orphan")) - - width = Column(SmallInteger) - height = Column(SmallInteger) - - -class AsciiData(Base_v0): - __tablename__ = "ascii__mediadata" - - # The primary key *and* reference to the main media_entry - media_entry = Column(Integer, ForeignKey('core__media_entries.id'), - primary_key=True) - get_media_entry = relationship("MediaEntry", - backref=backref("ascii__media_data", cascade="all, delete-orphan")) - - -class AudioData(Base_v0): - __tablename__ = "audio__mediadata" - - # The primary key *and* reference to the main media_entry - media_entry = Column(Integer, ForeignKey('core__media_entries.id'), - primary_key=True) - get_media_entry = relationship("MediaEntry", - backref=backref("audio__media_data", cascade="all, delete-orphan")) - - -###################################################### -# 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_v0): - __tablename__ = "core__migrations" - - name = Column(Unicode, primary_key=True) - version = Column(Integer, nullable=False, default=0) - -###################################################### diff --git a/mediagoblin/db/util.py b/mediagoblin/db/util.py index 2f167e45..32dd0884 100644 --- a/mediagoblin/db/util.py +++ b/mediagoblin/db/util.py @@ -15,7 +15,7 @@ # along with this program. If not, see . import sys from mediagoblin.db.sql.base import Session -from mediagoblin.db.sql.models import MediaEntry, Tag, MediaTag, Collection +from mediagoblin.db.models import MediaEntry, Tag, MediaTag, Collection from mediagoblin.tools.common import simple_printer @@ -45,7 +45,7 @@ class MigrationManager(object): self.printer = printer # For convenience - from mediagoblin.db.sql.models import MigrationData + from mediagoblin.db.models import MigrationData self.migration_model = MigrationData self.migration_table = MigrationData.__table__ @@ -259,7 +259,7 @@ def assure_migrations_table_setup(db): """ Make sure the migrations table is set up in the database. """ - from mediagoblin.db.sql.models import MigrationData + from mediagoblin.db.models import MigrationData if not MigrationData.__table__.exists(db.bind): MigrationData.metadata.create_all( diff --git a/mediagoblin/decorators.py b/mediagoblin/decorators.py index f6169060..4ae1b867 100644 --- a/mediagoblin/decorators.py +++ b/mediagoblin/decorators.py @@ -20,7 +20,7 @@ from urlparse import urljoin from werkzeug.exceptions import Forbidden from werkzeug.urls import url_quote -from mediagoblin.db.sql.models import MediaEntry, User +from mediagoblin.db.models import MediaEntry, User from mediagoblin.tools.response import redirect, render_404 diff --git a/mediagoblin/gmg_commands/dbupdate.py b/mediagoblin/gmg_commands/dbupdate.py index 95898c08..5151ba9d 100644 --- a/mediagoblin/gmg_commands/dbupdate.py +++ b/mediagoblin/gmg_commands/dbupdate.py @@ -52,7 +52,7 @@ def gather_database_data(media_types, plugins): managed_dbdata = [] # Add main first - from mediagoblin.db.sql.models import MODELS as MAIN_MODELS + from mediagoblin.db.models import MODELS as MAIN_MODELS from mediagoblin.db.migrations import MIGRATIONS as MAIN_MIGRATIONS managed_dbdata.append( diff --git a/mediagoblin/listings/views.py b/mediagoblin/listings/views.py index 80182124..d37161fc 100644 --- a/mediagoblin/listings/views.py +++ b/mediagoblin/listings/views.py @@ -14,7 +14,7 @@ # You should have received a copy of the GNU Affero General Public License # along with this program. If not, see . -from mediagoblin.db.sql.models import MediaEntry +from mediagoblin.db.models import MediaEntry from mediagoblin.db.util import media_entries_for_tag_slug from mediagoblin.tools.pagination import Pagination from mediagoblin.tools.response import render_to_response diff --git a/mediagoblin/plugins/oauth/migrations.py b/mediagoblin/plugins/oauth/migrations.py index 3beef087..7b028faf 100644 --- a/mediagoblin/plugins/oauth/migrations.py +++ b/mediagoblin/plugins/oauth/migrations.py @@ -20,7 +20,7 @@ from sqlalchemy import (MetaData, Table, Column, from sqlalchemy.ext.declarative import declarative_base from mediagoblin.db.util import RegisterMigration -from mediagoblin.db.sql.models import User +from mediagoblin.db.models import User MIGRATIONS = {} diff --git a/mediagoblin/plugins/oauth/models.py b/mediagoblin/plugins/oauth/models.py index 7e247c1a..f52a8ce9 100644 --- a/mediagoblin/plugins/oauth/models.py +++ b/mediagoblin/plugins/oauth/models.py @@ -20,7 +20,7 @@ import bcrypt from datetime import datetime, timedelta from mediagoblin.db.sql.base import Base -from mediagoblin.db.sql.models import User +from mediagoblin.db.models import User from sqlalchemy import ( Column, Unicode, Integer, DateTime, ForeignKey, Enum) diff --git a/mediagoblin/processing/task.py b/mediagoblin/processing/task.py index b7e761f2..b29de9bd 100644 --- a/mediagoblin/processing/task.py +++ b/mediagoblin/processing/task.py @@ -19,7 +19,7 @@ import logging from celery.task import Task from mediagoblin import mg_globals as mgg -from mediagoblin.db.sql.models import MediaEntry +from mediagoblin.db.models import MediaEntry from mediagoblin.processing import mark_entry_failed, BaseProcessingFail from mediagoblin.tools.processing import json_processing_callback diff --git a/mediagoblin/tests/test_auth.py b/mediagoblin/tests/test_auth.py index 169b2309..f4a31a81 100644 --- a/mediagoblin/tests/test_auth.py +++ b/mediagoblin/tests/test_auth.py @@ -21,7 +21,7 @@ from nose.tools import assert_equal from mediagoblin import mg_globals from mediagoblin.auth import lib as auth_lib -from mediagoblin.db.sql.models import User +from mediagoblin.db.models import User from mediagoblin.tests.tools import setup_fresh_app, fixture_add_user from mediagoblin.tools import template, mail diff --git a/mediagoblin/tests/test_tests.py b/mediagoblin/tests/test_tests.py index 2228d15a..b11dc730 100644 --- a/mediagoblin/tests/test_tests.py +++ b/mediagoblin/tests/test_tests.py @@ -16,7 +16,7 @@ from mediagoblin import mg_globals from mediagoblin.tests.tools import get_test_app -from mediagoblin.db.sql.models import User +from mediagoblin.db.models import User def test_get_test_app_wipes_db(): diff --git a/mediagoblin/tools/request.py b/mediagoblin/tools/request.py index f40ab360..f7311fac 100644 --- a/mediagoblin/tools/request.py +++ b/mediagoblin/tools/request.py @@ -15,7 +15,7 @@ # along with this program. If not, see . import logging -from mediagoblin.db.sql.models import User +from mediagoblin.db.models import User _log = logging.getLogger(__name__) diff --git a/mediagoblin/user_pages/views.py b/mediagoblin/user_pages/views.py index 1e5ea3a3..fd9692f5 100644 --- a/mediagoblin/user_pages/views.py +++ b/mediagoblin/user_pages/views.py @@ -18,7 +18,7 @@ import logging import datetime from mediagoblin import messages, mg_globals -from mediagoblin.db.sql.models import (MediaEntry, Collection, CollectionItem, +from mediagoblin.db.models import (MediaEntry, Collection, CollectionItem, User) from mediagoblin.tools.response import render_to_response, render_404, redirect from mediagoblin.tools.translate import pass_to_ugettext as _ diff --git a/mediagoblin/views.py b/mediagoblin/views.py index 14fbc4e6..6acd7e96 100644 --- a/mediagoblin/views.py +++ b/mediagoblin/views.py @@ -15,7 +15,7 @@ # along with this program. If not, see . from mediagoblin import mg_globals -from mediagoblin.db.sql.models import MediaEntry +from mediagoblin.db.models import MediaEntry from mediagoblin.tools.pagination import Pagination from mediagoblin.tools.response import render_to_response from mediagoblin.decorators import uses_pagination -- cgit v1.2.3