app/soc/logic/model.py
changeset 299 a1cc853a56e5
parent 263 9b39d93b677f
child 302 3b9c52170f46
--- a/app/soc/logic/model.py	Sun Oct 12 00:08:54 2008 +0000
+++ b/app/soc/logic/model.py	Sun Oct 12 00:12:53 2008 +0000
@@ -18,13 +18,17 @@
 """
 
 __authors__ = [
+  '"Todd Larsen" <tlarsen@google.com>',
+  '"Sverre Rabbelier" <sverer@rabbelier.nl>',
   '"Pawel Solyga" <pawel.solyga@gmail.com>',
-  '"Todd Larsen" <tlarsen@google.com>',
   ]
 
 
 from google.appengine.ext import db
 
+from soc.logic import key_name
+from soc.logic import out_of_band
+
 
 def getFullClassName(cls):
   """Returns fully-qualified module.class name string.""" 
@@ -176,34 +180,167 @@
   return None
 
 
-def updateModelProperties(model, **model_properties):
-  """Update existing model entity using supplied model properties.
+class BaseLogic():
+  """Base logic for entity classes.
+
+  The BaseLogic class functions specific to Entity classes by relying
+  on the the child-classes to implement _model, _name and _key_name
+  """
+
+  def _updateField(self, model, name, value):
+    """Hook called when a field is updated.
+
+    Base classes should override if any special actions need to be
+    taken when a field is updated. The field is not updated if the
+    method does not return a True value.
+    """
+
+    return True
 
-  Args:
-    model: a model entity
-    **model_properties: keyword arguments that correspond to model entity
-      properties and their values
+  def getFromKeyName(self, key_name):
+    """"Returns User entity for key_name or None if not found.
+-
+-    Args:
+-      key_name: key name of entity
+    """
+    return self._model.get_by_key_name(key_name)
+
+  def getFromFields(self, **kwargs):
+    """Returns the entity for a given link name, or None if not found.
+
+    Args:
+      link_name: a link name of the entity that uniquely identifies it
+    """
+    # lookup by Sponsor key name
+    key_name = self.getKeyNameForFields(**kwargs)
 
-  Returns:
-    the original model entity with any supplied properties changed 
-  """
-  def update():
-    return _unsafeUpdateModelProperties(model, **model_properties)
+    if key_name:
+      entity = self._model.get_by_key_name(key_name)
+    else:
+      entity = None
+
+    return entity
+
+  def getIfFields(self, **kwargs):
+    """Returns Sponsor entity for supplied link name if one exists.
+
+    Args:
+      link_name: a link name of the Sponsor that uniquely identifies it
+
+    Returns:
+      * None if link name is false.
+      * Sponsor entity for supplied link_name
 
-  return db.run_in_transaction(update)
+    Raises:
+      out_of_band.ErrorResponse if link name is not false, but no Sponsor entity
+      with the supplied link name exists in the Datastore
+    """
+    if not all(kwargs.values()):
+      # exit without error, to let view know that link_name was not supplied
+      return None
+
+    entity = self.getFromFields(**kwargs)
+
+    if entity:
+      # a Sponsor exist for this link_name, so return that Sponsor entity
+      return entity
+
+    fields = []
+
+    for key, value in kwargs.iteritems():
+      fields.extend('"%s" is "%s"' % (key, value))
+
+    # else: fields were supplied, but there is no Entity that has it
+    raise out_of_band.ErrorResponse(
+        'There is no %s with %s.' % (self._name, ''.join(fields)), status=404)
+
+  def getKeyNameForFields(self, **kwargs):
+    """Return a Datastore key_name for a Sponsor from the link name.
 
+    Args:
+      link_name: a link name of the entity that uniquely identifies it
+    """
+    if not all(kwargs.values()):
+      return None
 
-def _unsafeUpdateModelProperties(model, **model_properties):
-  """(see updateModelProperties)
+    return self._keyName(**kwargs)
+
+  def getForLimitAndOffset(self, limit, offset=0):
+    """Returns entities for given offset and limit or None if not found.
+
+    Args:
+      limit: max amount of entities to return
+      offset: optional offset in entities list which defines first entity to
+        return; default is zero (first entity)
+    """
+    query = self._model.all()
+    return query.fetch(limit, offset)
 
-  Like updateModelProperties(), but not run within a transaction. 
-  """
-  properties = model.properties()
+  def updateModelProperties(self, model, **model_properties):
+    """Update existing model entity using supplied model properties.
+
+    Args:
+      model: a model entity
+      **model_properties: keyword arguments that correspond to model entity
+        properties and their values
+
+    Returns:
+      the original model entity with any supplied properties changed
+    """
+    def update():
+      return self._unsafeUpdateModelProperties(model, **model_properties)
+
+    return db.run_in_transaction(update)
+
+  def _unsafeUpdateModelProperties(self, model, **model_properties):
+    """(see updateModelProperties)
+
+    Like updateModelProperties(), but not run within a transaction.
+    """
+    properties = model.properties()
 
-  for prop in properties.values():
-    if prop.name in model_properties:
-      value = model_properties[prop.name]
-      prop.__set__(model, value)
+    for prop in properties.values():
+      name = prop.name
+
+      if not name in self._skip_properties and name in model_properties:
+        value = model_properties[prop.name]
+
+        if self._updateField(model, name, value):
+          prop.__set__(model, value)
+
+    model.put()
+    return model
+
+  def updateOrCreateFromKeyName(self, properties, key_name):
+    """Update existing entity, or create new one with supplied properties.
+
+    Args:
+      link_name: a link_name of the entity that uniquely identifies it
+      **properties: keyword arguments that correspond to entity
+        properties and their values
 
-  model.put()
-  return model
\ No newline at end of file
+    Returns:
+      the entity corresponding to the key_name, with any supplied
+      properties changed, or a new entity now associated with the
+      supplied key_name and properties.
+    """
+
+    entity = self.getFromKeyName(key_name)
+
+    if not entity:
+      # entity did not exist, so create one in a transaction
+      entity = self._model.get_or_insert(key_name, **properties)
+
+    # there is no way to be sure if get_or_insert() returned a new entity or
+    # got an existing one due to a race, so update with sponsor_properties anyway,
+    # in a transaction
+    return self.updateModelProperties(entity, **properties)
+
+  def updateOrCreateFromFields(self, properties, **kwargs):
+    """Like updateOrCreateFromKeyName, but resolves **kwargs to a key_name first
+    """
+
+    # attempt to retrieve the existing entity
+    key_name  = self.getKeyNameForFields(**kwargs)
+
+    return self.updateOrCreateFromKeyName(properties, key_name)