From 7f9d3ca7c9e4934c3ee487a0f1d10e1b6525db71 Mon Sep 17 00:00:00 2001 From: Rodney Ewing Date: Tue, 27 Aug 2013 11:40:35 -0700 Subject: [PATCH] need to use mutation tracking to detect changes in JSONEncoded types --- mediagoblin/db/extratypes.py | 30 +++++++++++++++++++++++++++++- mediagoblin/db/migrations.py | 4 ++-- mediagoblin/db/models.py | 6 ++++-- 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 . +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'), -- 2.25.1