Extend taggable-mixin to support different Tag models.
authorPawel Solyga <Pawel.Solyga@gmail.com>
Mon, 01 Jun 2009 22:23:46 +0200
changeset 2376 feec28b50f1b
parent 2375 b9b203dc96d8
child 2377 8f3771ff383b
Extend taggable-mixin to support different Tag models. Usage is pretty simple. Tag model is default in Taggable constructor but you can call it with different model like GHOPTaskType that inherits from Tag model. Both Taggable and Tag models have been updated and they don't use hardcoded Tag model anymore and instead use cls of class methods or self.__class__. In case of Taggable it's self.__tag_model.
app/taggable-mixin/taggable.py
--- a/app/taggable-mixin/taggable.py	Mon Jun 01 22:17:38 2009 +0200
+++ b/app/taggable-mixin/taggable.py	Mon Jun 01 22:23:46 2009 +0200
@@ -16,9 +16,9 @@
     tagged_count = db.IntegerProperty(default=0)
     "The number of entities in tagged."
 
-    @staticmethod
-    def __key_name(tag_name):
-        return "tag_%s" % tag_name
+    @classmethod
+    def __key_name(cls, tag_name):
+        return cls.__name__ + '_' + tag_name
     
     def remove_tagged(self, key):
         def remove_tagged_txn():
@@ -27,7 +27,7 @@
                 self.tagged_count -= 1
                 self.put()
         db.run_in_transaction(remove_tagged_txn)
-        Tag.expire_cached_tags()
+        self.__class__.expire_cached_tags()
 
     def add_tagged(self, key):
         def add_tagged_txn():
@@ -36,7 +36,7 @@
                 self.tagged_count += 1
                 self.put()
         db.run_in_transaction(add_tagged_txn)
-        Tag.expire_cached_tags()
+        self.__class__.expire_cached_tags()
     
     def clear_tagged(self):
         def clear_tagged_txn():
@@ -44,27 +44,27 @@
             self.tagged_count = 0
             self.put()
         db.run_in_transaction(clear_tagged_txn)
-        Tag.expire_cached_tags()
+        self.__class__.expire_cached_tags()
         
     @classmethod
     def get_by_name(cls, tag_name):
-        return Tag.get_by_key_name(Tag.__key_name(tag_name))
+        return cls.get_by_key_name(cls.__key_name(tag_name))
     
     @classmethod
     def get_tags_for_key(cls, key):
         "Set the tags for the datastore object represented by key."
-        tags = db.Query(Tag).filter('tagged =', key).fetch(1000)
+        tags = db.Query(cls).filter('tagged =', key).fetch(1000)
         return tags
     
     @classmethod
     def get_or_create(cls, tag_name):
         "Get the Tag object that has the tag value given by tag_value."
-        tag_key_name = Tag.__key_name(tag_name)
-        existing_tag = Tag.get_by_key_name(tag_key_name)
+        tag_key_name = cls.__key_name(tag_name)
+        existing_tag = cls.get_by_key_name(tag_key_name)
         if existing_tag is None:
             # The tag does not yet exist, so create it.
             def create_tag_txn():
-                new_tag = Tag(key_name=tag_key_name, tag = tag_name)
+                new_tag = cls(key_name=tag_key_name, tag=tag_name)
                 new_tag.put()
                 return new_tag
             existing_tag = db.run_in_transaction(create_tag_txn)
@@ -75,7 +75,7 @@
         """Return a list of Tags sorted by the number of objects to which they have been applied,
         most frequently-used first.  If limit is given, return only that many tags; otherwise,
         return all."""
-        tag_list = db.Query(Tag).filter('tagged_count >', 0).order("-tagged_count").fetch(limit)
+        tag_list = db.Query(cls).filter('tagged_count >', 0).order("-tagged_count").fetch(limit)
             
         return tag_list
 
@@ -87,7 +87,7 @@
 
         from google.appengine.api import memcache
 
-        cache_name = 'tags_by_name'
+        cache_name = cls.__name__ + '_tags_by_name'
         if ascending:
             cache_name += '_asc'
         else:
@@ -99,7 +99,7 @@
             if not ascending:
                 order_by = "-tag"
             
-            tags = db.Query(Tag).order(order_by).fetch(limit)
+            tags = db.Query(cls).order(order_by).fetch(limit)
             memcache.add(cache_name, tags, 3600)
         else:
             if len(tags) > limit:
@@ -113,10 +113,10 @@
     def popular_tags(cls, limit=5):
         from google.appengine.api import memcache
         
-        tags = memcache.get('popular_tags')
+        tags = memcache.get(cls.__name__ + '_popular_tags')
         if tags is None:
-            tags = Tag.get_tags_by_frequency(limit)
-            memcache.add('popular_tags', tags, 3600)
+            tags = cls.get_tags_by_frequency(limit)
+            memcache.add(cls.__name__ + '_popular_tags', tags, 3600)
         
         return tags
 
@@ -124,12 +124,12 @@
     def expire_cached_tags(cls):
         from google.appengine.api import memcache
         
-        memcache.delete('popular_tags')
-        memcache.delete('tags_by_name_asc')
-        memcache.delete('tags_by_name_desc')
+        memcache.delete(cls.__name__ + '_popular_tags')
+        memcache.delete(cls.__name__ + '_tags_by_name_asc')
+        memcache.delete(cls.__name__ + '_tags_by_name_desc')
 
 class Taggable:
-    """A mixin class that is used for making Google AppEnigne Model classes taggable.
+    """A mixin class that is used for making Google AppEngine Model classes taggable.
         Usage:
             class Post(db.Model, taggable.Taggable):
                 body = db.TextProperty(required = True)
@@ -142,8 +142,9 @@
                     taggable.Taggable.__init__(self)
     """
     
-    def __init__(self):
+    def __init__(self, tag_model = Tag):
         self.__tags = None
+        self.__tag_model = tag_model
         self.tag_separator = ","
         """The string that is used to separate individual tags in a string
         representation of a list of tags.  Used by tags_string() to join the tags
@@ -153,7 +154,7 @@
     def __get_tags(self):
         "Get a List of Tag objects for all Tags that apply to this object."
         if self.__tags is None or len(self.__tags) == 0:
-            self.__tags = Tag.get_tags_for_key(self.key())
+            self.__tags = self.__tag_model.get_tags_for_key(self.key())
         return self.__tags
 
     def __set_tags(self, tags):
@@ -182,7 +183,7 @@
                     # A tag that was not previously assigned to this entity
                     # is present in the list that is being assigned, so we
                     # associate this entity with the tag.
-                    tag = Tag.get_or_create(each_tag)
+                    tag = self.__tag_model.get_or_create(each_tag)
                     tag.add_tagged(self.key())
                     self.__tags.append(tag)
         else: