# HG changeset patch # User Todd Larsen # Date 1222457721 0 # Node ID 187d45783300e31189af96c346911372acba6904 # Parent b8b4a83788d4682e6ea3c8c100d46dbb1f9566a4 Add the third-party PolyModel class. This class supports inheritance in Google App Engine Datastore Model classes. diff -r b8b4a83788d4 -r 187d45783300 app/polymodel/LICENSE --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/app/polymodel/LICENSE Fri Sep 26 19:35:21 2008 +0000 @@ -0,0 +1,22 @@ +The MIT License + +Copyright (c) 2008 Andreas Blixt + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + diff -r b8b4a83788d4 -r 187d45783300 app/polymodel/__init__.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/app/polymodel/__init__.py Fri Sep 26 19:35:21 2008 +0000 @@ -0,0 +1,120 @@ +# +# Copyright (c) 2008 Andreas Blixt +# Project homepage: +# +# License: MIT license +# + +"""Data-related Google App Engine extensions. +""" + +from google.appengine.ext import db + +import sys + +class Error(Exception): + """Base of all exceptions in the blixt.data module.""" + pass + +class PolyModel(db.Model): + """An extension to Google App Engine models that improves the support for + inheritance. + + Any models extending a model that extends PolyModel will be stored in the + datastore as the same kind as the model that extends PolyModel, but with + their additional properties. When they are retrieved from the datastore, + they will be loaded as the appropiate model class. + + The difference from the Model class is that queries will include models that + inherit from the model being queried. + """ + inheritance_line = db.StringListProperty() + + def __init__(self, parent = None, key_name = None, _app = None, **kwds): + """Creates a new instance of this polymorphic model. + + Args: + parent: Parent instance for this instance or None, indicating a top- + level instance. + key_name: Name for new model instance. + _app: Intentionally undocumented. + args: Keyword arguments mapping to properties of model. + """ + if self.__class__ == PolyModel or not isinstance(self, PolyModel): + raise Error('Instances of PolyModel must be created through a ' + 'subclass of PolyModel.') + + line = [] + for c in self.__class__.__mro__: + if c == PolyModel: + self.__class__._kind = p + break + line.append('%s.%s' % (c.__module__, c.__name__)) + p = c + + kwds['inheritance_line'] = line + + super(PolyModel, self).__init__(parent = parent, key_name = key_name, + _app = _app, **kwds) + + @classmethod + def _kind_type(cls): + """Gets the class highest in the model inheritance hierarchy (the class + that will be used as the datastore kind.) + """ + p = cls + + for c in cls.__mro__: + # The meta-class 'PropertiedClass' calls kind() which leads here. + # The variable 'PolyModel' is not assigned until after the meta- + # class has finished setting up the class. Therefore, a string + # comparison is used instead of a value comparison. + if c.__name__ == 'PolyModel': break + p = c + + return p + + @classmethod + def all(cls): + """Returns a query over all instances of this model, as well as the + instances of any descendant models, from the datastore. + + Returns: + Query that will retrieve all instances from entity collection. + """ + qry = super(PolyModel, cls).all() + + if cls != cls._kind_type(): + full_name = '%s.%s' % (cls.__module__, cls.__name__) + qry.filter('inheritance_line =', full_name) + + return qry + + @classmethod + def from_entity(cls, entity): + """Converts the entity representation of this model to an instance. + + Converts datastore.Entity instance to an instance of the appropiate + class, which can be cls or any descendant thereof. + + Args: + entity: Entity loaded directly from datastore. + + Raises: + KindError when cls is incorrect model for entity. + """ + if entity.has_key('inheritance_line'): + mod_name, cls_name = entity['inheritance_line'][0].rsplit('.', 1) + __import__(mod_name) + cls = getattr(sys.modules[mod_name], cls_name) + return super(PolyModel, cls).from_entity(entity) + + @classmethod + def kind(cls): + """Returns the datastore kind we use for this model. + + This is the name of the class that is highest up in the inheritance + hierarchy of this model. + """ + return cls._kind_type().__name__ +