thirdparty/google_appengine/google/appengine/ext/db/__init__.py
changeset 109 620f9b141567
child 149 f2e327a7c5de
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/thirdparty/google_appengine/google/appengine/ext/db/__init__.py	Tue Aug 26 21:49:54 2008 +0000
@@ -0,0 +1,2457 @@
+#!/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.
+#
+
+"""Simple, schema-based database abstraction layer for the datastore.
+
+Modeled after Django's abstraction layer on top of SQL databases,
+http://www.djangoproject.com/documentation/mode_api/. Ours is a little simpler
+and a lot less code because the datastore is so much simpler than SQL
+databases.
+
+The programming model is to declare Python subclasses of the Model class,
+declaring datastore properties as class members of that class. So if you want to
+publish a story with title, body, and created date, you would do it like this:
+
+    class Story(db.Model):
+      title = db.StringProperty()
+      body = db.TextProperty()
+      created = db.DateTimeProperty(auto_now_add=True)
+
+You can create a new Story in the datastore with this usage pattern:
+
+    story = Story(title='My title')
+    story.body = 'My body'
+    story.put()
+
+You query for Story entities using built in query interfaces that map directly
+to the syntax and semantics of the datastore:
+
+    stories = Story.all().filter('date >=', yesterday).order('-date')
+    for story in stories:
+      print story.title
+
+The Property declarations enforce types by performing validation on assignment.
+For example, the DateTimeProperty enforces that you assign valid datetime
+objects, and if you supply the "required" option for a property, you will not
+be able to assign None to that property.
+
+We also support references between models, so if a story has comments, you
+would represent it like this:
+
+    class Comment(db.Model):
+      story = db.ReferenceProperty(Story)
+      body = db.TextProperty()
+
+When you get a story out of the datastore, the story reference is resolved
+automatically the first time it is referenced, which makes it easy to use
+model instances without performing additional queries by hand:
+
+    comment = Comment.get(key)
+    print comment.story.title
+
+Likewise, you can access the set of comments that refer to each story through
+this property through a reverse reference called comment_set, which is a Query
+preconfigured to return all matching comments:
+
+    story = Story.get(key)
+    for comment in story.comment_set:
+       print comment.body
+
+"""
+
+
+
+
+
+
+import datetime
+import logging
+import time
+import urlparse
+
+from google.appengine.api import datastore
+from google.appengine.api import datastore_errors
+from google.appengine.api import datastore_types
+from google.appengine.api import users
+
+Error = datastore_errors.Error
+BadValueError = datastore_errors.BadValueError
+BadPropertyError = datastore_errors.BadPropertyError
+BadRequestError = datastore_errors.BadRequestError
+EntityNotFoundError = datastore_errors.EntityNotFoundError
+BadArgumentError = datastore_errors.BadArgumentError
+QueryNotFoundError = datastore_errors.QueryNotFoundError
+TransactionNotFoundError = datastore_errors.TransactionNotFoundError
+Rollback = datastore_errors.Rollback
+TransactionFailedError = datastore_errors.TransactionFailedError
+BadFilterError = datastore_errors.BadFilterError
+BadQueryError = datastore_errors.BadQueryError
+BadKeyError = datastore_errors.BadKeyError
+InternalError = datastore_errors.InternalError
+NeedIndexError = datastore_errors.NeedIndexError
+Timeout = datastore_errors.Timeout
+
+ValidationError = BadValueError
+
+Key = datastore_types.Key
+Category = datastore_types.Category
+Link = datastore_types.Link
+Email = datastore_types.Email
+GeoPt = datastore_types.GeoPt
+IM = datastore_types.IM
+PhoneNumber = datastore_types.PhoneNumber
+PostalAddress = datastore_types.PostalAddress
+Rating = datastore_types.Rating
+Text = datastore_types.Text
+Blob = datastore_types.Blob
+
+_kind_map = {}
+
+_SELF_REFERENCE = object()
+
+
+_RESERVED_WORDS = set(['key_name'])
+
+
+
+
+class NotSavedError(Error):
+  """Raised when a saved-object action is performed on a non-saved object."""
+
+
+class KindError(BadValueError):
+  """Raised when an entity is used with incorrect Model."""
+
+
+class PropertyError(Error):
+  """Raised when non-existent property is referenced."""
+
+
+class DuplicatePropertyError(Error):
+  """Raised when a property is duplicated in a model definition."""
+
+
+class ConfigurationError(Error):
+  """Raised when a property is improperly configured."""
+
+
+class ReservedWordError(Error):
+  """Raised when a property is defined for a reserved word."""
+
+
+_ALLOWED_PROPERTY_TYPES = set([
+    basestring,
+    str,
+    unicode,
+    bool,
+    int,
+    long,
+    float,
+    Key,
+    datetime.datetime,
+    datetime.date,
+    datetime.time,
+    Blob,
+    Text,
+    users.User,
+    Category,
+    Link,
+    Email,
+    GeoPt,
+    IM,
+    PhoneNumber,
+    PostalAddress,
+    Rating,
+    ])
+
+_ALLOWED_EXPANDO_PROPERTY_TYPES = set(_ALLOWED_PROPERTY_TYPES)
+_ALLOWED_EXPANDO_PROPERTY_TYPES.update((list, tuple, type(None)))
+
+
+def class_for_kind(kind):
+  """Return base-class responsible for implementing kind.
+
+  Necessary to recover the class responsible for implementing provided
+  kind.
+
+  Args:
+    kind: Entity kind string.
+
+  Returns:
+    Class implementation for kind.
+
+  Raises:
+    KindError when there is no implementation for kind.
+  """
+  try:
+    return _kind_map[kind]
+  except KeyError:
+    raise KindError('No implementation for kind \'%s\'' % kind)
+
+
+def check_reserved_word(attr_name):
+  """Raise an exception if attribute name is a reserved word.
+
+  Args:
+    attr_name: Name to check to see if it is a reserved word.
+
+  Raises:
+    ReservedWordError when attr_name is determined to be a reserved word.
+  """
+  if datastore_types.RESERVED_PROPERTY_NAME.match(attr_name):
+    raise ReservedWordError(
+        "Cannot define property.  All names both beginning and "
+        "ending with '__' are reserved.")
+
+  if attr_name in _RESERVED_WORDS or attr_name in dir(Model):
+    raise ReservedWordError(
+        "Cannot define property using reserved word '%(attr_name)s'. "
+        "If you would like to use this name in the datastore consider "
+        "using a different name like %(attr_name)s_ and adding "
+        "name='%(attr_name)s' to the parameter list of the property "
+        "definition." % locals())
+
+
+class PropertiedClass(type):
+  """Meta-class for initializing Model classes properties.
+
+  Used for initializing Properties defined in the context of a model.
+  By using a meta-class much of the configuration of a Property
+  descriptor becomes implicit.  By using this meta-class, descriptors
+  that are of class Model are notified about which class they
+  belong to and what attribute they are associated with and can
+  do appropriate initialization via __property_config__.
+
+  Duplicate properties are not permitted.
+  """
+
+  def __init__(cls, name, bases, dct):
+    """Initializes a class that might have property definitions.
+
+    This method is called when a class is created with the PropertiedClass
+    meta-class.
+
+    Loads all properties for this model and its base classes in to a dictionary
+    for easy reflection via the 'properties' method.
+
+    Configures each property defined in the new class.
+
+    Duplicate properties, either defined in the new class or defined separately
+    in two base classes are not permitted.
+
+    Properties may not assigned to names which are in the list of
+    _RESERVED_WORDS.  It is still possible to store a property using a reserved
+    word in the datastore by using the 'name' keyword argument to the Property
+    constructor.
+
+    Args:
+      cls: Class being initialized.
+      name: Name of new class.
+      bases: Base classes of new class.
+      dct: Dictionary of new definitions for class.
+
+    Raises:
+      DuplicatePropertyError when a property is duplicated either in the new
+        class or separately in two base classes.
+      ReservedWordError when a property is given a name that is in the list of
+        reserved words, attributes of Model and names of the form '__.*__'.
+    """
+    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
+
+
+class Property(object):
+  """A Property is an attribute of a Model.
+
+  It defines the type of the attribute, which determines how it is stored
+  in the datastore and how the property values are validated. Different property
+  types support different options, which change validation rules, default
+  values, etc.  The simplest example of a property is a StringProperty:
+
+     class Story(db.Model):
+       title = db.StringProperty()
+  """
+
+  creation_counter = 0
+
+  def __init__(self, verbose_name=None, name=None, default=None,
+               required=False, validator=None, choices=None):
+    """Initializes this Property with the given options.
+
+    Args:
+      verbose_name: User friendly name of property.
+      name: Storage name for property.  By default, uses attribute name
+        as it is assigned in the Model sub-class.
+      default: Default value for property if none is assigned.
+      required: Whether property is required.
+      validator: User provided method used for validation.
+      choices: User provided set of valid property values.
+    """
+    self.verbose_name = verbose_name
+    self.name = name
+    self.default = default
+    self.required = required
+    self.validator = validator
+    self.choices = choices
+    self.creation_counter = Property.creation_counter
+    Property.creation_counter += 1
+
+  def __property_config__(self, model_class, property_name):
+    """Configure property, connecting it to its model.
+
+    Configure the property so that it knows its property name and what class
+    it belongs to.
+
+    Args:
+      model_class: Model class which Property will belong to.
+      property_name: Name of property within Model instance to store property
+        values in.  By default this will be the property name preceded by
+        an underscore, but may change for different subclasses.
+    """
+    self.model_class = model_class
+    if self.name is None:
+      self.name = property_name
+
+  def __get__(self, model_instance, model_class):
+    """Returns the value for this property on the given model instance.
+
+    See http://docs.python.org/ref/descriptors.html for a description of
+    the arguments to this class and what they mean."""
+    if model_instance is None:
+      return self
+
+    try:
+      return getattr(model_instance, self._attr_name())
+    except AttributeError:
+      return None
+
+  def __set__(self, model_instance, value):
+    """Sets the value for this property on the given model instance.
+
+    See http://docs.python.org/ref/descriptors.html for a description of
+    the arguments to this class and what they mean.
+    """
+    value = self.validate(value)
+    setattr(model_instance, self._attr_name(), value)
+
+  def default_value(self):
+    """Default value for unassigned values.
+
+    Returns:
+      Default value as provided by __init__(default).
+    """
+    return self.default
+
+  def validate(self, value):
+    """Assert that provided value is compatible with this property.
+
+    Args:
+      value: Value to validate against this Property.
+
+    Returns:
+      A valid value, either the input unchanged or adapted to the
+      required type.
+
+    Raises:
+      BadValueError if the value is not appropriate for this
+      property in any way.
+    """
+    if self.empty(value):
+      if self.required:
+        raise BadValueError('Property %s is required' % self.name)
+    else:
+      if self.choices:
+        match = False
+        for choice in self.choices:
+          if choice == value:
+            match = True
+        if not match:
+          raise BadValueError('Property %s is %r; must be one of %r' %
+                              (self.name, value, self.choices))
+    if self.validator is not None:
+      self.validator(value)
+    return value
+
+  def empty(self, value):
+    """Determine if value is empty in the context of this property.
+
+    For most kinds, this is equivalent to "not value", but for kinds like
+    bool, the test is more subtle, so subclasses can override this method
+    if necessary.
+
+    Args:
+      value: Value to validate against this Property.
+
+    Returns:
+      True if this value is considered empty in the context of this Property
+      type, otherwise False.
+    """
+    return not value
+
+  def get_value_for_datastore(self, model_instance):
+    """Datastore representation of this property.
+
+    Looks for this property in the given model instance, and returns the proper
+    datastore representation of the value that can be stored in a datastore
+    entity.  Most critically, it will fetch the datastore key value for
+    reference properties.
+
+    Args:
+      model_instance: Instance to fetch datastore value from.
+
+    Returns:
+      Datastore representation of the model value in a form that is
+      appropriate for storing in the datastore.
+    """
+    return self.__get__(model_instance, model_instance.__class__)
+
+  def make_value_from_datastore(self, value):
+    """Native representation of this property.
+
+    Given a value retrieved from a datastore entity, return a value,
+    possibly converted, to be stored on the model instance.  Usually
+    this returns the value unchanged, but a property class may
+    override this when it uses a different datatype on the model
+    instance than on the entity.
+
+    This API is not quite symmetric with get_value_for_datastore(),
+    because the model instance on which to store the converted value
+    may not exist yet -- we may be collecting values to be passed to a
+    model constructor.
+
+    Args:
+      value: value retrieved from the datastore entity.
+
+    Returns:
+      The value converted for use as a model instance attribute.
+    """
+    return value
+
+  def _attr_name(self):
+    """Attribute name we use for this property in model instances."""
+    return '_' + self.name
+
+  data_type = str
+
+  def datastore_type(self):
+    """Deprecated backwards-compatible accessor method for self.data_type."""
+    return self.data_type
+
+
+class Model(object):
+  """Model is the superclass of all object entities in the datastore.
+
+  The programming model is to declare Python subclasses of the Model class,
+  declaring datastore properties as class members of that class. So if you want
+  to publish a story with title, body, and created date, you would do it like
+  this:
+
+    class Story(db.Model):
+      title = db.StringProperty()
+      body = db.TextProperty()
+      created = db.DateTimeProperty(auto_now_add=True)
+
+  A model instance can have a single parent.  Model instances without any
+  parent are root entities.  It is possible to efficiently query for
+  instances by their shared parent.  All descendents of a single root
+  instance also behave as a transaction group.  This means that when you
+  work one member of the group within a transaction all descendents of that
+  root join the transaction.  All operations within a transaction on this
+  group are ACID.
+  """
+
+  __metaclass__ = PropertiedClass
+
+  def __init__(self, parent=None, key_name=None, _app=None, **kwds):
+    """Creates a new instance of this model.
+
+    To create a new entity, you instantiate a model and then call save(),
+    which saves the entity to the datastore:
+
+       person = Person()
+       person.name = 'Bret'
+       person.save()
+
+    You can initialize properties in the model in the constructor with keyword
+    arguments:
+
+       person = Person(name='Bret')
+
+    We initialize all other properties to the default value (as defined by the
+    properties in the model definition) if they are not provided in the
+    constructor.
+
+    Args:
+      parent: Parent instance for this instance or None, indicating a top-
+        level instance.
+      key_name: Name for new model instance.
+      _app: Intentionally undocumented.
+      args: Keyword arguments mapping to properties of model.
+    """
+    if key_name == '':
+      raise BadKeyError('Name cannot be empty.')
+    elif key_name is not None and not isinstance(key_name, basestring):
+      raise BadKeyError('Name must be string type, not %s' %
+                        key_name.__class__.__name__)
+
+    if parent is not None:
+      if not isinstance(parent, Model):
+        raise TypeError('Expected Model type; received %s (is %s)' %
+                        (parent, parent.__class__.__name__))
+      if not parent.is_saved():
+        raise BadValueError(
+            "%s instance must be saved before it can be used as a "
+            "parent." % parent.kind())
+
+    self._parent = parent
+    self._entity = None
+    self._key_name = key_name
+    self._app = _app
+
+    properties = self.properties()
+    for prop in self.properties().values():
+      if prop.name in kwds:
+        value = kwds[prop.name]
+      else:
+        value = prop.default_value()
+      prop.__set__(self, value)
+
+  def key(self):
+    """Unique key for this entity.
+
+    This property is only available if this entity is already stored in the
+    datastore, so it is available if this entity was fetched returned from a
+    query, or after save() is called the first time for new entities.
+
+    Returns:
+      Datastore key of persisted entity.
+
+    Raises:
+      NotSavedError when entity is not persistent.
+    """
+    if self.is_saved():
+      return self._entity.key()
+    else:
+      raise NotSavedError()
+
+  def _to_entity(self, entity):
+    """Copies information from this model to provided entity.
+
+    Args:
+      entity: Entity to save information on.
+    """
+    for prop in self.properties().values():
+      datastore_value = prop.get_value_for_datastore(self)
+      if datastore_value == []:
+        try:
+          del entity[prop.name]
+        except KeyError:
+          pass
+      else:
+        entity[prop.name] = datastore_value
+
+  def _populate_internal_entity(self, _entity_class=datastore.Entity):
+    """Populates self._entity, saving its state to the datastore.
+
+    After this method is called, calling is_saved() will return True.
+
+    Returns:
+      Populated self._entity
+    """
+    self._entity = self._populate_entity(_entity_class=_entity_class)
+    if hasattr(self, '_key_name'):
+      del self._key_name
+    return self._entity
+
+  def put(self):
+    """Writes this model instance to the datastore.
+
+    If this instance is new, we add an entity to the datastore.
+    Otherwise, we update this instance, and the key will remain the
+    same.
+
+    Returns:
+      The key of the instance (either the existing key or a new key).
+
+    Raises:
+      TransactionFailedError if the data could not be committed.
+    """
+    self._populate_internal_entity()
+    return datastore.Put(self._entity)
+
+  save = put
+
+  def _populate_entity(self, _entity_class=datastore.Entity):
+    """Internal helper -- Populate self._entity or create a new one
+    if that one does not exist.  Does not change any state of the instance
+    other than the internal state of the entity.
+
+    This method is separate from _populate_internal_entity so that it is
+    possible to call to_xml without changing the state of an unsaved entity
+    to saved.
+
+    Returns:
+      self._entity or a new Entity which is not stored on the instance.
+    """
+    if self.is_saved():
+      entity = self._entity
+    else:
+      if self._parent is not None:
+        entity = _entity_class(self.kind(),
+                               parent=self._parent._entity,
+                               name=self._key_name,
+                               _app=self._app)
+      else:
+        entity = _entity_class(self.kind(),
+                               name=self._key_name,
+                               _app=self._app)
+
+    self._to_entity(entity)
+    return entity
+
+  def delete(self):
+    """Deletes this entity from the datastore.
+
+    Raises:
+      TransactionFailedError if the data could not be committed.
+    """
+    datastore.Delete(self.key())
+    self._entity = None
+
+
+  def is_saved(self):
+    """Determine if entity is persisted in the datastore.
+
+    New instances of Model do not start out saved in the data.  Objects which
+    are saved to or loaded from the Datastore will have a True saved state.
+
+    Returns:
+      True if object has been persisted to the datastore, otherwise False.
+    """
+    return self._entity is not None
+
+  def dynamic_properties(self):
+    """Returns a list of all dynamic properties defined for instance."""
+    return []
+
+  def instance_properties(self):
+    """Alias for dyanmic_properties."""
+    return self.dynamic_properties()
+
+  def parent(self):
+    """Get the parent of the model instance.
+
+    Returns:
+      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())
+    return self._parent
+
+  def parent_key(self):
+    """Get the parent's key.
+
+    This method is useful for avoiding a potential fetch from the datastore
+    but still get information about the instances parent.
+
+    Returns:
+      Parent key of entity, None if there is no parent.
+    """
+    if self._parent is not None:
+      return self._parent.key()
+    elif self._entity is not None:
+      return self._entity.parent()
+    else:
+      return None
+
+  def to_xml(self, _entity_class=datastore.Entity):
+    """Generate an XML representation of this model instance.
+
+    atom and gd:namespace properties are converted to XML according to their
+    respective schemas. For more information, see:
+
+      http://www.atomenabled.org/developers/syndication/
+      http://code.google.com/apis/gdata/common-elements.html
+    """
+    entity = self._populate_entity(_entity_class)
+    return entity.ToXml()
+
+  @classmethod
+  def get(cls, keys):
+    """Fetch instance from the datastore of a specific Model type using key.
+
+    We support Key objects and string keys (we convert them to Key objects
+    automatically).
+
+    Useful for ensuring that specific instance types are retrieved from the
+    datastore.  It also helps that the source code clearly indicates what
+    kind of object is being retreived.  Example:
+
+      story = Story.get(story_key)
+
+    Args:
+      keys: Key within datastore entity collection to find; or string key;
+        or list of Keys or string keys.
+
+    Returns:
+      If a single key was given: a Model instance associated with key
+      for provided class if it exists in the datastore, otherwise
+      None; if a list of keys was given: a list whose items are either
+      a Model instance or None.
+
+    Raises:
+      KindError if any of the retreived objects are not instances of the
+      type associated with call to 'get'.
+    """
+    results = get(keys)
+    if results is None:
+      return None
+
+    if isinstance(results, Model):
+      instances = [results]
+    else:
+      instances = results
+
+    for instance in instances:
+      if not(instance is None or isinstance(instance, cls)):
+        raise KindError('Kind %r is not a subclass of kind %r' %
+                        (instance.kind(), cls.kind()))
+
+    return results
+
+  @classmethod
+  def get_by_key_name(cls, key_names, parent=None):
+    """Get instance of Model class by its key's name.
+
+    Args:
+      key_names: A single key-name or a list of key-names.
+      parent: Parent of instances to get.  Can be a model or key.
+    """
+    if isinstance(parent, Model):
+      parent = parent.key()
+    key_names, multiple = datastore.NormalizeAndTypeCheck(key_names, basestring)
+    keys = [datastore.Key.from_path(cls.kind(), name, parent=parent)
+            for name in key_names]
+    if multiple:
+      return get(keys)
+    else:
+      return get(*keys)
+
+  @classmethod
+  def get_by_id(cls, ids, parent=None):
+    """Get instance of Model class by id.
+
+    Args:
+      key_names: A single id or a list of ids.
+      parent: Parent of instances to get.  Can be a model or key.
+    """
+    if isinstance(parent, Model):
+      parent = parent.key()
+    ids, multiple = datastore.NormalizeAndTypeCheck(ids, (int, long))
+    keys = [datastore.Key.from_path(cls.kind(), id, parent=parent)
+            for id in ids]
+    if multiple:
+      return get(keys)
+    else:
+      return get(*keys)
+
+  @classmethod
+  def get_or_insert(cls, key_name, **kwds):
+    """Transactionally retrieve or create an instance of Model class.
+
+    This acts much like the Python dictionary setdefault() method, where we
+    first try to retrieve a Model instance with the given key name and parent.
+    If it's not present, then we create a new instance (using the *kwds
+    supplied) and insert that with the supplied key name.
+
+    Subsequent calls to this method with the same key_name and parent will
+    always yield the same entity (though not the same actual object instance),
+    regardless of the *kwds supplied. If the specified entity has somehow
+    been deleted separately, then the next call will create a new entity and
+    return it.
+
+    If the 'parent' keyword argument is supplied, it must be a Model instance.
+    It will be used as the parent of the new instance of this Model class if
+    one is created.
+
+    This method is especially useful for having just one unique entity for
+    a specific identifier. Insertion/retrieval is done transactionally, which
+    guarantees uniqueness.
+
+    Example usage:
+
+      class WikiTopic(db.Model):
+        creation_date = db.DatetimeProperty(auto_now_add=True)
+        body = db.TextProperty(required=True)
+
+      # The first time through we'll create the new topic.
+      wiki_word = 'CommonIdioms'
+      topic = WikiTopic.get_or_insert(wiki_word,
+                                      body='This topic is totally new!')
+      assert topic.key().name() == 'CommonIdioms'
+      assert topic.body == 'This topic is totally new!'
+
+      # The second time through will just retrieve the entity.
+      overwrite_topic = WikiTopic.get_or_insert(wiki_word,
+                                      body='A totally different message!')
+      assert topic.key().name() == 'CommonIdioms'
+      assert topic.body == 'This topic is totally new!'
+
+    Args:
+      key_name: Key name to retrieve or create.
+      **kwds: Keyword arguments to pass to the constructor of the model class
+        if an instance for the specified key name does not already exist. If
+        an instance with the supplied key_name and parent already exists, the
+        rest of these arguments will be discarded.
+
+    Returns:
+      Existing instance of Model class with the specified key_name and parent
+      or a new one that has just been created.
+
+    Raises:
+      TransactionFailedError if the specified Model instance could not be
+      retrieved or created transactionally (due to high contention, etc).
+    """
+    def txn():
+      entity = cls.get_by_key_name(key_name, parent=kwds.get('parent'))
+      if entity is None:
+        entity = cls(key_name=key_name, **kwds)
+        entity.put()
+      return entity
+    return run_in_transaction(txn)
+
+  @classmethod
+  def all(cls):
+    """Returns a query over all instances of this model from the datastore.
+
+    Returns:
+      Query that will retrieve all instances from entity collection.
+    """
+    return Query(cls)
+
+  @classmethod
+  def gql(cls, query_string, *args, **kwds):
+    """Returns a query using GQL query string.
+
+    See appengine/ext/gql for more information about GQL.
+
+    Args:
+      query_string: properly formatted GQL query string with the
+        'SELECT * FROM <entity>' part omitted
+      *args: rest of the positional arguments used to bind numeric references
+        in the query.
+      **kwds: dictionary-based arguments (for named parameters).
+    """
+    return GqlQuery('SELECT * FROM %s %s' % (cls.kind(), query_string),
+                    *args, **kwds)
+
+  @classmethod
+  def _load_entity_values(cls, entity):
+    """Load dynamic properties from entity.
+
+    Loads attributes which are not defined as part of the entity in
+    to the model instance.
+
+    Args:
+      entity: Entity which contain values to search dyanmic properties for.
+    """
+    entity_values = {}
+    for prop in cls.properties().values():
+      if prop.name in entity:
+        try:
+          value = prop.make_value_from_datastore(entity[prop.name])
+          entity_values[prop.name] = value
+        except KeyError:
+          entity_values[prop.name] = []
+
+    return entity_values
+
+  @classmethod
+  def from_entity(cls, entity):
+    """Converts the entity representation of this model to an instance.
+
+    Converts datastore.Entity instance to an instance of cls.
+
+    Args:
+      entity: Entity loaded directly from datastore.
+
+    Raises:
+      KindError when cls is incorrect model for entity.
+    """
+    if cls.kind() != entity.kind():
+      raise KindError('Class %s cannot handle kind \'%s\'' %
+                      (repr(cls), entity.kind()))
+
+    entity_values = cls._load_entity_values(entity)
+    instance = cls(None, **entity_values)
+    instance._entity = entity
+    del instance._key_name
+    return instance
+
+  @classmethod
+  def kind(cls):
+    """Returns the datastore kind we use for this model.
+
+    We just use the name of the model for now, ignoring potential collisions.
+    """
+    return cls.__name__
+
+  @classmethod
+  def entity_type(cls):
+    """Soon to be removed alias for kind."""
+    return cls.kind()
+
+  @classmethod
+  def properties(cls):
+    """Returns a dictionary of all the properties defined for this model."""
+    return dict(cls._properties)
+
+  @classmethod
+  def fields(cls):
+    """Soon to be removed alias for properties."""
+    return cls.properties()
+
+
+def get(keys):
+  """Fetch the specific Model instance with the given key from the datastore.
+
+  We support Key objects and string keys (we convert them to Key objects
+  automatically).
+
+  Args:
+    keys: Key within datastore entity collection to find; or string key;
+      or list of Keys or string keys.
+
+    Returns:
+      If a single key was given: a Model instance associated with key
+      for if it exists in the datastore, otherwise None; if a list of
+      keys was given: a list whose items are either a Model instance or
+      None.
+  """
+  keys, multiple = datastore.NormalizeAndTypeCheckKeys(keys)
+  try:
+    entities = datastore.Get(keys)
+  except datastore_errors.EntityNotFoundError:
+    assert not multiple
+    return None
+  models = []
+  for entity in entities:
+    if entity is None:
+      model = None
+    else:
+      cls1 = class_for_kind(entity.kind())
+      model = cls1.from_entity(entity)
+    models.append(model)
+  if multiple:
+    return models
+  assert len(models) == 1
+  return models[0]
+
+
+def put(models):
+  """Store one or more Model instances.
+
+  Args:
+    models: Model instance or list of Model instances.
+
+  Returns:
+    A Key or a list of Keys (corresponding to the argument's plurality).
+
+  Raises:
+    TransactionFailedError if the data could not be committed.
+  """
+  models, multiple = datastore.NormalizeAndTypeCheck(models, Model)
+  entities = [model._populate_internal_entity() for model in models]
+  keys = datastore.Put(entities)
+  if multiple:
+    return keys
+  assert len(keys) == 1
+  return keys[0]
+
+save = put
+
+
+def delete(models):
+  """Delete one or more Model instances.
+
+  Args:
+    models: 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)
+
+
+class Expando(Model):
+  """Dynamically expandable model.
+
+  An Expando does not require (but can still benefit from) the definition
+  of any properties before it can be used to store information in the
+  datastore.  Properties can be added to an expando object by simply
+  performing an assignment.  The assignment of properties is done on
+  an instance by instance basis, so it is possible for one object of an
+  expando type to have different properties from another or even the same
+  properties with different types.  It is still possible to define
+  properties on an expando, allowing those properties to behave the same
+  as on any other model.
+
+  Example:
+    import datetime
+
+    class Song(db.Expando):
+      title = db.StringProperty()
+
+    crazy = Song(title='Crazy like a diamond',
+                 author='Lucy Sky',
+                 publish_date='yesterday',
+                 rating=5.0)
+
+    hoboken = Song(title='The man from Hoboken',
+                   author=['Anthony', 'Lou'],
+                   publish_date=datetime.datetime(1977, 5, 3))
+
+    crazy.last_minute_note=db.Text('Get a train to the station.')
+
+  Possible Uses:
+
+    One use of an expando is to create an object without any specific
+    structure and later, when your application mature and it in the right
+    state, change it to a normal model object and define explicit properties.
+
+  Additional exceptions for expando:
+
+    Protected attributes (ones whose names begin with '_') cannot be used
+    as dynamic properties.  These are names that are reserved for protected
+    transient (non-persisted) attributes.
+
+  Order of lookup:
+
+    When trying to set or access an attribute value, any other defined
+    properties, such as methods and other values in __dict__ take precedence
+    over values in the datastore.
+
+    1 - Because it is not possible for the datastore to know what kind of
+        property to store on an undefined expando value, setting a property to
+        None is the same as deleting it form the expando.
+
+    2 - Persistent variables on Expando must not begin with '_'.  These
+        variables considered to be 'protected' in Python, and are used
+        internally.
+
+    3 - Expando's dynamic properties are not able to store empty lists.
+        Attempting to assign an empty list to a dynamic property will raise
+        ValueError.  Static properties on Expando can still support empty
+        lists but like normal Model properties is restricted from using
+        None.
+  """
+
+  _dynamic_properties = None
+
+  def __init__(self, parent=None, key_name=None, _app=None, **kwds):
+    """Creates a new instance of this expando model.
+
+    Args:
+      parent: Parent instance for this instance or None, indicating a top-
+        level instance.
+      key_name: Name for new model instance.
+      _app: Intentionally undocumented.
+      args: Keyword arguments mapping to properties of model.
+    """
+    super(Expando, self).__init__(parent, key_name, _app, **kwds)
+    self._dynamic_properties = {}
+    for prop, value in kwds.iteritems():
+      if prop not in self.properties() and value is not None:
+        setattr(self, prop, value)
+
+  def __setattr__(self, key, value):
+    """Dynamically set field values that are not defined.
+
+    Tries to set the value on the object normally, but failing that
+    sets the value on the contained entity.
+
+    Args:
+      key: Name of attribute.
+      value: Value to set for attribute.  Must be compatible with
+        datastore.
+
+    Raises:
+      ValueError on attempt to assign empty list.
+    """
+    check_reserved_word(key)
+    if key[:1] != '_' and key not in self.properties():
+      if value == []:
+        raise ValueError('Cannot store empty list to dynamic property %s' %
+                         key)
+      if type(value) not in _ALLOWED_EXPANDO_PROPERTY_TYPES:
+        raise TypeError("Expando cannot accept values of type '%s'." %
+                        type(value).__name__)
+      if self._dynamic_properties is None:
+        self._dynamic_properties = {}
+      self._dynamic_properties[key] = value
+    else:
+      Model.__setattr__(self, key, value)
+
+  def __getattr__(self, key):
+    """If no explicit attribute defined, retrieve value from entity.
+
+    Tries to get the value on the object normally, but failing that
+    retrieves value from contained entity.
+
+    Args:
+      key: Name of attribute.
+
+    Raises:
+      AttributeError when there is no attribute for key on object or
+        contained entity.
+    """
+    if self._dynamic_properties and key in self._dynamic_properties:
+      return self._dynamic_properties[key]
+    else:
+      return getattr(super(Expando, self), key)
+
+  def __delattr__(self, key):
+    """Remove attribute from expando.
+
+    Expando is not like normal entities in that undefined fields
+    can be removed.
+
+    Args:
+      key: Dynamic property to be deleted.
+    """
+    if self._dynamic_properties and key in self._dynamic_properties:
+      del self._dynamic_properties[key]
+    else:
+      object.__delattr__(self, key)
+
+  def dynamic_properties(self):
+    """Determine which properties are particular to instance of entity.
+
+    Returns:
+      Set of names which correspond only to the dynamic properties.
+    """
+    if self._dynamic_properties is None:
+      return []
+    return self._dynamic_properties.keys()
+
+  def _to_entity(self, entity):
+    """Store to entity, deleting dynamic properties that no longer exist.
+
+    When the expando is saved, it is possible that a given property no longer
+    exists.  In this case, the property will be removed from the saved instance.
+
+    Args:
+      entity: Entity which will receive dynamic properties.
+    """
+    super(Expando, self)._to_entity(entity)
+
+    if self._dynamic_properties is None:
+      self._dynamic_properties = {}
+
+    for key, value in self._dynamic_properties.iteritems():
+      entity[key] = value
+
+    all_properties = set(self._dynamic_properties.iterkeys())
+    all_properties.update(self.properties().iterkeys())
+    for key in entity.keys():
+      if key not in all_properties:
+        del entity[key]
+
+  @classmethod
+  def _load_entity_values(cls, entity):
+    """Load dynamic properties from entity.
+
+    Expando needs to do a second pass to add the entity values which were
+    ignored by Model because they didn't have an corresponding predefined
+    property on the model.
+
+    Args:
+      entity: Entity which contain values to search dyanmic properties for.
+    """
+    entity_values = Model._load_entity_values(entity)
+    for key, value in entity.iteritems():
+      if key not in entity_values:
+        entity_values[str(key)] = value
+    return entity_values
+
+
+class _BaseQuery(object):
+  """Base class for both Query and GqlQuery."""
+
+  def __init__(self, model_class):
+    """Constructor."
+
+      Args:
+        model_class: Model class from which entities are constructed.
+    """
+    self._model_class = model_class
+
+  def _get_query(self):
+    """Subclass must override (and not call their super method).
+
+    Returns:
+      A datastore.Query instance representing the query.
+    """
+    raise NotImplementedError
+
+  def run(self):
+    """Iterator for this query.
+
+    If you know the number of results you need, consider fetch() instead,
+    or use a GQL query with a LIMIT clause. It's more efficient.
+
+    Returns:
+      Iterator for this query.
+    """
+    return _QueryIterator(self._model_class, iter(self._get_query().Run()))
+
+  def __iter__(self):
+    """Iterator for this query.
+
+    If you know the number of results you need, consider fetch() instead,
+    or use a GQL query with a LIMIT clause. It's more efficient.
+    """
+    return self.run()
+
+  def get(self):
+    """Get first result from this.
+
+    Beware: get() ignores the LIMIT clause on GQL queries.
+
+    Returns:
+      First result from running the query if there are any, else None.
+    """
+    results = self.fetch(1)
+    try:
+      return results[0]
+    except IndexError:
+      return None
+
+  def count(self, limit=None):
+    """Number of entities this query fetches.
+
+    Beware: count() ignores the LIMIT clause on GQL queries.
+
+    Args:
+      limit, a number. If there are more results than this, stop short and
+      just return this number. Providing this argument makes the count
+      operation more efficient.
+
+    Returns:
+      Number of entities this query fetches.
+    """
+    return self._get_query().Count(limit=limit)
+
+  def fetch(self, limit, offset=0):
+    """Return a list of items selected using SQL-like limit and offset.
+
+    Whenever possible, use fetch() instead of iterating over the query
+    results with run() or __iter__() . fetch() is more efficient.
+
+    Beware: fetch() ignores the LIMIT clause on GQL queries.
+
+    Args:
+      limit: Maximum number of results to return.
+      offset: Optional number of results to skip first; default zero.
+
+    Returns:
+      A list of db.Model instances.  There may be fewer than 'limit'
+      results if there aren't enough results to satisfy the request.
+    """
+    accepted = (int, long)
+    if not (isinstance(limit, accepted) and isinstance(offset, accepted)):
+      raise TypeError('Arguments to fetch() must be integers')
+    if limit < 0 or offset < 0:
+      raise ValueError('Arguments to fetch() must be >= 0')
+    if limit == 0:
+      return []
+    raw = self._get_query().Get(limit, offset)
+    return map(self._model_class.from_entity, raw)
+
+  def __getitem__(self, arg):
+    """Support for query[index] and query[start:stop].
+
+    Beware: this ignores the LIMIT clause on GQL queries.
+
+    Args:
+      arg: Either a single integer, corresponding to the query[index]
+        syntax, or a Python slice object, corresponding to the
+        query[start:stop] or query[start:stop:step] syntax.
+
+    Returns:
+      A single Model instance when the argument is a single integer.
+      A list of Model instances when the argument is a slice.
+    """
+    if isinstance(arg, slice):
+      start, stop, step = arg.start, arg.stop, arg.step
+      if start is None:
+        start = 0
+      if stop is None:
+        raise ValueError('Open-ended slices are not supported')
+      if step is None:
+        step = 1
+      if start < 0 or stop < 0 or step != 1:
+        raise ValueError(
+            'Only slices with start>=0, stop>=0, step==1 are supported')
+      limit = stop - start
+      if limit < 0:
+        return []
+      return self.fetch(limit, start)
+    elif isinstance(arg, (int, long)):
+      if arg < 0:
+        raise ValueError('Only indices >= 0 are supported')
+      results = self.fetch(1, arg)
+      if results:
+        return results[0]
+      else:
+        raise IndexError('The query returned fewer than %d results' % (arg+1))
+    else:
+      raise TypeError('Only integer indices and slices are supported')
+
+
+class _QueryIterator(object):
+  """Wraps the datastore iterator to return Model instances.
+
+  The datastore returns entities. We wrap the datastore iterator to
+  return Model instances instead.
+  """
+
+  def __init__(self, model_class, datastore_iterator):
+    """Iterator constructor
+
+    Args:
+      model_class: Model class from which entities are constructed.
+      datastore_iterator: Underlying datastore iterator.
+    """
+    self.__model_class = model_class
+    self.__iterator = datastore_iterator
+
+  def __iter__(self):
+    """Iterator on self.
+
+    Returns:
+      Self.
+    """
+    return self
+
+  def next(self):
+    """Get next Model instance in query results.
+
+    Returns:
+      Next model instance.
+
+    Raises:
+      StopIteration when there are no more results in query.
+    """
+    return self.__model_class.from_entity(self.__iterator.next())
+
+
+def _normalize_query_parameter(value):
+  """Make any necessary type conversions to a query parameter.
+
+  The following conversions are made:
+    - Model instances are converted to Key instances.  This is necessary so
+      that querying reference properties will work.
+    - datetime.date objects are converted to datetime.datetime objects (see
+      _date_to_datetime for details on this conversion).  This is necessary so
+      that querying date properties with date objects will work.
+    - datetime.time objects are converted to datetime.datetime objects (see
+      _time_to_datetime for details on this conversion).  This is necessary so
+      that querying time properties with time objects will work.
+
+  Args:
+    value: The query parameter value.
+
+  Returns:
+    The input value, or a converted value if value matches one of the
+    conversions specified above.
+  """
+  if isinstance(value, Model):
+    value = value.key()
+  if (isinstance(value, datetime.date) and
+      not isinstance(value, datetime.datetime)):
+    value = _date_to_datetime(value)
+  elif isinstance(value, datetime.time):
+    value = _time_to_datetime(value)
+  return value
+
+
+class Query(_BaseQuery):
+  """A Query instance queries over instances of Models.
+
+  You construct a query with a model class, like this:
+
+     class Story(db.Model):
+       title = db.StringProperty()
+       date = db.DateTimeProperty()
+
+     query = Query(Story)
+
+  You modify a query with filters and orders like this:
+
+     query.filter('title =', 'Foo')
+     query.order('-date')
+     query.ancestor(key_or_model_instance)
+
+  Every query can return an iterator, so you access the results of a query
+  by iterating over it:
+
+     for story in query:
+       print story.title
+
+  For convenience, all of the filtering and ordering methods return "self",
+  so the easiest way to use the query interface is to cascade all filters and
+  orders in the iterator line like this:
+
+     for story in Query(story).filter('title =', 'Foo').order('-date'):
+       print story.title
+  """
+
+  def __init__(self, model_class):
+    """Constructs a query over instances of the given Model.
+
+    Args:
+      model_class: Model class to build query for.
+    """
+    super(Query, self).__init__(model_class)
+    self.__query_set = {}
+    self.__orderings = []
+    self.__ancestor = None
+
+  def _get_query(self, _query_class=datastore.Query):
+    query = _query_class(self._model_class.kind(), self.__query_set)
+    if self.__ancestor is not None:
+      query.Ancestor(self.__ancestor)
+    query.Order(*self.__orderings)
+    return query
+
+  def filter(self, property_operator, value):
+    """Add filter to query.
+
+    Args:
+      property_operator: string with the property and operator to filter by.
+      value: the filter value.
+
+    Returns:
+      Self to support method chaining.
+    """
+    if isinstance(value, (list, tuple)):
+      raise BadValueError('Filtering on lists is not supported')
+
+    value = _normalize_query_parameter(value)
+    datastore._AddOrAppend(self.__query_set, property_operator, value)
+    return self
+
+  def order(self, property):
+    """Set order of query result.
+
+    To use descending order, prepend '-' (minus) to the property name, e.g.,
+    '-date' rather than 'date'.
+
+    Args:
+      property: Property to sort on.
+
+    Returns:
+      Self to support method chaining.
+
+    Raises:
+      PropertyError if invalid property name is provided.
+    """
+    if property.startswith('-'):
+      property = property[1:]
+      order = datastore.Query.DESCENDING
+    else:
+      order = datastore.Query.ASCENDING
+
+    if not issubclass(self._model_class, Expando):
+      if property not in self._model_class.properties():
+        raise PropertyError('Invalid property name \'%s\'' % property)
+
+    self.__orderings.append((property, order))
+    return self
+
+  def ancestor(self, ancestor):
+    """Sets an ancestor for this query.
+
+    This restricts the query to only return results that descend from
+    a given model instance. In other words, all of the results will
+    have the ancestor as their parent, or parent's parent, etc.  The
+    ancestor itself is also a possible result!
+
+    Args:
+      ancestor: Model or Key (that has already been saved)
+
+    Returns:
+      Self to support method chaining.
+
+    Raises:
+      TypeError if the argument isn't a Key or Model; NotSavedError
+      if it is, but isn't saved yet.
+    """
+    if isinstance(ancestor, datastore.Key):
+      if ancestor.has_id_or_name():
+        self.__ancestor = ancestor
+      else:
+        raise NotSavedError()
+    elif isinstance(ancestor, Model):
+      if ancestor.is_saved():
+        self.__ancestor = ancestor.key()
+      else:
+        raise NotSavedError()
+    else:
+      raise TypeError('ancestor should be Key or Model')
+    return self
+
+
+class GqlQuery(_BaseQuery):
+  """A Query class that uses GQL query syntax instead of .filter() etc."""
+
+  def __init__(self, query_string, *args, **kwds):
+    """Constructor.
+
+    Args:
+      query_string: Properly formatted GQL query string.
+      *args: Positional arguments used to bind numeric references in the query.
+      **kwds: Dictionary-based arguments for named references.
+    """
+    from google.appengine.ext import gql
+    app = kwds.pop('_app', None)
+    self._proto_query = gql.GQL(query_string, _app=app)
+    super(GqlQuery, self).__init__(class_for_kind(self._proto_query._entity))
+    self.bind(*args, **kwds)
+
+  def bind(self, *args, **kwds):
+    """Bind arguments (positional or keyword) to the query.
+
+    Note that you can also pass arguments directly to the query
+    constructor.  Each time you call bind() the previous set of
+    arguments is replaced with the new set.  This is useful because
+    the hard work in in parsing the query; so if you expect to be
+    using the same query with different sets of arguments, you should
+    hold on to the GqlQuery() object and call bind() on it each time.
+
+    Args:
+      *args: Positional arguments used to bind numeric references in the query.
+      **kwds: Dictionary-based arguments for named references.
+    """
+    self._args = []
+    for arg in args:
+      self._args.append(_normalize_query_parameter(arg))
+    self._kwds = {}
+    for name, arg in kwds.iteritems():
+      self._kwds[name] = _normalize_query_parameter(arg)
+
+  def run(self):
+    """Override _BaseQuery.run() so the LIMIT clause is handled properly."""
+    query_run = self._proto_query.Run(*self._args, **self._kwds)
+    return _QueryIterator(self._model_class, iter(query_run))
+
+  def _get_query(self):
+    return self._proto_query.Bind(self._args, self._kwds)
+
+
+class TextProperty(Property):
+  """A string that can be longer than 500 bytes.
+
+  This type should be used for large text values to make sure the datastore
+  has good performance for queries.
+  """
+
+  def validate(self, value):
+    """Validate text property.
+
+    Returns:
+      A valid value.
+
+    Raises:
+      BadValueError if property is not instance of 'Text'.
+    """
+    if value is not None and not isinstance(value, Text):
+      try:
+        value = Text(value)
+      except TypeError, err:
+        raise BadValueError('Property %s must be convertible '
+                            'to a Text instance (%s)' % (self.name, err))
+    value = super(TextProperty, self).validate(value)
+    if value is not None and not isinstance(value, Text):
+      raise BadValueError('Property %s must be a Text instance' % self.name)
+    return value
+
+  data_type = Text
+
+
+class StringProperty(Property):
+  """A textual property, which can be multi- or single-line."""
+
+  def __init__(self, verbose_name=None, multiline=False, **kwds):
+    """Construct string property.
+
+    Args:
+      verbose_name: Verbose name is always first parameter.
+      multi-line: Carriage returns permitted in property.
+    """
+    super(StringProperty, self).__init__(verbose_name, **kwds)
+    self.multiline = multiline
+
+  def validate(self, value):
+    """Validate string property.
+
+    Returns:
+      A valid value.
+
+    Raises:
+      BadValueError if property is not multi-line but value is.
+    """
+    value = super(StringProperty, self).validate(value)
+    if value is not None and not isinstance(value, basestring):
+      raise BadValueError(
+          'Property %s must be a str or unicode instance, not a %s'
+          % (self.name, type(value).__name__))
+    if not self.multiline and value and value.find('\n') != -1:
+      raise BadValueError('Property %s is not multi-line' % self.name)
+    return value
+
+  data_type = basestring
+
+
+class _CoercingProperty(Property):
+  """A Property subclass that extends validate() to coerce to self.data_type."""
+
+  def validate(self, value):
+    """Coerce values (except None) to self.data_type.
+
+    Args:
+      value: The value to be validated and coerced.
+
+    Returns:
+      The coerced and validated value.  It is guaranteed that this is
+      either None or an instance of self.data_type; otherwise an exception
+      is raised.
+
+    Raises:
+      BadValueError if the value could not be validated or coerced.
+    """
+    value = super(_CoercingProperty, self).validate(value)
+    if value is not None and not isinstance(value, self.data_type):
+      value = self.data_type(value)
+    return value
+
+
+class CategoryProperty(_CoercingProperty):
+  """A property whose values are Category instances."""
+
+  data_type = Category
+
+
+class LinkProperty(_CoercingProperty):
+  """A property whose values are Link instances."""
+
+  def validate(self, value):
+    value = super(LinkProperty, self).validate(value)
+    if value is not None:
+      scheme, netloc, path, query, fragment = urlparse.urlsplit(value)
+      if not scheme or not netloc:
+        raise BadValueError('Property %s must be a full URL (\'%s\')' %
+                            (self.name, value))
+    return value
+
+  data_type = Link
+
+URLProperty = LinkProperty
+
+
+class EmailProperty(_CoercingProperty):
+  """A property whose values are Email instances."""
+
+  data_type = Email
+
+
+class GeoPtProperty(_CoercingProperty):
+  """A property whose values are GeoPt instances."""
+
+  data_type = GeoPt
+
+
+class IMProperty(_CoercingProperty):
+  """A property whose values are IM instances."""
+
+  data_type = IM
+
+
+class PhoneNumberProperty(_CoercingProperty):
+  """A property whose values are PhoneNumber instances."""
+
+  data_type = PhoneNumber
+
+
+class PostalAddressProperty(_CoercingProperty):
+  """A property whose values are PostalAddress instances."""
+
+  data_type = PostalAddress
+
+
+class BlobProperty(Property):
+  """A string that can be longer than 500 bytes.
+
+  This type should be used for large binary values to make sure the datastore
+  has good performance for queries.
+  """
+
+  def validate(self, value):
+    """Validate blob property.
+
+    Returns:
+      A valid value.
+
+    Raises:
+      BadValueError if property is not instance of 'Blob'.
+    """
+    if value is not None and not isinstance(value, Blob):
+      try:
+        value = Blob(value)
+      except TypeError, err:
+        raise BadValueError('Property %s must be convertible '
+                            'to a Blob instance (%s)' % (self.name, err))
+    value = super(BlobProperty, self).validate(value)
+    if value is not None and not isinstance(value, Blob):
+      raise BadValueError('Property %s must be a Blob instance' % self.name)
+    return value
+
+  data_type = Blob
+
+
+class DateTimeProperty(Property):
+  """The base class of all of our date/time properties.
+
+  We handle common operations, like converting between time tuples and
+  datetime instances.
+  """
+
+  def __init__(self, verbose_name=None, auto_now=False, auto_now_add=False,
+               **kwds):
+    """Construct a DateTimeProperty
+
+    Args:
+      verbose_name: Verbose name is always first parameter.
+      auto_now: Date/time property is updated with the current time every time
+        it is saved to the datastore.  Useful for properties that want to track
+        the modification time of an instance.
+      auto_now_add: Date/time is set to the when its instance is created.
+        Useful for properties that record the creation time of an entity.
+    """
+    super(DateTimeProperty, self).__init__(verbose_name, **kwds)
+    self.auto_now = auto_now
+    self.auto_now_add = auto_now_add
+
+  def validate(self, value):
+    """Validate datetime.
+
+    Returns:
+      A valid value.
+
+    Raises:
+      BadValueError if property is not instance of 'datetime'.
+    """
+    value = super(DateTimeProperty, self).validate(value)
+    if value and not isinstance(value, self.data_type):
+      raise BadValueError('Property %s must be a %s' %
+                          (self.name, self.data_type.__name__))
+    return value
+
+  def default_value(self):
+    """Default value for datetime.
+
+    Returns:
+      value of now() as appropriate to the date-time instance if auto_now
+      or auto_now_add is set, else user configured default value implementation.
+    """
+    if self.auto_now or self.auto_now_add:
+      return self.now()
+    return Property.default_value(self)
+
+  def get_value_for_datastore(self, model_instance):
+    """Get value from property to send to datastore.
+
+    Returns:
+      now() as appropriate to the date-time instance in the odd case where
+      auto_now is set to True, else the default implementation.
+    """
+    if self.auto_now:
+      return self.now()
+    else:
+      return super(DateTimeProperty,
+                   self).get_value_for_datastore(model_instance)
+
+  data_type = datetime.datetime
+
+  @staticmethod
+  def now():
+    """Get now as a full datetime value.
+
+    Returns:
+      'now' as a whole timestamp, including both time and date.
+    """
+    return datetime.datetime.now()
+
+
+def _date_to_datetime(value):
+  """Convert a date to a datetime for datastore storage.
+
+  Args:
+    value: A datetime.date object.
+
+  Returns:
+    A datetime object with time set to 0:00.
+  """
+  assert isinstance(value, datetime.date)
+  return datetime.datetime(value.year, value.month, value.day)
+
+
+def _time_to_datetime(value):
+  """Convert a time to a datetime for datastore storage.
+
+  Args:
+    value: A datetime.time object.
+
+  Returns:
+    A datetime object with date set to 1970-01-01.
+  """
+  assert isinstance(value, datetime.time)
+  return datetime.datetime(1970, 1, 1,
+                           value.hour, value.minute, value.second,
+                           value.microsecond)
+
+
+class DateProperty(DateTimeProperty):
+  """A date property, which stores a date without a time."""
+
+
+  @staticmethod
+  def now():
+    """Get now as a date datetime value.
+
+    Returns:
+      'date' part of 'now' only.
+    """
+    return datetime.datetime.now().date()
+
+  def validate(self, value):
+    """Validate date.
+
+    Returns:
+      A valid value.
+
+    Raises:
+      BadValueError if property is not instance of 'date',
+      or if it is an instance of 'datetime' (which is a subclass
+      of 'date', but for all practical purposes a different type).
+    """
+    value = super(DateProperty, self).validate(value)
+    if isinstance(value, datetime.datetime):
+      raise BadValueError('Property %s must be a %s, not a datetime' %
+                          (self.name, self.data_type.__name__))
+    return value
+
+  def get_value_for_datastore(self, model_instance):
+    """Get value from property to send to datastore.
+
+    We retrieve a datetime.date from the model instance and return a
+    datetime.datetime instance with the time set to zero.
+
+    See base class method documentation for details.
+    """
+    value = super(DateProperty, self).get_value_for_datastore(model_instance)
+    if value is not None:
+      assert isinstance(value, datetime.date)
+      value = _date_to_datetime(value)
+    return value
+
+  def make_value_from_datastore(self, value):
+    """Native representation of this property.
+
+    We receive a datetime.datetime retrieved from the entity and return
+    a datetime.date instance representing its date portion.
+
+    See base class method documentation for details.
+    """
+    if value is not None:
+      assert isinstance(value, datetime.datetime)
+      value = value.date()
+    return value
+
+  data_type = datetime.date
+
+
+class TimeProperty(DateTimeProperty):
+  """A time property, which stores a time without a date."""
+
+
+  @staticmethod
+  def now():
+    """Get now as a time datetime value.
+
+    Returns:
+      'time' part of 'now' only.
+    """
+    return datetime.datetime.now().time()
+
+  def get_value_for_datastore(self, model_instance):
+    """Get value from property to send to datastore.
+
+    We retrieve a datetime.time from the model instance and return a
+    datetime.datetime instance with the date set to 1/1/1970.
+
+    See base class method documentation for details.
+    """
+    value = super(TimeProperty, self).get_value_for_datastore(model_instance)
+    if value is not None:
+      assert isinstance(value, datetime.time), repr(value)
+      value = _time_to_datetime(value)
+    return value
+
+  def make_value_from_datastore(self, value):
+    """Native representation of this property.
+
+    We receive a datetime.datetime retrieved from the entity and return
+    a datetime.date instance representing its time portion.
+
+    See base class method documentation for details.
+    """
+    if value is not None:
+      assert isinstance(value, datetime.datetime)
+      value = value.time()
+    return value
+
+  data_type = datetime.time
+
+
+class IntegerProperty(Property):
+  """An integer property."""
+
+  def validate(self, value):
+    """Validate integer property.
+
+    Returns:
+      A valid value.
+
+    Raises:
+      BadValueError if value is not an integer or long instance.
+    """
+    value = super(IntegerProperty, self).validate(value)
+    if value is None:
+      return value
+    if not isinstance(value, (int, long)) or isinstance(value, bool):
+      raise BadValueError('Property %s must be an int or long, not a %s'
+                          % (self.name, type(value).__name__))
+    if value < -0x8000000000000000 or value > 0x7fffffffffffffff:
+      raise BadValueError('Property %s must fit in 64 bits' % self.name)
+    return value
+
+  data_type = int
+
+  def empty(self, value):
+    """Is integer property empty.
+
+    0 is not an empty value.
+
+    Returns:
+      True if value is None, else False.
+    """
+    return value is None
+
+
+class RatingProperty(_CoercingProperty, IntegerProperty):
+  """A property whose values are Rating instances."""
+
+  data_type = Rating
+
+
+class FloatProperty(Property):
+  """A float property."""
+
+  def validate(self, value):
+    """Validate float.
+
+    Returns:
+      A valid value.
+
+    Raises:
+      BadValueError if property is not instance of 'float'.
+    """
+    value = super(FloatProperty, self).validate(value)
+    if value is not None and not isinstance(value, float):
+      raise BadValueError('Property %s must be a float' % self.name)
+    return value
+
+  data_type = float
+
+  def empty(self, value):
+    """Is float property empty.
+
+    0.0 is not an empty value.
+
+    Returns:
+      True if value is None, else False.
+    """
+    return value is None
+
+
+class BooleanProperty(Property):
+  """A boolean property."""
+
+  def validate(self, value):
+    """Validate boolean.
+
+    Returns:
+      A valid value.
+
+    Raises:
+      BadValueError if property is not instance of 'bool'.
+    """
+    value = super(BooleanProperty, self).validate(value)
+    if value is not None and not isinstance(value, bool):
+      raise BadValueError('Property %s must be a bool' % self.name)
+    return value
+
+  data_type = bool
+
+  def empty(self, value):
+    """Is boolean property empty.
+
+    False is not an empty value.
+
+    Returns:
+      True if value is None, else False.
+    """
+    return value is None
+
+
+class UserProperty(Property):
+  """A user property."""
+
+  def __init__(self, verbose_name=None, name=None,
+               required=False, validator=None, choices=None):
+    """Initializes this Property with the given options.
+
+    Do not assign user properties a default value.
+
+    Args:
+      verbose_name: User friendly name of property.
+      name: Storage name for property.  By default, uses attribute name
+        as it is assigned in the Model sub-class.
+      default: Default value for property if none is assigned.
+      required: Whether property is required.
+      validator: User provided method used for validation.
+      choices: User provided set of valid property values.
+    """
+    super(UserProperty, self).__init__(verbose_name, name,
+                                       required=required,
+                                       validator=validator,
+                                       choices=choices)
+
+  def validate(self, value):
+    """Validate user.
+
+    Returns:
+      A valid value.
+
+    Raises:
+      BadValueError if property is not instance of 'User'.
+    """
+    value = super(UserProperty, self).validate(value)
+    if value is not None and not isinstance(value, users.User):
+      raise BadValueError('Property %s must be a User' % self.name)
+    return value
+
+  data_type = users.User
+
+
+
+class ListProperty(Property):
+  """A property that stores a list of things.
+
+  This is a parameterized property; the parameter must be a valid
+  non-list data type, and all items must conform to this type.
+  """
+
+  def __init__(self, item_type, verbose_name=None, default=None, **kwds):
+    """Construct ListProperty.
+
+    Args:
+      item_type: Type for the list items; must be one of the allowed property
+        types.
+      verbose_name: Optional verbose name.
+      default: Optional default value; if omitted, an empty list is used.
+      **kwds: Optional additional keyword arguments, passed to base class.
+
+    Note that the only permissible value for 'required' is True.
+    """
+    if not isinstance(item_type, type):
+      raise TypeError('Item type should be a type object')
+    if item_type not in _ALLOWED_PROPERTY_TYPES:
+      raise ValueError('Item type %s is not acceptable' % item_type.__name__)
+    if 'required' in kwds and kwds['required'] is not True:
+      raise ValueError('List values must be required')
+    if default is None:
+      default = []
+    self.item_type = item_type
+    super(ListProperty, self).__init__(verbose_name,
+                                       required=True,
+                                       default=default,
+                                       **kwds)
+
+  def validate(self, value):
+    """Validate list.
+
+    Returns:
+      A valid value.
+
+    Raises:
+      BadValueError if property is not a list whose items are instances of
+      the item_type given to the constructor.
+    """
+    value = super(ListProperty, self).validate(value)
+    if value is not None:
+      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__))
+    return value
+
+  def empty(self, value):
+    """Is list property empty.
+
+    [] is not an empty value.
+
+    Returns:
+      True if value is None, else false.
+    """
+    return value is None
+
+  data_type = list
+
+  def default_value(self):
+    """Default value for list.
+
+    Because the property supplied to 'default' is a static value,
+    that value must be shallow copied to prevent all fields with
+    default values from sharing the same instance.
+
+    Returns:
+      Copy of the default value.
+    """
+    return list(super(ListProperty, self).default_value())
+
+
+def StringListProperty(verbose_name=None, default=None, **kwds):
+  """A shorthand for the most common type of ListProperty.
+
+  Args:
+    verbose_name: Optional verbose name.
+    default: Optional default value; if omitted, an empty list is used.
+    **kwds: Optional additional keyword arguments, passed to ListProperty().
+
+  Returns:
+    A ListProperty instance whose item type is basestring and whose other
+    arguments are whatever was passed here.
+  """
+  return ListProperty(basestring, verbose_name, default, **kwds)
+
+
+class ReferenceProperty(Property):
+  """A property that represents a many-to-one reference to another model.
+
+  For example, a reference property in model A that refers to model B forms
+  a many-to-one relationship from A to B: every instance of A refers to a
+  single B instance, and every B instance can have many A instances refer
+  to it.
+  """
+
+  def __init__(self,
+               reference_class=None,
+               verbose_name=None,
+               collection_name=None,
+               **attrs):
+    """Construct ReferenceProperty.
+
+    Args:
+      reference_class: Which model class this property references.
+      verbose_name: User friendly name of property.
+      collection_name: If provided, alternate name of collection on
+        reference_class to store back references.  Use this to allow
+        a Model to have multiple fields which refer to the same class.
+    """
+    super(ReferenceProperty, self).__init__(verbose_name, **attrs)
+
+    self.collection_name = collection_name
+
+    if reference_class is None:
+      reference_class = Model
+    if not ((isinstance(reference_class, type) and
+             issubclass(reference_class, Model)) or
+            reference_class is _SELF_REFERENCE):
+      raise KindError('reference_class must be Model or _SELF_REFERENCE')
+    self.reference_class = self.data_type = reference_class
+
+  def __property_config__(self, model_class, property_name):
+    """Loads all of the references that point to this model.
+
+    We need to do this to create the ReverseReferenceProperty properties for
+    this model and create the <reference>_set attributes on the referenced
+    model, e.g.:
+
+       class Story(db.Model):
+         title = db.StringProperty()
+       class Comment(db.Model):
+         story = db.ReferenceProperty(Story)
+       story = Story.get(id)
+       print [c for c in story.comment_set]
+
+    In this example, the comment_set property was created based on the reference
+    from Comment to Story (which is inherently one to many).
+
+    Args:
+      model_class: Model class which will have its reference properties
+        initialized.
+      property_name: Name of property being configured.
+
+    Raises:
+      DuplicatePropertyError if referenced class already has the provided
+        collection name as a property.
+    """
+    super(ReferenceProperty, self).__property_config__(model_class,
+                                                       property_name)
+
+    if self.reference_class is _SELF_REFERENCE:
+      self.reference_class = self.data_type = model_class
+
+    if self.collection_name is None:
+      self.collection_name = '%s_set' % (model_class.__name__.lower())
+    if hasattr(self.reference_class, self.collection_name):
+      raise DuplicatePropertyError('Class %s already has property %s'
+                                   % (self.reference_class.__name__,
+                                      self.collection_name))
+    setattr(self.reference_class,
+            self.collection_name,
+            _ReverseReferenceProperty(model_class, property_name))
+
+  def __get__(self, model_instance, model_class):
+    """Get reference object.
+
+    This method will fetch unresolved entities from the datastore if
+    they are not already loaded.
+
+    Returns:
+      ReferenceProperty to Model object if property is set, else None.
+    """
+    if model_instance is None:
+      return self
+    if hasattr(model_instance, self.__id_attr_name()):
+      reference_id = getattr(model_instance, self.__id_attr_name())
+    else:
+      reference_id = None
+    if reference_id is not None:
+      resolved = getattr(model_instance, self.__resolved_attr_name())
+      if resolved is not None:
+        return resolved
+      else:
+        instance = get(reference_id)
+        if instance is None:
+          raise Error('ReferenceProperty failed to be resolved')
+        setattr(model_instance, self.__resolved_attr_name(), instance)
+        return instance
+    else:
+      return None
+
+  def __set__(self, model_instance, value):
+    """Set reference."""
+    value = self.validate(value)
+    if value is not None:
+      if isinstance(value, datastore.Key):
+        setattr(model_instance, self.__id_attr_name(), value)
+        setattr(model_instance, self.__resolved_attr_name(), None)
+      else:
+        setattr(model_instance, self.__id_attr_name(), value.key())
+        setattr(model_instance, self.__resolved_attr_name(), value)
+    else:
+      setattr(model_instance, self.__id_attr_name(), None)
+      setattr(model_instance, self.__resolved_attr_name(), None)
+
+  def get_value_for_datastore(self, model_instance):
+    """Get key of reference rather than reference itself."""
+    return getattr(model_instance, self.__id_attr_name())
+
+  def validate(self, value):
+    """Validate reference.
+
+    Returns:
+      A valid value.
+
+    Raises:
+      BadValueError for the following reasons:
+        - Value is not saved.
+        - Object not of correct model type for reference.
+    """
+    if isinstance(value, datastore.Key):
+      return value
+
+    if value is not None and not value.is_saved():
+      raise BadValueError(
+          '%s instance must be saved before it can be stored as a '
+          'reference' % self.reference_class.kind())
+
+    value = super(ReferenceProperty, self).validate(value)
+
+    if value is not None and not isinstance(value, self.reference_class):
+      raise KindError('Property %s must be an instance of %s' %
+                            (self.name, self.reference_class.kind()))
+
+    return value
+
+  def __id_attr_name(self):
+    """Get attribute of referenced id.
+
+    Returns:
+      Attribute where to store id of referenced entity.
+    """
+    return self._attr_name()
+
+  def __resolved_attr_name(self):
+    """Get attribute of resolved attribute.
+
+    The resolved attribute is where the actual loaded reference instance is
+    stored on the referring model instance.
+
+    Returns:
+      Attribute name of where to store resolved reference model instance.
+    """
+    return '_RESOLVED' + self._attr_name()
+
+
+Reference = ReferenceProperty
+
+
+def SelfReferenceProperty(verbose_name=None, collection_name=None, **attrs):
+  """Create a self reference.
+
+  Function for declaring a self referencing property on a model.
+
+  Example:
+    class HtmlNode(db.Model):
+      parent = db.SelfReferenceProperty('Parent', 'children')
+
+  Args:
+    verbose_name: User friendly name of property.
+    collection_name: Name of collection on model.
+
+  Raises:
+    ConfigurationError if reference_class provided as parameter.
+  """
+  if 'reference_class' in attrs:
+    raise ConfigurationError(
+        'Do not provide reference_class to self-reference.')
+  return ReferenceProperty(_SELF_REFERENCE,
+                           verbose_name,
+                           collection_name,
+                           **attrs)
+
+
+SelfReference = SelfReferenceProperty
+
+
+class _ReverseReferenceProperty(Property):
+  """The inverse of the Reference property above.
+
+  We construct reverse references automatically for the model to which
+  the Reference property is pointing to create the one-to-many property for
+  that model.  For example, if you put a Reference property in model A that
+  refers to model B, we automatically create a _ReverseReference property in
+  B called a_set that can fetch all of the model A instances that refer to
+  that instance of model B.
+  """
+
+  def __init__(self, model, prop):
+    """Constructor for reverse reference.
+
+    Constructor does not take standard values of other property types.
+
+    Args:
+      model: Model that this property is a collection of.
+      property: Foreign property on referred model that points back to this
+        properties entity.
+    """
+    self.__model = model
+    self.__property = prop
+
+  def __get__(self, model_instance, model_class):
+    """Fetches collection of model instances of this collection property."""
+    if model_instance is not None:
+      query = Query(self.__model)
+      return query.filter(self.__property + ' =', model_instance.key())
+    else:
+      return self
+
+  def __set__(self, model_instance, value):
+    """Not possible to set a new collection."""
+    raise BadValueError('Virtual property is read-only')
+
+
+run_in_transaction = datastore.RunInTransaction
+
+RunInTransaction = run_in_transaction