Skip to content

Commit

Permalink
Merge branch 'release/1.5.2'
Browse files Browse the repository at this point in the history
  • Loading branch information
kopertop committed Jan 5, 2015
2 parents 42e0713 + 46f23e8 commit e0f184c
Show file tree
Hide file tree
Showing 4 changed files with 299 additions and 196 deletions.
2 changes: 1 addition & 1 deletion botoweb/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
# WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
# IN THE SOFTWARE.
__version__ = '1.5.1'
__version__ = '1.5.2'
env = None
import logging
log = logging.getLogger('botoweb')
Expand Down
114 changes: 67 additions & 47 deletions botoweb/db/coremodel.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
# OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABIL-
# ITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
# SHALL THE AUTHOR BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
# SHALL THE AUTHOR BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
# WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
# IN THE SOFTWARE.
Expand All @@ -26,12 +26,12 @@
from decimal import Decimal
from datetime import datetime, date
import time
import boto
import logging
log = logging.getLogger('botoweb.db.model')


class ModelMeta(type):
"Metaclass for all Models"
'''Metaclass for all Models'''

def __init__(cls, name, bases, dict):
super(ModelMeta, cls).__init__(name, bases, dict)
Expand All @@ -57,11 +57,12 @@ def __init__(cls, name, bases, dict):
# 'Model' isn't defined yet, meaning we're looking at our own
# Model class, defined below.
pass



class Model(object):
__metaclass__ = ModelMeta
__consistent__ = False # Consistent is set off by default
_raw_item = None # Allows us to cache the raw items
__consistent__ = False # Consistent is set off by default
_raw_item = None # Allows us to cache the raw items
id = None

@classmethod
Expand All @@ -73,20 +74,20 @@ def get_lineage(cls):
@classmethod
def kind(cls):
return cls.__name__

@classmethod
def _get_by_id(cls, id, manager=None):
if not manager:
manager = cls._manager
return manager.get_object(cls, id)

@classmethod
def lookup(cls, *args,**kwargs):
"""
def lookup(cls, *args, **kwargs):
'''
returns get_by_id function value from the class that called lookup.
"""
'''
return cls.get_by_id(*args, **kwargs)

@classmethod
def get_by_id(cls, ids=None, parent=None):
if isinstance(ids, list):
Expand All @@ -99,7 +100,7 @@ def get_by_id(cls, ids=None, parent=None):

@classmethod
def get_by_key_name(cls, key_names, parent=None):
raise NotImplementedError, "Key Names are not currently supported"
raise NotImplementedError('Key Names are not currently supported')

@classmethod
def find(cls, limit=None, next_token=None, **params):
Expand All @@ -108,14 +109,31 @@ def find(cls, limit=None, next_token=None, **params):
q.filter('%s =' % key, value)
return q

@classmethod
def match_reference_property(cls, reference_property, model_instance):
'''
:param reference_property: Name (or list of names) of the reference property to match
:type reference_property: str or list
:param model_instance: Model instance to match to reference property
:type model_instance: :class:`~.Model`
:return: An iterator with the matched instances of this class
:rtype: :class:`~botoweb.db.query.Query`
'''
query = Query(cls)
if isinstance(reference_property, basestring):
return query.filter(reference_property + ' =', model_instance)
else:
props = ['%s =' % prop for prop in reference_property]
return query.filter(props, model_instance)

@classmethod
def all(cls, limit=None, next_token=None):
return cls.find(limit=limit, next_token=next_token)

@classmethod
def get_or_insert(key_name, **kw):
raise NotImplementedError, "get_or_insert not currently supported"
raise NotImplementedError('get_or_insert not currently supported')

@classmethod
def properties(cls, hidden=True):
properties = []
Expand Down Expand Up @@ -168,7 +186,7 @@ def __init__(self, id=None, **kw):
setattr(self, prop.name, prop.default_value())
except ValueError:
pass
if kw.has_key('manager'):
if 'manager' in kw:
self._manager = kw['manager']
self.id = id
for key in kw:
Expand All @@ -186,13 +204,13 @@ def __repr__(self):

def __str__(self):
return str(self.id)

def __eq__(self, other):
return other and isinstance(other, Model) and self.id == other.id

def _get_raw_item(self):
if not self._raw_item:
self._raw_item = self._manager.get_raw_item(self)
self._raw_item = self._manager.get_raw_item(self)
return self._raw_item

def load(self):
Expand All @@ -205,56 +223,56 @@ def reload(self):
self._manager.load_object(self)

def put(self, expected_value=None):
"""
'''
Save this object as it is, with an optional expected value
:param expected_value: Optional tuple of Attribute, and Value that
must be the same in order to save this object. If this
:param expected_value: Optional tuple of Attribute, and Value that
must be the same in order to save this object. If this
condition is not met, an SDBResponseError will be raised with a
Confict status code.
:type expected_value: tuple or list
:return: This object
:rtype: :class:`boto.sdb.db.model.Model`
"""
:rtype: :class:`~.Model`
'''
self._manager.save_object(self, expected_value)
return self

save = put

def put_attributes(self, attrs):
"""
'''
Save just these few attributes, not the whole object
:param attrs: Attributes to save, key->value dict
:type attrs: dict
:return: self
:rtype: :class:`boto.sdb.db.model.Model`
"""
assert(isinstance(attrs, dict)), "Argument must be a dict of key->values to save"
:rtype: :class:`~.Model`
'''
assert(isinstance(attrs, dict)), 'Argument must be a dict of key->values to save'
for prop_name in attrs:
value = attrs[prop_name]
prop = self.find_property(prop_name)
assert(prop), "Property not found: %s" % prop_name
assert(prop), 'Property not found: %s' % prop_name
self._manager.set_property(prop, self, prop_name, value)
self.reload()
return self

def delete_attributes(self, attrs):
"""
'''
Delete just these attributes, not the whole object.
:param attrs: Attributes to save, as a list of string names
:type attrs: list
:return: self
:rtype: :class:`boto.sdb.db.model.Model`
"""
assert(isinstance(attrs, list)), "Argument must be a list of names of keys to delete."
:rtype: :class:`~.Model`
'''
assert(isinstance(attrs, list)), 'Argument must be a list of names of keys to delete.'
self._manager.domain.delete_attributes(self.id, attrs)
self.reload()
return self

save_attributes = put_attributes

def delete(self):
self._manager.delete_object(self)

Expand All @@ -268,8 +286,8 @@ def set_manager(self, manager):
# serialization with the JSON module

def to_dict(self, recursive=False):
"""Get this generic object as simple DICT
that can be easily JSON encoded"""
'''Get this generic object as simple DICT
that can be easily JSON encoded'''
from botoweb.db.query import Query
from botoweb.db.property import CalculatedProperty, IntegerProperty, _ReverseReferenceProperty
ret = {'__type__': self.__class__.__name__, '__id__': self.id}
Expand Down Expand Up @@ -337,8 +355,8 @@ def to_dict(self, recursive=False):

@classmethod
def from_dict(cls, data):
"""Load this object from a dictionary as exported by
to_dict"""
'''Load this object from a dictionary as exported by
to_dict'''
obj = cls(data['__id__'])
obj._loaded = True
obj._validate = False
Expand All @@ -362,19 +380,19 @@ def from_dict(cls, data):
def _decode(cls, t, val, prop):
if val is None:
return val
if isinstance(val, dict) and val.has_key('__id__'):
if isinstance(val, dict) and '__id__' in val:
val = t(val['__id__'])
elif isinstance(val, dict) and val.has_key('ID'):
elif isinstance(val, dict) and 'ID' in val:
val = t(val['ID'])
elif t == datetime:
# Some exports turn this into an integer,
# which is the Unix Timestamp
if isinstance(val, int) or isinstance(val, Decimal):
val = datetime.fromtimestamp(val)
elif 'T' in val:
# If there a "T" in the datetime value, then
# If there a "T" in the datetime value, then
# it's a full date and time

# Remove fractional seconds, Z or +00:00Z time zone formatting
# Times are in UTC so formatting inconsistencies can be ignored
val = val[:19]
Expand All @@ -390,27 +408,31 @@ def _decode(cls, t, val, prop):
if not isinstance(val, list) and not isinstance(val, set):
val = [val]
val = [cls._decode(prop.item_type, v, prop) for v in val]
elif isinstance(t, tuple):
# Support for JSON multi-types
import json
if(val):
val = json.loads(val)
elif t not in (str, unicode, int):
val = t(val)
return val



def to_xml(self, doc=None):
xmlmanager = self.get_xmlmanager()
doc = xmlmanager.marshal_object(self, doc)
return doc

@classmethod
def find_subclass(cls, name):
"""Find a subclass with a given name"""
'''Find a subclass with a given name'''
if name == cls.__name__:
return cls
for sc in cls.__sub_classes__:
r = sc.find_subclass(name)
if r != None:
if r is not None:
return r


class Expando(Model):

def __setattr__(self, name, value):
Expand All @@ -431,5 +453,3 @@ def __getattr__(self, name):
object.__setattr__(self, name, value)
return value
raise AttributeError


0 comments on commit e0f184c

Please sign in to comment.