aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--mediagoblin/db/extratypes.py30
-rw-r--r--mediagoblin/db/migrations.py4
-rw-r--r--mediagoblin/db/models.py6
3 files changed, 35 insertions, 5 deletions
diff --git a/mediagoblin/db/extratypes.py b/mediagoblin/db/extratypes.py
index f2304af0..8e04d58d 100644
--- a/mediagoblin/db/extratypes.py
+++ b/mediagoblin/db/extratypes.py
@@ -15,6 +15,7 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
+from sqlalchemy.ext.mutable import Mutable
from sqlalchemy.types import TypeDecorator, Unicode, TEXT
import json
@@ -38,7 +39,7 @@ class PathTupleWithSlashes(TypeDecorator):
return value
-# The following class and only this one class is in very
+# The following two classes and only these two classes is in very
# large parts based on example code from sqlalchemy.
#
# The original copyright notice and license follows:
@@ -61,3 +62,30 @@ class JSONEncoded(TypeDecorator):
if value is not None:
value = json.loads(value)
return value
+
+
+class MutationDict(Mutable, dict):
+ @classmethod
+ def coerce(cls, key, value):
+ "Convert plain dictionaries to MutationDict."
+
+ if not isinstance(value, MutationDict):
+ if isinstance(value, dict):
+ return MutationDict(value)
+
+ # this call will raise ValueError
+ return Mutable.coerce(key, value)
+ else:
+ return value
+
+ def __setitem__(self, key, value):
+ "Detect dictionary set events and emit change events."
+
+ dict.__setitem__(self, key, value)
+ self.changed()
+
+ def __delitem__(self, key):
+ "Detect dictionary del events and emit change events."
+
+ dict.__delitem__(self, key)
+ self.changed()
diff --git a/mediagoblin/db/migrations.py b/mediagoblin/db/migrations.py
index a88518f4..423508f6 100644
--- a/mediagoblin/db/migrations.py
+++ b/mediagoblin/db/migrations.py
@@ -26,7 +26,7 @@ from sqlalchemy.sql import and_
from migrate.changeset.constraint import UniqueConstraint
-from mediagoblin.db.extratypes import JSONEncoded
+from mediagoblin.db.extratypes import JSONEncoded, MutationDict
from mediagoblin.db.migration_tools import RegisterMigration, inspect_table
from mediagoblin.db.models import MediaEntry, Collection, User, MediaComment
@@ -502,7 +502,7 @@ def add_file_metadata(db):
metadata = MetaData(bind=db.bind)
media_file_table = inspect_table(metadata, "core__mediafiles")
- col = Column('file_metadata', JSONEncoded)
+ col = Column('file_metadata', MutationDict.as_mutable(JSONEncoded))
col.create(media_file_table)
db.commit()
diff --git a/mediagoblin/db/models.py b/mediagoblin/db/models.py
index 809e4722..10558c5f 100644
--- a/mediagoblin/db/models.py
+++ b/mediagoblin/db/models.py
@@ -31,7 +31,8 @@ from sqlalchemy.ext.associationproxy import association_proxy
from sqlalchemy.util import memoized_property
-from mediagoblin.db.extratypes import PathTupleWithSlashes, JSONEncoded
+from mediagoblin.db.extratypes import (PathTupleWithSlashes, JSONEncoded,
+ MutationDict)
from mediagoblin.db.base import Base, DictReadAttrProxy
from mediagoblin.db.mixin import UserMixin, MediaEntryMixin, \
MediaCommentMixin, CollectionMixin, CollectionItemMixin
@@ -294,6 +295,7 @@ class MediaEntry(Base, MediaEntryMixin):
file_metadata[key] = value
media_file.file_metadata = file_metadata
+ media_file.save()
@property
def media_data(self):
@@ -391,7 +393,7 @@ class MediaFile(Base):
nullable=False)
name_id = Column(SmallInteger, ForeignKey(FileKeynames.id), nullable=False)
file_path = Column(PathTupleWithSlashes)
- file_metadata = Column(JSONEncoded)
+ file_metadata = Column(MutationDict.as_mutable(JSONEncoded))
__table_args__ = (
PrimaryKeyConstraint('media_entry', 'name_id'),