| 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 |