need to use mutation tracking to detect changes in JSONEncoded types
authorRodney Ewing <ewing.rj@gmail.com>
Tue, 27 Aug 2013 18:40:35 +0000 (11:40 -0700)
committerRodney Ewing <ewing.rj@gmail.com>
Thu, 19 Sep 2013 15:12:09 +0000 (08:12 -0700)
mediagoblin/db/extratypes.py
mediagoblin/db/migrations.py
mediagoblin/db/models.py

index f2304af0b5fd0bcb0bc40ab667d19c2bf812b945..8e04d58da96898562dc6449efae3bf50d15ba224 100644 (file)
@@ -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()
index a88518f45c8b38d918c2d6f5df75cc5cd32e5e32..423508f6bfa5afef70fad03ba2822d593203f8d3 100644 (file)
@@ -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()
index 809e47220dd6b6901b2361b8d563ca045ec7caac..10558c5f40066d4a521c948680f16c5a9ab902ae 100644 (file)
@@ -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'),