--- a/thirdparty/google_appengine/google/appengine/ext/db/__init__.py Sat Dec 06 14:50:45 2008 +0000
+++ b/thirdparty/google_appengine/google/appengine/ext/db/__init__.py Sat Dec 06 16:52:21 2008 +0000
@@ -77,7 +77,6 @@
-
import datetime
import logging
import time
@@ -119,8 +118,10 @@
Text = datastore_types.Text
Blob = datastore_types.Blob
+
_kind_map = {}
+
_SELF_REFERENCE = object()
@@ -146,13 +147,17 @@
class ConfigurationError(Error):
- """Raised when a property is improperly configured."""
+ """Raised when a property or model is improperly configured."""
class ReservedWordError(Error):
"""Raised when a property is defined for a reserved word."""
+class DerivedPropertyError(Error):
+ """Raised when attempting to assign a value to a derived property."""
+
+
_ALLOWED_PROPERTY_TYPES = set([
basestring,
str,
@@ -226,6 +231,36 @@
"definition." % locals())
+def _initialize_properties(model_class, name, bases, dct):
+ """Initialize Property attributes for Model-class.
+
+ Args:
+ model_class: Model class to initialize properties for.
+ """
+ model_class._properties = {}
+ defined = set()
+ for base in bases:
+ if hasattr(base, '_properties'):
+ property_keys = base._properties.keys()
+ duplicate_properties = defined.intersection(property_keys)
+ if duplicate_properties:
+ raise DuplicatePropertyError(
+ 'Duplicate properties in base class %s already defined: %s' %
+ (base.__name__, list(duplicate_properties)))
+ defined.update(property_keys)
+ model_class._properties.update(base._properties)
+
+ for attr_name in dct.keys():
+ attr = dct[attr_name]
+ if isinstance(attr, Property):
+ check_reserved_word(attr_name)
+ if attr_name in defined:
+ raise DuplicatePropertyError('Duplicate property: %s' % attr_name)
+ defined.add(attr_name)
+ model_class._properties[attr_name] = attr
+ attr.__property_config__(model_class, attr_name)
+
+
class PropertiedClass(type):
"""Meta-class for initializing Model classes properties.
@@ -239,7 +274,7 @@
Duplicate properties are not permitted.
"""
- def __init__(cls, name, bases, dct):
+ def __init__(cls, name, bases, dct, map_kind=True):
"""Initializes a class that might have property definitions.
This method is called when a class is created with the PropertiedClass
@@ -272,30 +307,10 @@
"""
super(PropertiedClass, cls).__init__(name, bases, dct)
- cls._properties = {}
- defined = set()
- for base in bases:
- if hasattr(base, '_properties'):
- property_keys = base._properties.keys()
- duplicate_properties = defined.intersection(property_keys)
- if duplicate_properties:
- raise DuplicatePropertyError(
- 'Duplicate properties in base class %s already defined: %s' %
- (base.__name__, list(duplicate_properties)))
- defined.update(property_keys)
- cls._properties.update(base._properties)
-
- for attr_name in dct.keys():
- attr = dct[attr_name]
- if isinstance(attr, Property):
- check_reserved_word(attr_name)
- if attr_name in defined:
- raise DuplicatePropertyError('Duplicate property: %s' % attr_name)
- defined.add(attr_name)
- cls._properties[attr_name] = attr
- attr.__property_config__(cls, attr_name)
-
- _kind_map[cls.kind()] = cls
+ _initialize_properties(cls, name, bases, dct)
+
+ if map_kind:
+ _kind_map[cls.kind()] = cls
class Property(object):
@@ -466,7 +481,10 @@
return value
def _attr_name(self):
- """Attribute name we use for this property in model instances."""
+ """Attribute name we use for this property in model instances.
+
+ DO NOT USE THIS METHOD.
+ """
return '_' + self.name
data_type = str
@@ -500,7 +518,12 @@
__metaclass__ = PropertiedClass
- def __init__(self, parent=None, key_name=None, _app=None, **kwds):
+ def __init__(self,
+ parent=None,
+ key_name=None,
+ _app=None,
+ _from_entity=False,
+ **kwds):
"""Creates a new instance of this model.
To create a new entity, you instantiate a model and then call save(),
@@ -524,6 +547,7 @@
level instance.
key_name: Name for new model instance.
_app: Intentionally undocumented.
+ _from_entity: Intentionally undocumented.
args: Keyword arguments mapping to properties of model.
"""
if key_name == '':
@@ -533,15 +557,22 @@
key_name.__class__.__name__)
if parent is not None:
- if not isinstance(parent, Model):
+ if not isinstance(parent, (Model, Key)):
raise TypeError('Expected Model type; received %s (is %s)' %
(parent, parent.__class__.__name__))
- if not parent.is_saved():
+ if isinstance(parent, Model) and not parent.has_key():
raise BadValueError(
- "%s instance must be saved before it can be used as a "
+ "%s instance must have a complete key before it can be used as a "
"parent." % parent.kind())
-
- self._parent = parent
+ if isinstance(parent, Key):
+ self._parent_key = parent
+ self._parent = None
+ else:
+ self._parent_key = parent.key()
+ self._parent = parent
+ else:
+ self._parent_key = None
+ self._parent = None
self._entity = None
self._key_name = key_name
self._app = _app
@@ -552,7 +583,11 @@
value = kwds[prop.name]
else:
value = prop.default_value()
- prop.__set__(self, value)
+ try:
+ prop.__set__(self, value)
+ except DerivedPropertyError, e:
+ if prop.name in kwds and not _from_entity:
+ raise
def key(self):
"""Unique key for this entity.
@@ -569,6 +604,9 @@
"""
if self.is_saved():
return self._entity.key()
+ elif self._key_name:
+ parent = self._parent and self._parent.key()
+ return Key.from_path(self.kind(), self._key_name, parent=parent)
else:
raise NotSavedError()
@@ -634,7 +672,12 @@
if self.is_saved():
entity = self._entity
else:
- if self._parent is not None:
+ if self._parent_key is not None:
+ entity = _entity_class(self.kind(),
+ parent=self._parent_key,
+ name=self._key_name,
+ _app=self._app)
+ elif self._parent is not None:
entity = _entity_class(self.kind(),
parent=self._parent._entity,
name=self._key_name,
@@ -668,6 +711,18 @@
"""
return self._entity is not None
+ def has_key(self):
+ """Determine if this model instance has a complete key.
+
+ Ids are not assigned until the data is saved to the Datastore, but
+ instances with a key name always have a full key.
+
+ Returns:
+ True if the object has been persisted to the datastore or has a key_name,
+ otherwise False.
+ """
+ return self.is_saved() or self._key_name
+
def dynamic_properties(self):
"""Returns a list of all dynamic properties defined for instance."""
return []
@@ -683,10 +738,10 @@
Parent of contained entity or parent provided in constructor, None if
instance has no parent.
"""
- if (self._parent is None and
- self._entity is not None and
- self._entity.parent() is not None):
- self._parent = get(self._entity.parent())
+ if self._parent is None:
+ parent_key = self.parent_key()
+ if parent_key is not None:
+ self._parent = get(parent_key)
return self._parent
def parent_key(self):
@@ -698,7 +753,9 @@
Returns:
Parent key of entity, None if there is no parent.
"""
- if self._parent is not None:
+ if self._parent_key is not None:
+ return self._parent_key
+ elif self._parent is not None:
return self._parent.key()
elif self._entity is not None:
return self._entity.parent()
@@ -924,7 +981,7 @@
(repr(cls), entity.kind()))
entity_values = cls._load_entity_values(entity)
- instance = cls(None, **entity_values)
+ instance = cls(None, _from_entity=True, **entity_values)
instance._entity = entity
del instance._key_name
return instance
@@ -1016,14 +1073,23 @@
"""Delete one or more Model instances.
Args:
- models: Model instance or list of Model instances.
+ models_or_keys: Model instance or list of Model instances.
Raises:
TransactionFailedError if the data could not be committed.
"""
- models, multiple = datastore.NormalizeAndTypeCheck(models, Model)
- entities = [model.key() for model in models]
- keys = datastore.Delete(entities)
+ models_or_keys, multiple = datastore.NormalizeAndTypeCheck(
+ models, (Model, Key, basestring))
+ keys = []
+ for model_or_key in models_or_keys:
+ if isinstance(model_or_key, Model):
+ key = model_or_key = model_or_key.key()
+ elif isinstance(model_or_key, basestring):
+ key = model_or_key = Key(model_or_key)
+ else:
+ key = model_or_key
+ keys.append(key)
+ datastore.Delete(keys)
class Expando(Model):
@@ -1133,7 +1199,7 @@
self._dynamic_properties = {}
self._dynamic_properties[key] = value
else:
- Model.__setattr__(self, key, value)
+ super(Expando, self).__setattr__(key, value)
def __getattr__(self, key):
"""If no explicit attribute defined, retrieve value from entity.
@@ -1211,7 +1277,7 @@
Args:
entity: Entity which contain values to search dyanmic properties for.
"""
- entity_values = Model._load_entity_values(entity)
+ entity_values = super(Expando, cls)._load_entity_values(entity)
for key, value in entity.iteritems():
if key not in entity_values:
entity_values[str(key)] = value
@@ -1537,7 +1603,7 @@
else:
raise NotSavedError()
elif isinstance(ancestor, Model):
- if ancestor.is_saved():
+ if ancestor.has_key():
self.__ancestor = ancestor.key()
else:
raise NotSavedError()
@@ -2170,20 +2236,33 @@
if not isinstance(value, list):
raise BadValueError('Property %s must be a list' % self.name)
- if self.item_type in (int, long):
- item_type = (int, long)
- else:
- item_type = self.item_type
-
- for item in value:
- if not isinstance(item, item_type):
- if item_type == (int, long):
- raise BadValueError('Items in the %s list must all be integers.' %
- self.name)
- else:
- raise BadValueError(
- 'Items in the %s list must all be %s instances' %
- (self.name, self.item_type.__name__))
+ value = self.validate_list_contents(value)
+ return value
+
+ def validate_list_contents(self, value):
+ """Validates that all items in the list are of the correct type.
+
+ Returns:
+ The validated list.
+
+ Raises:
+ BadValueError if the list has items are not instances of the
+ item_type given to the constructor.
+ """
+ if self.item_type in (int, long):
+ item_type = (int, long)
+ else:
+ item_type = self.item_type
+
+ for item in value:
+ if not isinstance(item, item_type):
+ if item_type == (int, long):
+ raise BadValueError('Items in the %s list must all be integers.' %
+ self.name)
+ else:
+ raise BadValueError(
+ 'Items in the %s list must all be %s instances' %
+ (self.name, self.item_type.__name__))
return value
def empty(self, value):
@@ -2210,6 +2289,15 @@
"""
return list(super(ListProperty, self).default_value())
+ def get_value_for_datastore(self, model_instance):
+ """Get value from property to send to datastore.
+
+ Returns:
+ validated list appropriate to save in the datastore.
+ """
+ return self.validate_list_contents(
+ super(ListProperty, self).get_value_for_datastore(model_instance))
+
class StringListProperty(ListProperty):
"""A property that stores a list of strings.
@@ -2368,9 +2456,9 @@
if isinstance(value, datastore.Key):
return value
- if value is not None and not value.is_saved():
+ if value is not None and not value.has_key():
raise BadValueError(
- '%s instance must be saved before it can be stored as a '
+ '%s instance must have a complete key before it can be stored as a '
'reference' % self.reference_class.kind())
value = super(ReferenceProperty, self).validate(value)