#!/usr/bin/env python
#
# Copyright 2007 Google Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
"""Models and helper functions for access to app's datastore metadata.
These entities cannot be created by users, but are created as results of
__namespace__, __kind__ and __property__ metadata queries such as
# Find all namespaces
q = db.GqlQuery("SELECT * FROM __namespace__")
for p in q.run():
print "namespace:", repr(p.namespace_name)
or
# Find all properties of A whose name starts with a lower-case letter
q = db.GqlQuery("SELECT __key__ from __property__ " +
"WHERE __key__ >= :1 AND __key__ < :2",
Property.key_for_property("A", "a"),
Property.key_for_property("A", chr(ord("z") + 1)))
for p in q.run():
print "%s: %s" % (Property.key_to_kind(p), Property.key_to_property(p))
or, using Query objects
# Find all kinds >= "a"
q = metadata.Kind().all()
q.filter("__key__ >=", metadata.Kind.key_for_kind("a"))
for p in q.run():
print "kind:", repr(p.kind_name)
"""
import os
if os.environ.get('APPENGINE_RUNTIME') == 'python27':
from google.appengine.api import datastore_types
from google.appengine.ext import db
else:
from google.appengine.api import datastore_types
from google.appengine.ext import db
[docs]class Namespace(BaseMetadata):
"""Model for __namespace__ metadata query results."""
KIND_NAME = '__namespace__'
EMPTY_NAMESPACE_ID = datastore_types._EMPTY_NAMESPACE_ID
@property
def namespace_name(self):
"""Return the namespace name specified by this entity's key."""
return self.key_to_namespace(self.key())
[docs] @classmethod
def key_for_namespace(cls, namespace):
"""Return the __namespace__ key for namespace.
Args:
namespace: namespace whose key is requested.
Returns:
The key for namespace.
"""
if namespace:
return db.Key.from_path(cls.KIND_NAME, namespace)
else:
return db.Key.from_path(cls.KIND_NAME, cls.EMPTY_NAMESPACE_ID)
[docs] @classmethod
def key_to_namespace(cls, key):
"""Return the namespace specified by a given __namespace__ key.
Args:
key: key whose name is requested.
Returns:
The namespace specified by key.
"""
return key.name() or ''
[docs]class Kind(BaseMetadata):
"""Model for __kind__ metadata query results."""
KIND_NAME = '__kind__'
@property
def kind_name(self):
"""Return the kind name specified by this entity's key."""
return self.key_to_kind(self.key())
[docs] @classmethod
def key_for_kind(cls, kind):
"""Return the __kind__ key for kind.
Args:
kind: kind whose key is requested.
Returns:
The key for kind.
"""
return db.Key.from_path(cls.KIND_NAME, kind)
[docs] @classmethod
def key_to_kind(cls, key):
"""Return the kind specified by a given __kind__ key.
Args:
key: key whose name is requested.
Returns:
The kind specified by key.
"""
return key.name()
[docs]class Property(BaseMetadata):
"""Model for __property__ metadata query results."""
KIND_NAME = '__property__'
@property
def property_name(self):
"""Return the property name specified by this entity's key."""
return self.key_to_property(self.key())
@property
def kind_name(self):
"""Return the kind name specified by this entity's key."""
return self.key_to_kind(self.key())
property_representation = db.StringListProperty()
[docs] @classmethod
def key_for_kind(cls, kind):
"""Return the __property__ key for kind.
Args:
kind: kind whose key is requested.
Returns:
The parent key for __property__ keys of kind.
"""
return db.Key.from_path(Kind.KIND_NAME, kind)
[docs] @classmethod
def key_for_property(cls, kind, property):
"""Return the __property__ key for property of kind.
Args:
kind: kind whose key is requested.
property: property whose key is requested.
Returns:
The key for property of kind.
"""
return db.Key.from_path(Kind.KIND_NAME, kind, Property.KIND_NAME, property)
[docs] @classmethod
def key_to_kind(cls, key):
"""Return the kind specified by a given __property__ key.
Args:
key: key whose kind name is requested.
Returns:
The kind specified by key.
"""
if key.kind() == Kind.KIND_NAME:
return key.name()
else:
return key.parent().name()
[docs] @classmethod
def key_to_property(cls, key):
"""Return the property specified by a given __property__ key.
Args:
key: key whose property name is requested.
Returns:
property specified by key, or None if the key specified only a kind.
"""
if key.kind() == Kind.KIND_NAME:
return None
else:
return key.name()
[docs]class EntityGroup(BaseMetadata):
"""Model for __entity_group__ metadata (available in HR datastore only).
This metadata contains a numeric __version__ property that is guaranteed
to increase on every change to the entity group. The version may increase
even in the absence of user-visible changes to the entity group. The
__entity_group__ entity may not exist None if the entity group was never
written to.
"""
KIND_NAME = '__entity_group__'
ID = 1
version = db.IntegerProperty(name='__version__')
[docs] @classmethod
def key_for_entity(cls, entity_or_key):
"""Return the metadata key for the entity group containing entity_or_key.
Use this key to get() the __entity_group__ metadata entity for the
entity group containing entity_or_key.
Args:
entity_or_key: a key or entity whose __entity_group__ key you want.
Returns:
The __entity_group__ key for the entity group containing entity_or_key.
"""
if isinstance(entity_or_key, db.Model):
key = entity_or_key.key()
else:
key = entity_or_key
while key.parent():
key = key.parent()
return db.Key.from_path(cls.KIND_NAME, cls.ID, parent=key)
[docs]def get_namespaces(start=None, end=None):
"""Return all namespaces in the specified range.
Args:
start: only return namespaces >= start if start is not None.
end: only return namespaces < end if end is not None.
Returns:
A list of namespace names between the (optional) start and end values.
"""
q = Namespace.all()
if start is not None:
q.filter('__key__ >=', Namespace.key_for_namespace(start))
if end is not None:
q.filter('__key__ <', Namespace.key_for_namespace(end))
return [x.namespace_name for x in q.run()]
[docs]def get_kinds(start=None, end=None):
"""Return all kinds in the specified range.
Args:
start: only return kinds >= start if start is not None.
end: only return kinds < end if end is not None.
Returns:
A list of kind names between the (optional) start and end values.
"""
q = Kind.all()
if start is not None and start != '':
q.filter('__key__ >=', Kind.key_for_kind(start))
if end is not None:
if end == '':
return []
q.filter('__key__ <', Kind.key_for_kind(end))
return [x.kind_name for x in q.run()]
[docs]def get_properties_of_kind(kind, start=None, end=None):
"""Return all properties of kind in the specified range.
NOTE: This function does not return unindexed properties.
Args:
kind: name of kind whose properties you want.
start: only return properties >= start if start is not None.
end: only return properties < end if end is not None.
Returns:
A list of property names of kind between the (optional) start and end
values.
"""
q = Property.all(keys_only=True)
q.ancestor(Property.key_for_kind(kind))
if start is not None and start != '':
q.filter('__key__ >=', Property.key_for_property(kind, start))
if end is not None:
if end == '':
return []
q.filter('__key__ <', Property.key_for_property(kind, end))
return [Property.key_to_property(x) for x in q.run()]
[docs]def get_representations_of_kind(kind, start=None, end=None):
"""Return all representations of properties of kind in the specified range.
NOTE: This function does not return unindexed properties.
Args:
kind: name of kind whose properties you want.
start: only return properties >= start if start is not None.
end: only return properties < end if end is not None.
Returns:
A dictionary mapping property names to its list of representations.
"""
q = Property.all()
q.ancestor(Property.key_for_kind(kind))
if start is not None and start != '':
q.filter('__key__ >=', Property.key_for_property(kind, start))
if end is not None:
if end == '':
return {}
q.filter('__key__ <', Property.key_for_property(kind, end))
result = {}
for property in q.run():
result[property.property_name] = property.property_representation
return result
[docs]def get_entity_group_version(entity_or_key):
"""Return the version of the entity group containing entity_or_key.
Args:
entity_or_key: a key or entity whose version you want.
Returns: The version of the entity group containing entity_or_key. This
version is guaranteed to increase on every change to the entity
group. The version may increase even in the absence of user-visible
changes to the entity group. May return None if the entity group was
never written to.
On non-HR datatores, this function returns None.
"""
eg = db.get(EntityGroup.key_for_entity(entity_or_key))
if eg:
return eg.version
else:
return None