1.9.30 sync.
[gae.git] / python / google / appengine / ext / ndb / metadata.py
blob4d3a9b3c9307392271f9215ef877734caa6b3346
1 """Models and helper functions for access to app's datastore metadata.
3 These entities cannot be created by users, but are created as results of
4 __namespace__, __kind__, __property__ and __entity_group__ metadata queries
5 or gets.
7 A simplified API is also offered:
9 ndb.metadata.get_namespaces(): A list of namespace names.
10 ndb.metadata.get_kinds(): A list of kind names.
11 ndb.metadata.get_properties_of_kind(kind):
12 A list of property names for the given kind name.
13 ndb.metadata.get_representations_of_kind(kind):
14 A dict mapping property names to lists of representation ids.
15 ndb.metadata.get_entity_group_version(key):
16 The version of the entity group containing key (HRD only).
18 get_kinds(), get_properties_of_kind(), get_representations_of_kind()
19 implicitly apply to the current namespace.
21 get_namespaces(), get_kinds(), get_properties_of_kind(),
22 get_representations_of_kind() have optional start and end arguments to limit the
23 query to a range of names, such that start <= name < end.
24 """
26 from . import model
28 __all__ = ['Namespace', 'Kind', 'Property', 'EntityGroup',
29 'get_namespaces', 'get_kinds',
30 'get_properties_of_kind', 'get_representations_of_kind',
31 'get_entity_group_version',
35 class _BaseMetadata(model.Model):
36 """Base class for all metadata models."""
38 _use_cache = False
39 _use_memcache = False
41 KIND_NAME = '' # Don't instantiate this class; always use a subclass.
43 @classmethod
44 def _get_kind(cls):
45 """Kind name override."""
46 return cls.KIND_NAME
49 class Namespace(_BaseMetadata):
50 """Model for __namespace__ metadata query results."""
52 KIND_NAME = '__namespace__'
53 EMPTY_NAMESPACE_ID = 1 # == datastore_types._EMPTY_NAMESPACE_ID
55 @property
56 def namespace_name(self):
57 """Return the namespace name specified by this entity's key."""
58 return self.key_to_namespace(self.key)
60 @classmethod
61 def key_for_namespace(cls, namespace):
62 """Return the Key for a namespace.
64 Args:
65 namespace: A string giving the namespace whose key is requested.
67 Returns:
68 The Key for the namespace.
69 """
70 if namespace:
71 return model.Key(cls.KIND_NAME, namespace)
72 else:
73 return model.Key(cls.KIND_NAME, cls.EMPTY_NAMESPACE_ID)
75 @classmethod
76 def key_to_namespace(cls, key):
77 """Return the namespace specified by a given __namespace__ key.
79 Args:
80 key: key whose name is requested.
82 Returns:
83 The namespace specified by key.
84 """
85 return key.string_id() or ''
88 class Kind(_BaseMetadata):
89 """Model for __kind__ metadata query results."""
91 KIND_NAME = '__kind__'
93 @property
94 def kind_name(self):
95 """Return the kind name specified by this entity's key."""
96 return self.key_to_kind(self.key)
98 @classmethod
99 def key_for_kind(cls, kind):
100 """Return the __kind__ key for kind.
102 Args:
103 kind: kind whose key is requested.
105 Returns:
106 The key for kind.
108 return model.Key(cls.KIND_NAME, kind)
110 @classmethod
111 def key_to_kind(cls, key):
112 """Return the kind specified by a given __kind__ key.
114 Args:
115 key: key whose name is requested.
117 Returns:
118 The kind specified by key.
120 return key.id()
123 class Property(_BaseMetadata):
124 """Model for __property__ metadata query results."""
126 KIND_NAME = '__property__'
128 @property
129 def property_name(self):
130 """Return the property name specified by this entity's key."""
131 return self.key_to_property(self.key)
133 @property
134 def kind_name(self):
135 """Return the kind name specified by this entity's key."""
136 return self.key_to_kind(self.key)
138 property_representation = model.StringProperty(repeated=True)
140 @classmethod
141 def key_for_kind(cls, kind):
142 """Return the __property__ key for kind.
144 Args:
145 kind: kind whose key is requested.
147 Returns:
148 The parent key for __property__ keys of kind.
150 return model.Key(Kind.KIND_NAME, kind)
152 @classmethod
153 def key_for_property(cls, kind, property):
154 """Return the __property__ key for property of kind.
156 Args:
157 kind: kind whose key is requested.
158 property: property whose key is requested.
160 Returns:
161 The key for property of kind.
163 return model.Key(Kind.KIND_NAME, kind, Property.KIND_NAME, property)
165 @classmethod
166 def key_to_kind(cls, key):
167 """Return the kind specified by a given __property__ key.
169 Args:
170 key: key whose kind name is requested.
172 Returns:
173 The kind specified by key.
175 if key.kind() == Kind.KIND_NAME:
176 return key.id()
177 else:
178 return key.parent().id()
180 @classmethod
181 def key_to_property(cls, key):
182 """Return the property specified by a given __property__ key.
184 Args:
185 key: key whose property name is requested.
187 Returns:
188 property specified by key, or None if the key specified only a kind.
190 if key.kind() == Kind.KIND_NAME:
191 return None
192 else:
193 return key.id()
196 class EntityGroup(_BaseMetadata):
197 """Model for __entity_group__ metadata (available in HR datastore only).
199 This metadata contains a numeric __version__ property that is guaranteed
200 to increase on every change to the entity group. The version may increase
201 even in the absence of user-visible changes to the entity group. The
202 __entity_group__ entity may not exist if the entity group was never
203 written to.
206 KIND_NAME = '__entity_group__'
207 ID = 1
209 version = model.IntegerProperty(name='__version__')
211 @classmethod
212 def key_for_entity_group(cls, key):
213 """Return the key for the entity group containing key.
215 Args:
216 key: a key for an entity group whose __entity_group__ key you want.
218 Returns:
219 The __entity_group__ key for the entity group containing key.
221 return model.Key(cls.KIND_NAME, cls.ID, parent=key.root())
224 def get_namespaces(start=None, end=None):
225 """Return all namespaces in the specified range.
227 Args:
228 start: only return namespaces >= start if start is not None.
229 end: only return namespaces < end if end is not None.
231 Returns:
232 A list of namespace names between the (optional) start and end values.
234 q = Namespace.query()
235 if start is not None:
236 q = q.filter(Namespace.key >= Namespace.key_for_namespace(start))
237 if end is not None:
238 q = q.filter(Namespace.key < Namespace.key_for_namespace(end))
239 return [x.namespace_name for x in q]
242 def get_kinds(start=None, end=None):
243 """Return all kinds in the specified range, for the current namespace.
245 Args:
246 start: only return kinds >= start if start is not None.
247 end: only return kinds < end if end is not None.
249 Returns:
250 A list of kind names between the (optional) start and end values.
252 q = Kind.query()
253 if start is not None and start != '':
254 q = q.filter(Kind.key >= Kind.key_for_kind(start))
255 if end is not None:
256 if end == '':
257 return []
258 q = q.filter(Kind.key < Kind.key_for_kind(end))
260 return [x.kind_name for x in q]
263 def get_properties_of_kind(kind, start=None, end=None):
264 """Return all properties of kind in the specified range.
266 NOTE: This function does not return unindexed properties.
268 Args:
269 kind: name of kind whose properties you want.
270 start: only return properties >= start if start is not None.
271 end: only return properties < end if end is not None.
273 Returns:
274 A list of property names of kind between the (optional) start and end
275 values.
277 q = Property.query(ancestor=Property.key_for_kind(kind))
278 if start is not None and start != '':
279 q = q.filter(Property.key >= Property.key_for_property(kind, start))
280 if end is not None:
281 if end == '':
282 return []
283 q = q.filter(Property.key < Property.key_for_property(kind, end))
285 return [Property.key_to_property(k) for k in q.iter(keys_only=True)]
288 def get_representations_of_kind(kind, start=None, end=None):
289 """Return all representations of properties of kind in the specified range.
291 NOTE: This function does not return unindexed properties.
293 Args:
294 kind: name of kind whose properties you want.
295 start: only return properties >= start if start is not None.
296 end: only return properties < end if end is not None.
298 Returns:
299 A dictionary mapping property names to its list of representations.
301 q = Property.query(ancestor=Property.key_for_kind(kind))
302 if start is not None and start != '':
303 q = q.filter(Property.key >= Property.key_for_property(kind, start))
304 if end is not None:
305 if end == '':
306 return {}
307 q = q.filter(Property.key < Property.key_for_property(kind, end))
309 result = {}
310 for property in q:
311 result[property.property_name] = property.property_representation
313 return result
316 def get_entity_group_version(key):
317 """Return the version of the entity group containing key.
319 Args:
320 key: a key for an entity group whose __entity_group__ key you want.
322 Returns:
323 The version of the entity group containing key. This version is
324 guaranteed to increase on every change to the entity group. The version
325 may increase even in the absence of user-visible changes to the entity
326 group. May return None if the entity group was never written to.
328 On non-HR datatores, this function returns None.
331 eg = EntityGroup.key_for_entity_group(key).get()
332 if eg:
333 return eg.version
334 else:
335 return None