OLD | NEW |
| (Empty) |
1 # Copyright (c) 2006,2007,2008 Mitch Garnaat http://garnaat.org/ | |
2 # | |
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- | |
9 # lowing conditions: | |
10 # | |
11 # The above copyright notice and this permission notice shall be included | |
12 # in all copies or substantial portions of the Software. | |
13 # | |
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 | |
20 # IN THE SOFTWARE. | |
21 | |
22 from boto.exception import SDBPersistenceError | |
23 from boto.sdb.persist import get_manager, object_lister | |
24 from boto.sdb.persist.property import Property, ScalarProperty | |
25 import uuid | |
26 | |
27 class SDBBase(type): | |
28 "Metaclass for all SDBObjects" | |
29 def __init__(cls, name, bases, dict): | |
30 super(SDBBase, cls).__init__(name, bases, dict) | |
31 # Make sure this is a subclass of SDBObject - mainly copied from django
ModelBase (thanks!) | |
32 try: | |
33 if filter(lambda b: issubclass(b, SDBObject), bases): | |
34 # look for all of the Properties and set their names | |
35 for key in dict.keys(): | |
36 if isinstance(dict[key], Property): | |
37 property = dict[key] | |
38 property.set_name(key) | |
39 prop_names = [] | |
40 props = cls.properties() | |
41 for prop in props: | |
42 prop_names.append(prop.name) | |
43 setattr(cls, '_prop_names', prop_names) | |
44 except NameError: | |
45 # 'SDBObject' isn't defined yet, meaning we're looking at our own | |
46 # SDBObject class, defined below. | |
47 pass | |
48 | |
49 class SDBObject(object): | |
50 __metaclass__ = SDBBase | |
51 | |
52 _manager = get_manager() | |
53 | |
54 @classmethod | |
55 def get_lineage(cls): | |
56 l = [c.__name__ for c in cls.mro()] | |
57 l.reverse() | |
58 return '.'.join(l) | |
59 | |
60 @classmethod | |
61 def get(cls, id=None, **params): | |
62 if params.has_key('manager'): | |
63 manager = params['manager'] | |
64 else: | |
65 manager = cls._manager | |
66 if manager.domain and id: | |
67 a = cls._manager.domain.get_attributes(id, '__type__') | |
68 if a.has_key('__type__'): | |
69 return cls(id, manager) | |
70 else: | |
71 raise SDBPersistenceError('%s object with id=%s does not exist'
% (cls.__name__, id)) | |
72 else: | |
73 rs = cls.find(**params) | |
74 try: | |
75 obj = rs.next() | |
76 except StopIteration: | |
77 raise SDBPersistenceError('%s object matching query does not exi
st' % cls.__name__) | |
78 try: | |
79 rs.next() | |
80 except StopIteration: | |
81 return obj | |
82 raise SDBPersistenceError('Query matched more than 1 item') | |
83 | |
84 @classmethod | |
85 def find(cls, **params): | |
86 if params.has_key('manager'): | |
87 manager = params['manager'] | |
88 del params['manager'] | |
89 else: | |
90 manager = cls._manager | |
91 keys = params.keys() | |
92 if len(keys) > 4: | |
93 raise SDBPersistenceError('Too many fields, max is 4') | |
94 parts = ["['__type__'='%s'] union ['__lineage__'starts-with'%s']" % (cls
.__name__, cls.get_lineage())] | |
95 properties = cls.properties() | |
96 for key in keys: | |
97 found = False | |
98 for property in properties: | |
99 if property.name == key: | |
100 found = True | |
101 if isinstance(property, ScalarProperty): | |
102 checker = property.checker | |
103 parts.append("['%s' = '%s']" % (key, checker.to_string(p
arams[key]))) | |
104 else: | |
105 raise SDBPersistenceError('%s is not a searchable field'
% key) | |
106 if not found: | |
107 raise SDBPersistenceError('%s is not a valid field' % key) | |
108 query = ' intersection '.join(parts) | |
109 if manager.domain: | |
110 rs = manager.domain.query(query) | |
111 else: | |
112 rs = [] | |
113 return object_lister(None, rs, manager) | |
114 | |
115 @classmethod | |
116 def list(cls, max_items=None, manager=None): | |
117 if not manager: | |
118 manager = cls._manager | |
119 if manager.domain: | |
120 rs = manager.domain.query("['__type__' = '%s']" % cls.__name__, max_
items=max_items) | |
121 else: | |
122 rs = [] | |
123 return object_lister(cls, rs, manager) | |
124 | |
125 @classmethod | |
126 def properties(cls): | |
127 properties = [] | |
128 while cls: | |
129 for key in cls.__dict__.keys(): | |
130 if isinstance(cls.__dict__[key], Property): | |
131 properties.append(cls.__dict__[key]) | |
132 if len(cls.__bases__) > 0: | |
133 cls = cls.__bases__[0] | |
134 else: | |
135 cls = None | |
136 return properties | |
137 | |
138 # for backwards compatibility | |
139 find_properties = properties | |
140 | |
141 def __init__(self, id=None, manager=None): | |
142 if manager: | |
143 self._manager = manager | |
144 self.id = id | |
145 if self.id: | |
146 self._auto_update = True | |
147 if self._manager.domain: | |
148 attrs = self._manager.domain.get_attributes(self.id, '__type__') | |
149 if len(attrs.keys()) == 0: | |
150 raise SDBPersistenceError('Object %s: not found' % self.id) | |
151 else: | |
152 self.id = str(uuid.uuid4()) | |
153 self._auto_update = False | |
154 | |
155 def __setattr__(self, name, value): | |
156 if name in self._prop_names: | |
157 object.__setattr__(self, name, value) | |
158 elif name.startswith('_'): | |
159 object.__setattr__(self, name, value) | |
160 elif name == 'id': | |
161 object.__setattr__(self, name, value) | |
162 else: | |
163 self._persist_attribute(name, value) | |
164 object.__setattr__(self, name, value) | |
165 | |
166 def __getattr__(self, name): | |
167 if not name.startswith('_'): | |
168 a = self._manager.domain.get_attributes(self.id, name) | |
169 if a.has_key(name): | |
170 object.__setattr__(self, name, a[name]) | |
171 return a[name] | |
172 raise AttributeError | |
173 | |
174 def __repr__(self): | |
175 return '%s<%s>' % (self.__class__.__name__, self.id) | |
176 | |
177 def _persist_attribute(self, name, value): | |
178 if self.id: | |
179 self._manager.domain.put_attributes(self.id, {name : value}, replace
=True) | |
180 | |
181 def _get_sdb_item(self): | |
182 return self._manager.domain.get_item(self.id) | |
183 | |
184 def save(self): | |
185 attrs = {'__type__' : self.__class__.__name__, | |
186 '__module__' : self.__class__.__module__, | |
187 '__lineage__' : self.get_lineage()} | |
188 for property in self.properties(): | |
189 attrs[property.name] = property.to_string(self) | |
190 if self._manager.domain: | |
191 self._manager.domain.put_attributes(self.id, attrs, replace=True) | |
192 self._auto_update = True | |
193 | |
194 def delete(self): | |
195 if self._manager.domain: | |
196 self._manager.domain.delete_attributes(self.id) | |
197 | |
198 def get_related_objects(self, ref_name, ref_cls=None): | |
199 if self._manager.domain: | |
200 query = "['%s' = '%s']" % (ref_name, self.id) | |
201 if ref_cls: | |
202 query += " intersection ['__type__'='%s']" % ref_cls.__name__ | |
203 rs = self._manager.domain.query(query) | |
204 else: | |
205 rs = [] | |
206 return object_lister(ref_cls, rs, self._manager) | |
207 | |
OLD | NEW |