85 |
85 |
86 __root_class__: If the new class is a root class, __root_class__ is set to |
86 __root_class__: If the new class is a root class, __root_class__ is set to |
87 itself so that it subclasses can quickly know what the root of |
87 itself so that it subclasses can quickly know what the root of |
88 their hierarchy is and what kind they are stored in. |
88 their hierarchy is and what kind they are stored in. |
89 __class_hierarchy__: List of classes describing the new model's place |
89 __class_hierarchy__: List of classes describing the new model's place |
90 in the class hierarchy. The first element is always the root |
90 in the class hierarchy in reverse MRO order. The first element is |
91 element while the last element is the new class itself. For example: |
91 always the root class while the last element is always the new class. |
92 |
92 |
|
93 MRO documentation: http://www.python.org/download/releases/2.3/mro/ |
|
94 |
|
95 For example: |
93 class Foo(PolymorphicClass): ... |
96 class Foo(PolymorphicClass): ... |
94 |
97 |
95 class Bar(Foo): ... |
98 class Bar(Foo): ... |
96 |
99 |
97 class Baz(Bar): ... |
100 class Baz(Bar): ... |
105 classes, are inserted in to the class-map which maps the class-key to |
108 classes, are inserted in to the class-map which maps the class-key to |
106 implementation. This class key is consulted using the polymorphic instances |
109 implementation. This class key is consulted using the polymorphic instances |
107 discriminator (the 'class' property of the entity) when loading from the |
110 discriminator (the 'class' property of the entity) when loading from the |
108 datastore. |
111 datastore. |
109 """ |
112 """ |
110 if name == 'PolyModel' or PolyModel not in bases: |
113 if name == 'PolyModel': |
111 db._initialize_properties(cls, name, bases, dct) |
114 super(PolymorphicClass, cls).__init__(name, bases, dct, map_kind=False) |
112 super(db.PropertiedClass, cls).__init__(name, bases, dct) |
115 return |
113 else: |
116 |
|
117 elif PolyModel in bases: |
|
118 if getattr(cls, '__class_hierarchy__', None): |
|
119 raise db.ConfigurationError(('%s cannot derive from PolyModel as ' |
|
120 '__class_hierarchy__ is already defined.') % cls.__name__) |
|
121 cls.__class_hierarchy__ = [cls] |
114 cls.__root_class__ = cls |
122 cls.__root_class__ = cls |
115 super(PolymorphicClass, cls).__init__(name, bases, dct) |
123 super(PolymorphicClass, cls).__init__(name, bases, dct) |
116 |
124 else: |
117 if name == 'PolyModel': |
125 super(PolymorphicClass, cls).__init__(name, bases, dct, map_kind=False) |
118 return |
126 |
119 |
127 cls.__class_hierarchy__ = [c for c in reversed(cls.mro()) |
120 if cls is not cls.__root_class__: |
128 if issubclass(c, PolyModel) and c != PolyModel] |
121 poly_class = None |
129 |
122 for base in cls.__bases__: |
130 if cls.__class_hierarchy__[0] != cls.__root_class__: |
123 if issubclass(base, PolyModel): |
|
124 poly_class = base |
|
125 break |
|
126 else: |
|
127 raise db.ConfigurationError( |
131 raise db.ConfigurationError( |
128 "Polymorphic class '%s' does not inherit from PolyModel." |
132 '%s cannot be derived from both root classes %s and %s' % |
129 % cls.__name__) |
133 (cls.__name__, |
130 |
134 cls.__class_hierarchy__[0].__name__, |
131 cls.__class_hierarchy__ = poly_class.__class_hierarchy__ + [cls] |
135 cls.__root_class__.__name__)) |
132 else: |
|
133 cls.__class_hierarchy__ = [cls] |
|
134 |
136 |
135 _class_map[cls.class_key()] = cls |
137 _class_map[cls.class_key()] = cls |
136 |
138 |
137 |
139 |
138 class PolyModel(db.Model): |
140 class PolyModel(db.Model): |
308 raise db.KindError('No implementation for class \'%s\'' % key) |
310 raise db.KindError('No implementation for class \'%s\'' % key) |
309 return poly_class.from_entity(entity) |
311 return poly_class.from_entity(entity) |
310 return super(PolyModel, cls).from_entity(entity) |
312 return super(PolyModel, cls).from_entity(entity) |
311 |
313 |
312 @classmethod |
314 @classmethod |
313 def all(cls): |
315 def all(cls, **kwds): |
314 """Get all instance of a class hierarchy. |
316 """Get all instance of a class hierarchy. |
|
317 |
|
318 Args: |
|
319 kwds: Keyword parameters passed on to Model.all. |
315 |
320 |
316 Returns: |
321 Returns: |
317 Query with filter set to match this class' discriminator. |
322 Query with filter set to match this class' discriminator. |
318 """ |
323 """ |
319 query = super(PolyModel, cls).all() |
324 query = super(PolyModel, cls).all(**kwds) |
320 if cls != cls.__root_class__: |
325 if cls != cls.__root_class__: |
321 query.filter(_CLASS_KEY_PROPERTY + ' =', cls.class_name()) |
326 query.filter(_CLASS_KEY_PROPERTY + ' =', cls.class_name()) |
322 return query |
327 return query |
323 |
328 |
324 @classmethod |
329 @classmethod |