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
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.
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."""
41 KIND_NAME
= '' # Don't instantiate this class; always use a subclass.
45 """Kind name override."""
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
56 def namespace_name(self
):
57 """Return the namespace name specified by this entity's key."""
58 return self
.key_to_namespace(self
.key
)
61 def key_for_namespace(cls
, namespace
):
62 """Return the Key for a namespace.
65 namespace: A string giving the namespace whose key is requested.
68 The Key for the namespace.
71 return model
.Key(cls
.KIND_NAME
, namespace
)
73 return model
.Key(cls
.KIND_NAME
, cls
.EMPTY_NAMESPACE_ID
)
76 def key_to_namespace(cls
, key
):
77 """Return the namespace specified by a given __namespace__ key.
80 key: key whose name is requested.
83 The namespace specified by key.
85 return key
.string_id() or ''
88 class Kind(_BaseMetadata
):
89 """Model for __kind__ metadata query results."""
91 KIND_NAME
= '__kind__'
95 """Return the kind name specified by this entity's key."""
96 return self
.key_to_kind(self
.key
)
99 def key_for_kind(cls
, kind
):
100 """Return the __kind__ key for kind.
103 kind: kind whose key is requested.
108 return model
.Key(cls
.KIND_NAME
, kind
)
111 def key_to_kind(cls
, key
):
112 """Return the kind specified by a given __kind__ key.
115 key: key whose name is requested.
118 The kind specified by key.
123 class Property(_BaseMetadata
):
124 """Model for __property__ metadata query results."""
126 KIND_NAME
= '__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
)
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)
141 def key_for_kind(cls
, kind
):
142 """Return the __property__ key for kind.
145 kind: kind whose key is requested.
148 The parent key for __property__ keys of kind.
150 return model
.Key(Kind
.KIND_NAME
, kind
)
153 def key_for_property(cls
, kind
, property):
154 """Return the __property__ key for property of kind.
157 kind: kind whose key is requested.
158 property: property whose key is requested.
161 The key for property of kind.
163 return model
.Key(Kind
.KIND_NAME
, kind
, Property
.KIND_NAME
, property)
166 def key_to_kind(cls
, key
):
167 """Return the kind specified by a given __property__ key.
170 key: key whose kind name is requested.
173 The kind specified by key.
175 if key
.kind() == Kind
.KIND_NAME
:
178 return key
.parent().id()
181 def key_to_property(cls
, key
):
182 """Return the property specified by a given __property__ key.
185 key: key whose property name is requested.
188 property specified by key, or None if the key specified only a kind.
190 if key
.kind() == Kind
.KIND_NAME
:
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
206 KIND_NAME
= '__entity_group__'
209 version
= model
.IntegerProperty(name
='__version__')
212 def key_for_entity_group(cls
, key
):
213 """Return the key for the entity group containing key.
216 key: a key for an entity group whose __entity_group__ key you want.
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.
228 start: only return namespaces >= start if start is not None.
229 end: only return namespaces < end if end is not None.
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
))
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.
246 start: only return kinds >= start if start is not None.
247 end: only return kinds < end if end is not None.
250 A list of kind names between the (optional) start and end values.
253 if start
is not None and start
!= '':
254 q
= q
.filter(Kind
.key
>= Kind
.key_for_kind(start
))
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.
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.
274 A list of property names of kind between the (optional) start and end
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
))
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.
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.
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
))
307 q
= q
.filter(Property
.key
< Property
.key_for_property(kind
, end
))
311 result
[property.property_name
] = property.property_representation
316 def get_entity_group_version(key
):
317 """Return the version of the entity group containing key.
320 key: a key for an entity group whose __entity_group__ key you want.
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()