|
1 # |
|
2 # Copyright (c) 2008 Andreas Blixt <andreas@blixt.org> |
|
3 # Project homepage: <http://code.google.com/p/blixt/> |
|
4 # |
|
5 # License: MIT license <http://www.opensource.org/licenses/mit-license.php> |
|
6 # |
|
7 |
|
8 """Data-related Google App Engine extensions. |
|
9 """ |
|
10 |
|
11 from google.appengine.ext import db |
|
12 |
|
13 import sys |
|
14 |
|
15 class Error(Exception): |
|
16 """Base of all exceptions in the blixt.data module.""" |
|
17 pass |
|
18 |
|
19 class PolyModel(db.Model): |
|
20 """An extension to Google App Engine models that improves the support for |
|
21 inheritance. |
|
22 |
|
23 Any models extending a model that extends PolyModel will be stored in the |
|
24 datastore as the same kind as the model that extends PolyModel, but with |
|
25 their additional properties. When they are retrieved from the datastore, |
|
26 they will be loaded as the appropiate model class. |
|
27 |
|
28 The difference from the Model class is that queries will include models that |
|
29 inherit from the model being queried. |
|
30 """ |
|
31 inheritance_line = db.StringListProperty() |
|
32 |
|
33 def __init__(self, parent = None, key_name = None, _app = None, **kwds): |
|
34 """Creates a new instance of this polymorphic model. |
|
35 |
|
36 Args: |
|
37 parent: Parent instance for this instance or None, indicating a top- |
|
38 level instance. |
|
39 key_name: Name for new model instance. |
|
40 _app: Intentionally undocumented. |
|
41 args: Keyword arguments mapping to properties of model. |
|
42 """ |
|
43 if self.__class__ == PolyModel or not isinstance(self, PolyModel): |
|
44 raise Error('Instances of PolyModel must be created through a ' |
|
45 'subclass of PolyModel.') |
|
46 |
|
47 line = [] |
|
48 for c in self.__class__.__mro__: |
|
49 if c == PolyModel: |
|
50 self.__class__._kind = p |
|
51 break |
|
52 line.append('%s.%s' % (c.__module__, c.__name__)) |
|
53 p = c |
|
54 |
|
55 kwds['inheritance_line'] = line |
|
56 |
|
57 super(PolyModel, self).__init__(parent = parent, key_name = key_name, |
|
58 _app = _app, **kwds) |
|
59 |
|
60 @classmethod |
|
61 def _kind_type(cls): |
|
62 """Gets the class highest in the model inheritance hierarchy (the class |
|
63 that will be used as the datastore kind.) |
|
64 """ |
|
65 p = cls |
|
66 |
|
67 for c in cls.__mro__: |
|
68 # The meta-class 'PropertiedClass' calls kind() which leads here. |
|
69 # The variable 'PolyModel' is not assigned until after the meta- |
|
70 # class has finished setting up the class. Therefore, a string |
|
71 # comparison is used instead of a value comparison. |
|
72 if c.__name__ == 'PolyModel': break |
|
73 p = c |
|
74 |
|
75 return p |
|
76 |
|
77 @classmethod |
|
78 def all(cls): |
|
79 """Returns a query over all instances of this model, as well as the |
|
80 instances of any descendant models, from the datastore. |
|
81 |
|
82 Returns: |
|
83 Query that will retrieve all instances from entity collection. |
|
84 """ |
|
85 qry = super(PolyModel, cls).all() |
|
86 |
|
87 if cls != cls._kind_type(): |
|
88 full_name = '%s.%s' % (cls.__module__, cls.__name__) |
|
89 qry.filter('inheritance_line =', full_name) |
|
90 |
|
91 return qry |
|
92 |
|
93 @classmethod |
|
94 def from_entity(cls, entity): |
|
95 """Converts the entity representation of this model to an instance. |
|
96 |
|
97 Converts datastore.Entity instance to an instance of the appropiate |
|
98 class, which can be cls or any descendant thereof. |
|
99 |
|
100 Args: |
|
101 entity: Entity loaded directly from datastore. |
|
102 |
|
103 Raises: |
|
104 KindError when cls is incorrect model for entity. |
|
105 """ |
|
106 if entity.has_key('inheritance_line'): |
|
107 mod_name, cls_name = entity['inheritance_line'][0].rsplit('.', 1) |
|
108 __import__(mod_name) |
|
109 cls = getattr(sys.modules[mod_name], cls_name) |
|
110 return super(PolyModel, cls).from_entity(entity) |
|
111 |
|
112 @classmethod |
|
113 def kind(cls): |
|
114 """Returns the datastore kind we use for this model. |
|
115 |
|
116 This is the name of the class that is highest up in the inheritance |
|
117 hierarchy of this model. |
|
118 """ |
|
119 return cls._kind_type().__name__ |
|
120 |