1
# Copyright (c) 2006,2007,2008 Mitch Garnaat http://garnaat.org/
3
# Permission is hereby granted, free of charge, to any person obtaining a
4
# copy of this software and associated documentation files (the
5
# "Software"), to deal in the Software without restriction, including
6
# without limitation the rights to use, copy, modify, merge, publish, dis-
7
# tribute, sublicense, and/or sell copies of the Software, and to permit
8
# persons to whom the Software is furnished to do so, subject to the fol-
11
# The above copyright notice and this permission notice shall be included
12
# in all copies or substantial portions of the Software.
14
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
15
# OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABIL-
16
# ITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
17
# SHALL THE AUTHOR BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
18
# WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
22
from boto.sdb.db.manager import get_manager
23
from boto.sdb.db.property import Property
24
from boto.sdb.db.key import Key
25
from boto.sdb.db.query import Query
28
class ModelMeta(type):
29
"Metaclass for all Models"
31
def __init__(cls, name, bases, dict):
32
super(ModelMeta, cls).__init__(name, bases, dict)
33
# Make sure this is a subclass of Model - mainly copied from django ModelBase (thanks!)
34
cls.__sub_classes__ = []
36
if filter(lambda b: issubclass(b, Model), bases):
38
base.__sub_classes__.append(cls)
39
cls._manager = get_manager(cls)
40
# look for all of the Properties and set their names
41
for key in dict.keys():
42
if isinstance(dict[key], Property):
44
property.__property_config__(cls, key)
46
props = cls.properties()
48
if not prop.__class__.__name__.startswith('_'):
49
prop_names.append(prop.name)
50
setattr(cls, '_prop_names', prop_names)
52
# 'Model' isn't defined yet, meaning we're looking at our own
53
# Model class, defined below.
57
__metaclass__ = ModelMeta
58
__consistent__ = False # Consistent is set off by default
63
l = [c.__name__ for c in cls.mro()]
72
def _get_by_id(cls, id, manager=None):
74
manager = cls._manager
75
return manager.get_object(cls, id)
78
def get_by_id(cls, ids=None, parent=None):
79
if isinstance(ids, list):
80
objs = [cls._get_by_id(id) for id in ids]
83
return cls._get_by_id(ids)
85
get_by_ids = get_by_id
88
def get_by_key_name(cls, key_names, parent=None):
89
raise NotImplementedError, "Key Names are not currently supported"
92
def find(cls, limit=None, next_token=None, **params):
93
q = Query(cls, limit=limit, next_token=next_token)
94
for key, value in params.items():
95
q.filter('%s =' % key, value)
99
def lookup(cls, name, value):
100
return cls._manager.lookup(cls, name, value)
103
def all(cls, limit=None, next_token=None):
104
return cls.find(limit=limit, next_token=next_token)
107
def get_or_insert(key_name, **kw):
108
raise NotImplementedError, "get_or_insert not currently supported"
111
def properties(cls, hidden=True):
114
for key in cls.__dict__.keys():
115
prop = cls.__dict__[key]
116
if isinstance(prop, Property):
117
if hidden or not prop.__class__.__name__.startswith('_'):
118
properties.append(prop)
119
if len(cls.__bases__) > 0:
120
cls = cls.__bases__[0]
126
def find_property(cls, prop_name):
129
for key in cls.__dict__.keys():
130
prop = cls.__dict__[key]
131
if isinstance(prop, Property):
132
if not prop.__class__.__name__.startswith('_') and prop_name == prop.name:
134
if len(cls.__bases__) > 0:
135
cls = cls.__bases__[0]
141
def get_xmlmanager(cls):
142
if not hasattr(cls, '_xmlmanager'):
143
from boto.sdb.db.manager.xmlmanager import XMLManager
144
cls._xmlmanager = XMLManager(cls, None, None, None,
145
None, None, None, None, False)
146
return cls._xmlmanager
149
def from_xml(cls, fp):
150
xmlmanager = cls.get_xmlmanager()
151
return xmlmanager.unmarshal_object(fp)
153
def __init__(self, id=None, **kw):
155
# first initialize all properties to their default values
156
for prop in self.properties(hidden=False):
157
setattr(self, prop.name, prop.default_value())
158
if kw.has_key('manager'):
159
self._manager = kw['manager']
163
# We don't want any errors populating up when loading an object,
164
# so if it fails we just revert to it's default value
166
setattr(self, key, kw[key])
168
boto.log.exception(e)
171
return '%s<%s>' % (self.__class__.__name__, self.id)
176
def __eq__(self, other):
177
return other and isinstance(other, Model) and self.id == other.id
179
def _get_raw_item(self):
180
return self._manager.get_raw_item(self)
183
if self.id and not self._loaded:
184
self._manager.load_object(self)
187
self._manager.save_object(self)
192
self._manager.delete_object(self)
197
def set_manager(self, manager):
198
self._manager = manager
202
for prop in self.properties(hidden=False):
203
props[prop.name] = getattr(self, prop.name)
204
obj = {'properties' : props,
206
return {self.__class__.__name__ : obj}
208
def to_xml(self, doc=None):
209
xmlmanager = self.get_xmlmanager()
210
doc = xmlmanager.marshal_object(self, doc)
213
class Expando(Model):
215
def __setattr__(self, name, value):
216
if name in self._prop_names:
217
object.__setattr__(self, name, value)
218
elif name.startswith('_'):
219
object.__setattr__(self, name, value)
221
object.__setattr__(self, name, value)
223
self._manager.set_key_value(self, name, value)
224
object.__setattr__(self, name, value)
226
def __getattr__(self, name):
227
if not name.startswith('_'):
228
value = self._manager.get_key_value(self, name)
230
object.__setattr__(self, name, value)