thirdparty/google_appengine/google/appengine/ext/db/__init__.py
changeset 686 df109be0567c
parent 297 35211afcd563
child 828 f5fd65cc3bf3
equal deleted inserted replaced
685:a440ced9a75f 686:df109be0567c
    75 
    75 
    76 
    76 
    77 
    77 
    78 
    78 
    79 
    79 
    80 
       
    81 import datetime
    80 import datetime
    82 import logging
    81 import logging
    83 import time
    82 import time
    84 import urlparse
    83 import urlparse
    85 
    84 
   117 PostalAddress = datastore_types.PostalAddress
   116 PostalAddress = datastore_types.PostalAddress
   118 Rating = datastore_types.Rating
   117 Rating = datastore_types.Rating
   119 Text = datastore_types.Text
   118 Text = datastore_types.Text
   120 Blob = datastore_types.Blob
   119 Blob = datastore_types.Blob
   121 
   120 
       
   121 
   122 _kind_map = {}
   122 _kind_map = {}
       
   123 
   123 
   124 
   124 _SELF_REFERENCE = object()
   125 _SELF_REFERENCE = object()
   125 
   126 
   126 
   127 
   127 _RESERVED_WORDS = set(['key_name'])
   128 _RESERVED_WORDS = set(['key_name'])
   144 class DuplicatePropertyError(Error):
   145 class DuplicatePropertyError(Error):
   145   """Raised when a property is duplicated in a model definition."""
   146   """Raised when a property is duplicated in a model definition."""
   146 
   147 
   147 
   148 
   148 class ConfigurationError(Error):
   149 class ConfigurationError(Error):
   149   """Raised when a property is improperly configured."""
   150   """Raised when a property or model is improperly configured."""
   150 
   151 
   151 
   152 
   152 class ReservedWordError(Error):
   153 class ReservedWordError(Error):
   153   """Raised when a property is defined for a reserved word."""
   154   """Raised when a property is defined for a reserved word."""
       
   155 
       
   156 
       
   157 class DerivedPropertyError(Error):
       
   158   """Raised when attempting to assign a value to a derived property."""
   154 
   159 
   155 
   160 
   156 _ALLOWED_PROPERTY_TYPES = set([
   161 _ALLOWED_PROPERTY_TYPES = set([
   157     basestring,
   162     basestring,
   158     str,
   163     str,
   224         "using a different name like %(attr_name)s_ and adding "
   229         "using a different name like %(attr_name)s_ and adding "
   225         "name='%(attr_name)s' to the parameter list of the property "
   230         "name='%(attr_name)s' to the parameter list of the property "
   226         "definition." % locals())
   231         "definition." % locals())
   227 
   232 
   228 
   233 
       
   234 def _initialize_properties(model_class, name, bases, dct):
       
   235   """Initialize Property attributes for Model-class.
       
   236 
       
   237   Args:
       
   238     model_class: Model class to initialize properties for.
       
   239   """
       
   240   model_class._properties = {}
       
   241   defined = set()
       
   242   for base in bases:
       
   243     if hasattr(base, '_properties'):
       
   244       property_keys = base._properties.keys()
       
   245       duplicate_properties = defined.intersection(property_keys)
       
   246       if duplicate_properties:
       
   247         raise DuplicatePropertyError(
       
   248             'Duplicate properties in base class %s already defined: %s' %
       
   249             (base.__name__, list(duplicate_properties)))
       
   250       defined.update(property_keys)
       
   251       model_class._properties.update(base._properties)
       
   252 
       
   253   for attr_name in dct.keys():
       
   254     attr = dct[attr_name]
       
   255     if isinstance(attr, Property):
       
   256       check_reserved_word(attr_name)
       
   257       if attr_name in defined:
       
   258         raise DuplicatePropertyError('Duplicate property: %s' % attr_name)
       
   259       defined.add(attr_name)
       
   260       model_class._properties[attr_name] = attr
       
   261       attr.__property_config__(model_class, attr_name)
       
   262 
       
   263 
   229 class PropertiedClass(type):
   264 class PropertiedClass(type):
   230   """Meta-class for initializing Model classes properties.
   265   """Meta-class for initializing Model classes properties.
   231 
   266 
   232   Used for initializing Properties defined in the context of a model.
   267   Used for initializing Properties defined in the context of a model.
   233   By using a meta-class much of the configuration of a Property
   268   By using a meta-class much of the configuration of a Property
   237   do appropriate initialization via __property_config__.
   272   do appropriate initialization via __property_config__.
   238 
   273 
   239   Duplicate properties are not permitted.
   274   Duplicate properties are not permitted.
   240   """
   275   """
   241 
   276 
   242   def __init__(cls, name, bases, dct):
   277   def __init__(cls, name, bases, dct, map_kind=True):
   243     """Initializes a class that might have property definitions.
   278     """Initializes a class that might have property definitions.
   244 
   279 
   245     This method is called when a class is created with the PropertiedClass
   280     This method is called when a class is created with the PropertiedClass
   246     meta-class.
   281     meta-class.
   247 
   282 
   270       ReservedWordError when a property is given a name that is in the list of
   305       ReservedWordError when a property is given a name that is in the list of
   271         reserved words, attributes of Model and names of the form '__.*__'.
   306         reserved words, attributes of Model and names of the form '__.*__'.
   272     """
   307     """
   273     super(PropertiedClass, cls).__init__(name, bases, dct)
   308     super(PropertiedClass, cls).__init__(name, bases, dct)
   274 
   309 
   275     cls._properties = {}
   310     _initialize_properties(cls, name, bases, dct)
   276     defined = set()
   311 
   277     for base in bases:
   312     if map_kind:
   278       if hasattr(base, '_properties'):
   313       _kind_map[cls.kind()] = cls
   279         property_keys = base._properties.keys()
       
   280         duplicate_properties = defined.intersection(property_keys)
       
   281         if duplicate_properties:
       
   282           raise DuplicatePropertyError(
       
   283               'Duplicate properties in base class %s already defined: %s' %
       
   284               (base.__name__, list(duplicate_properties)))
       
   285         defined.update(property_keys)
       
   286         cls._properties.update(base._properties)
       
   287 
       
   288     for attr_name in dct.keys():
       
   289       attr = dct[attr_name]
       
   290       if isinstance(attr, Property):
       
   291         check_reserved_word(attr_name)
       
   292         if attr_name in defined:
       
   293           raise DuplicatePropertyError('Duplicate property: %s' % attr_name)
       
   294         defined.add(attr_name)
       
   295         cls._properties[attr_name] = attr
       
   296         attr.__property_config__(cls, attr_name)
       
   297 
       
   298     _kind_map[cls.kind()] = cls
       
   299 
   314 
   300 
   315 
   301 class Property(object):
   316 class Property(object):
   302   """A Property is an attribute of a Model.
   317   """A Property is an attribute of a Model.
   303 
   318 
   464       The value converted for use as a model instance attribute.
   479       The value converted for use as a model instance attribute.
   465     """
   480     """
   466     return value
   481     return value
   467 
   482 
   468   def _attr_name(self):
   483   def _attr_name(self):
   469     """Attribute name we use for this property in model instances."""
   484     """Attribute name we use for this property in model instances.
       
   485 
       
   486     DO NOT USE THIS METHOD.
       
   487     """
   470     return '_' + self.name
   488     return '_' + self.name
   471 
   489 
   472   data_type = str
   490   data_type = str
   473 
   491 
   474   def datastore_type(self):
   492   def datastore_type(self):
   498   group are ACID.
   516   group are ACID.
   499   """
   517   """
   500 
   518 
   501   __metaclass__ = PropertiedClass
   519   __metaclass__ = PropertiedClass
   502 
   520 
   503   def __init__(self, parent=None, key_name=None, _app=None, **kwds):
   521   def __init__(self,
       
   522                parent=None,
       
   523                key_name=None,
       
   524                _app=None,
       
   525                _from_entity=False,
       
   526                **kwds):
   504     """Creates a new instance of this model.
   527     """Creates a new instance of this model.
   505 
   528 
   506     To create a new entity, you instantiate a model and then call save(),
   529     To create a new entity, you instantiate a model and then call save(),
   507     which saves the entity to the datastore:
   530     which saves the entity to the datastore:
   508 
   531 
   522     Args:
   545     Args:
   523       parent: Parent instance for this instance or None, indicating a top-
   546       parent: Parent instance for this instance or None, indicating a top-
   524         level instance.
   547         level instance.
   525       key_name: Name for new model instance.
   548       key_name: Name for new model instance.
   526       _app: Intentionally undocumented.
   549       _app: Intentionally undocumented.
       
   550       _from_entity: Intentionally undocumented.
   527       args: Keyword arguments mapping to properties of model.
   551       args: Keyword arguments mapping to properties of model.
   528     """
   552     """
   529     if key_name == '':
   553     if key_name == '':
   530       raise BadKeyError('Name cannot be empty.')
   554       raise BadKeyError('Name cannot be empty.')
   531     elif key_name is not None and not isinstance(key_name, basestring):
   555     elif key_name is not None and not isinstance(key_name, basestring):
   532       raise BadKeyError('Name must be string type, not %s' %
   556       raise BadKeyError('Name must be string type, not %s' %
   533                         key_name.__class__.__name__)
   557                         key_name.__class__.__name__)
   534 
   558 
   535     if parent is not None:
   559     if parent is not None:
   536       if not isinstance(parent, Model):
   560       if not isinstance(parent, (Model, Key)):
   537         raise TypeError('Expected Model type; received %s (is %s)' %
   561         raise TypeError('Expected Model type; received %s (is %s)' %
   538                         (parent, parent.__class__.__name__))
   562                         (parent, parent.__class__.__name__))
   539       if not parent.is_saved():
   563       if isinstance(parent, Model) and not parent.has_key():
   540         raise BadValueError(
   564         raise BadValueError(
   541             "%s instance must be saved before it can be used as a "
   565             "%s instance must have a complete key before it can be used as a "
   542             "parent." % parent.kind())
   566             "parent." % parent.kind())
   543 
   567       if isinstance(parent, Key):
   544     self._parent = parent
   568         self._parent_key = parent
       
   569         self._parent = None
       
   570       else:
       
   571         self._parent_key = parent.key()
       
   572         self._parent = parent
       
   573     else:
       
   574       self._parent_key = None
       
   575       self._parent = None
   545     self._entity = None
   576     self._entity = None
   546     self._key_name = key_name
   577     self._key_name = key_name
   547     self._app = _app
   578     self._app = _app
   548 
   579 
   549     properties = self.properties()
   580     properties = self.properties()
   550     for prop in self.properties().values():
   581     for prop in self.properties().values():
   551       if prop.name in kwds:
   582       if prop.name in kwds:
   552         value = kwds[prop.name]
   583         value = kwds[prop.name]
   553       else:
   584       else:
   554         value = prop.default_value()
   585         value = prop.default_value()
   555       prop.__set__(self, value)
   586       try:
       
   587         prop.__set__(self, value)
       
   588       except DerivedPropertyError, e:
       
   589         if prop.name in kwds and not _from_entity:
       
   590           raise
   556 
   591 
   557   def key(self):
   592   def key(self):
   558     """Unique key for this entity.
   593     """Unique key for this entity.
   559 
   594 
   560     This property is only available if this entity is already stored in the
   595     This property is only available if this entity is already stored in the
   567     Raises:
   602     Raises:
   568       NotSavedError when entity is not persistent.
   603       NotSavedError when entity is not persistent.
   569     """
   604     """
   570     if self.is_saved():
   605     if self.is_saved():
   571       return self._entity.key()
   606       return self._entity.key()
       
   607     elif self._key_name:
       
   608       parent = self._parent and self._parent.key()
       
   609       return Key.from_path(self.kind(), self._key_name, parent=parent)
   572     else:
   610     else:
   573       raise NotSavedError()
   611       raise NotSavedError()
   574 
   612 
   575   def _to_entity(self, entity):
   613   def _to_entity(self, entity):
   576     """Copies information from this model to provided entity.
   614     """Copies information from this model to provided entity.
   632       self._entity or a new Entity which is not stored on the instance.
   670       self._entity or a new Entity which is not stored on the instance.
   633     """
   671     """
   634     if self.is_saved():
   672     if self.is_saved():
   635       entity = self._entity
   673       entity = self._entity
   636     else:
   674     else:
   637       if self._parent is not None:
   675       if self._parent_key is not None:
       
   676         entity = _entity_class(self.kind(),
       
   677                                parent=self._parent_key,
       
   678                                name=self._key_name,
       
   679                                _app=self._app)
       
   680       elif self._parent is not None:
   638         entity = _entity_class(self.kind(),
   681         entity = _entity_class(self.kind(),
   639                                parent=self._parent._entity,
   682                                parent=self._parent._entity,
   640                                name=self._key_name,
   683                                name=self._key_name,
   641                                _app=self._app)
   684                                _app=self._app)
   642       else:
   685       else:
   666     Returns:
   709     Returns:
   667       True if object has been persisted to the datastore, otherwise False.
   710       True if object has been persisted to the datastore, otherwise False.
   668     """
   711     """
   669     return self._entity is not None
   712     return self._entity is not None
   670 
   713 
       
   714   def has_key(self):
       
   715     """Determine if this model instance has a complete key.
       
   716 
       
   717     Ids are not assigned until the data is saved to the Datastore, but
       
   718     instances with a key name always have a full key.
       
   719 
       
   720     Returns:
       
   721       True if the object has been persisted to the datastore or has a key_name,
       
   722       otherwise False.
       
   723     """
       
   724     return self.is_saved() or self._key_name
       
   725 
   671   def dynamic_properties(self):
   726   def dynamic_properties(self):
   672     """Returns a list of all dynamic properties defined for instance."""
   727     """Returns a list of all dynamic properties defined for instance."""
   673     return []
   728     return []
   674 
   729 
   675   def instance_properties(self):
   730   def instance_properties(self):
   681 
   736 
   682     Returns:
   737     Returns:
   683       Parent of contained entity or parent provided in constructor, None if
   738       Parent of contained entity or parent provided in constructor, None if
   684       instance has no parent.
   739       instance has no parent.
   685     """
   740     """
   686     if (self._parent is None and
   741     if self._parent is None:
   687         self._entity is not None and
   742       parent_key = self.parent_key()
   688         self._entity.parent() is not None):
   743       if parent_key is not None:
   689       self._parent = get(self._entity.parent())
   744         self._parent = get(parent_key)
   690     return self._parent
   745     return self._parent
   691 
   746 
   692   def parent_key(self):
   747   def parent_key(self):
   693     """Get the parent's key.
   748     """Get the parent's key.
   694 
   749 
   696     but still get information about the instances parent.
   751     but still get information about the instances parent.
   697 
   752 
   698     Returns:
   753     Returns:
   699       Parent key of entity, None if there is no parent.
   754       Parent key of entity, None if there is no parent.
   700     """
   755     """
   701     if self._parent is not None:
   756     if self._parent_key is not None:
       
   757       return self._parent_key
       
   758     elif self._parent is not None:
   702       return self._parent.key()
   759       return self._parent.key()
   703     elif self._entity is not None:
   760     elif self._entity is not None:
   704       return self._entity.parent()
   761       return self._entity.parent()
   705     else:
   762     else:
   706       return None
   763       return None
   922     if cls.kind() != entity.kind():
   979     if cls.kind() != entity.kind():
   923       raise KindError('Class %s cannot handle kind \'%s\'' %
   980       raise KindError('Class %s cannot handle kind \'%s\'' %
   924                       (repr(cls), entity.kind()))
   981                       (repr(cls), entity.kind()))
   925 
   982 
   926     entity_values = cls._load_entity_values(entity)
   983     entity_values = cls._load_entity_values(entity)
   927     instance = cls(None, **entity_values)
   984     instance = cls(None, _from_entity=True, **entity_values)
   928     instance._entity = entity
   985     instance._entity = entity
   929     del instance._key_name
   986     del instance._key_name
   930     return instance
   987     return instance
   931 
   988 
   932   @classmethod
   989   @classmethod
  1014 
  1071 
  1015 def delete(models):
  1072 def delete(models):
  1016   """Delete one or more Model instances.
  1073   """Delete one or more Model instances.
  1017 
  1074 
  1018   Args:
  1075   Args:
  1019     models: Model instance or list of Model instances.
  1076     models_or_keys: Model instance or list of Model instances.
  1020 
  1077 
  1021   Raises:
  1078   Raises:
  1022     TransactionFailedError if the data could not be committed.
  1079     TransactionFailedError if the data could not be committed.
  1023   """
  1080   """
  1024   models, multiple = datastore.NormalizeAndTypeCheck(models, Model)
  1081   models_or_keys, multiple = datastore.NormalizeAndTypeCheck(
  1025   entities = [model.key() for model in models]
  1082       models, (Model, Key, basestring))
  1026   keys = datastore.Delete(entities)
  1083   keys = []
       
  1084   for model_or_key in models_or_keys:
       
  1085     if isinstance(model_or_key, Model):
       
  1086       key = model_or_key = model_or_key.key()
       
  1087     elif isinstance(model_or_key, basestring):
       
  1088       key = model_or_key = Key(model_or_key)
       
  1089     else:
       
  1090       key = model_or_key
       
  1091     keys.append(key)
       
  1092   datastore.Delete(keys)
  1027 
  1093 
  1028 
  1094 
  1029 class Expando(Model):
  1095 class Expando(Model):
  1030   """Dynamically expandable model.
  1096   """Dynamically expandable model.
  1031 
  1097 
  1131                         type(value).__name__)
  1197                         type(value).__name__)
  1132       if self._dynamic_properties is None:
  1198       if self._dynamic_properties is None:
  1133         self._dynamic_properties = {}
  1199         self._dynamic_properties = {}
  1134       self._dynamic_properties[key] = value
  1200       self._dynamic_properties[key] = value
  1135     else:
  1201     else:
  1136       Model.__setattr__(self, key, value)
  1202       super(Expando, self).__setattr__(key, value)
  1137 
  1203 
  1138   def __getattr__(self, key):
  1204   def __getattr__(self, key):
  1139     """If no explicit attribute defined, retrieve value from entity.
  1205     """If no explicit attribute defined, retrieve value from entity.
  1140 
  1206 
  1141     Tries to get the value on the object normally, but failing that
  1207     Tries to get the value on the object normally, but failing that
  1209     property on the model.
  1275     property on the model.
  1210 
  1276 
  1211     Args:
  1277     Args:
  1212       entity: Entity which contain values to search dyanmic properties for.
  1278       entity: Entity which contain values to search dyanmic properties for.
  1213     """
  1279     """
  1214     entity_values = Model._load_entity_values(entity)
  1280     entity_values = super(Expando, cls)._load_entity_values(entity)
  1215     for key, value in entity.iteritems():
  1281     for key, value in entity.iteritems():
  1216       if key not in entity_values:
  1282       if key not in entity_values:
  1217         entity_values[str(key)] = value
  1283         entity_values[str(key)] = value
  1218     return entity_values
  1284     return entity_values
  1219 
  1285 
  1535       if ancestor.has_id_or_name():
  1601       if ancestor.has_id_or_name():
  1536         self.__ancestor = ancestor
  1602         self.__ancestor = ancestor
  1537       else:
  1603       else:
  1538         raise NotSavedError()
  1604         raise NotSavedError()
  1539     elif isinstance(ancestor, Model):
  1605     elif isinstance(ancestor, Model):
  1540       if ancestor.is_saved():
  1606       if ancestor.has_key():
  1541         self.__ancestor = ancestor.key()
  1607         self.__ancestor = ancestor.key()
  1542       else:
  1608       else:
  1543         raise NotSavedError()
  1609         raise NotSavedError()
  1544     else:
  1610     else:
  1545       raise TypeError('ancestor should be Key or Model')
  1611       raise TypeError('ancestor should be Key or Model')
  2168     value = super(ListProperty, self).validate(value)
  2234     value = super(ListProperty, self).validate(value)
  2169     if value is not None:
  2235     if value is not None:
  2170       if not isinstance(value, list):
  2236       if not isinstance(value, list):
  2171         raise BadValueError('Property %s must be a list' % self.name)
  2237         raise BadValueError('Property %s must be a list' % self.name)
  2172 
  2238 
  2173       if self.item_type in (int, long):
  2239       value = self.validate_list_contents(value)
  2174         item_type = (int, long)
  2240     return value
  2175       else:
  2241 
  2176         item_type = self.item_type
  2242   def validate_list_contents(self, value):
  2177 
  2243     """Validates that all items in the list are of the correct type.
  2178       for item in value:
  2244 
  2179         if not isinstance(item, item_type):
  2245     Returns:
  2180           if item_type == (int, long):
  2246       The validated list.
  2181             raise BadValueError('Items in the %s list must all be integers.' %
  2247 
  2182                                 self.name)
  2248     Raises:
  2183           else:
  2249       BadValueError if the list has items are not instances of the
  2184             raise BadValueError(
  2250       item_type given to the constructor.
  2185                 'Items in the %s list must all be %s instances' %
  2251     """
  2186                 (self.name, self.item_type.__name__))
  2252     if self.item_type in (int, long):
       
  2253       item_type = (int, long)
       
  2254     else:
       
  2255       item_type = self.item_type
       
  2256 
       
  2257     for item in value:
       
  2258       if not isinstance(item, item_type):
       
  2259         if item_type == (int, long):
       
  2260           raise BadValueError('Items in the %s list must all be integers.' %
       
  2261                               self.name)
       
  2262         else:
       
  2263           raise BadValueError(
       
  2264               'Items in the %s list must all be %s instances' %
       
  2265               (self.name, self.item_type.__name__))
  2187     return value
  2266     return value
  2188 
  2267 
  2189   def empty(self, value):
  2268   def empty(self, value):
  2190     """Is list property empty.
  2269     """Is list property empty.
  2191 
  2270 
  2207 
  2286 
  2208     Returns:
  2287     Returns:
  2209       Copy of the default value.
  2288       Copy of the default value.
  2210     """
  2289     """
  2211     return list(super(ListProperty, self).default_value())
  2290     return list(super(ListProperty, self).default_value())
       
  2291 
       
  2292   def get_value_for_datastore(self, model_instance):
       
  2293     """Get value from property to send to datastore.
       
  2294 
       
  2295     Returns:
       
  2296       validated list appropriate to save in the datastore.
       
  2297     """
       
  2298     return self.validate_list_contents(
       
  2299         super(ListProperty, self).get_value_for_datastore(model_instance))
  2212 
  2300 
  2213 
  2301 
  2214 class StringListProperty(ListProperty):
  2302 class StringListProperty(ListProperty):
  2215   """A property that stores a list of strings.
  2303   """A property that stores a list of strings.
  2216 
  2304 
  2366         - Object not of correct model type for reference.
  2454         - Object not of correct model type for reference.
  2367     """
  2455     """
  2368     if isinstance(value, datastore.Key):
  2456     if isinstance(value, datastore.Key):
  2369       return value
  2457       return value
  2370 
  2458 
  2371     if value is not None and not value.is_saved():
  2459     if value is not None and not value.has_key():
  2372       raise BadValueError(
  2460       raise BadValueError(
  2373           '%s instance must be saved before it can be stored as a '
  2461           '%s instance must have a complete key before it can be stored as a '
  2374           'reference' % self.reference_class.kind())
  2462           'reference' % self.reference_class.kind())
  2375 
  2463 
  2376     value = super(ReferenceProperty, self).validate(value)
  2464     value = super(ReferenceProperty, self).validate(value)
  2377 
  2465 
  2378     if value is not None and not isinstance(value, self.reference_class):
  2466     if value is not None and not isinstance(value, self.reference_class):