1# Copyright (c) 2012 Mitch Garnaat http://garnaat.org/ 2# Copyright (c) 2012 Amazon.com, Inc. or its affiliates. All Rights Reserved 3# 4# Permission is hereby granted, free of charge, to any person obtaining a 5# copy of this software and associated documentation files (the 6# "Software"), to deal in the Software without restriction, including 7# without limitation the rights to use, copy, modify, merge, publish, dis- 8# tribute, sublicense, and/or sell copies of the Software, and to permit 9# persons to whom the Software is furnished to do so, subject to the fol- 10# lowing conditions: 11# 12# The above copyright notice and this permission notice shall be included 13# in all copies or substantial portions of the Software. 14# 15# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 16# OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABIL- 17# ITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT 18# SHALL THE AUTHOR BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 19# WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 21# IN THE SOFTWARE. 22# 23 24from boto.dynamodb.exceptions import DynamoDBItemError 25 26 27class Item(dict): 28 """ 29 An item in Amazon DynamoDB. 30 31 :ivar hash_key: The HashKey of this item. 32 :ivar range_key: The RangeKey of this item or None if no RangeKey 33 is defined. 34 :ivar hash_key_name: The name of the HashKey associated with this item. 35 :ivar range_key_name: The name of the RangeKey associated with this item. 36 :ivar table: The Table this item belongs to. 37 """ 38 39 def __init__(self, table, hash_key=None, range_key=None, attrs=None): 40 self.table = table 41 self._updates = None 42 self._hash_key_name = self.table.schema.hash_key_name 43 self._range_key_name = self.table.schema.range_key_name 44 if attrs is None: 45 attrs = {} 46 if hash_key is None: 47 hash_key = attrs.get(self._hash_key_name, None) 48 self[self._hash_key_name] = hash_key 49 if self._range_key_name: 50 if range_key is None: 51 range_key = attrs.get(self._range_key_name, None) 52 self[self._range_key_name] = range_key 53 self._updates = {} 54 for key, value in attrs.items(): 55 if key != self._hash_key_name and key != self._range_key_name: 56 self[key] = value 57 self.consumed_units = 0 58 59 @property 60 def hash_key(self): 61 return self[self._hash_key_name] 62 63 @property 64 def range_key(self): 65 return self.get(self._range_key_name) 66 67 @property 68 def hash_key_name(self): 69 return self._hash_key_name 70 71 @property 72 def range_key_name(self): 73 return self._range_key_name 74 75 def add_attribute(self, attr_name, attr_value): 76 """ 77 Queue the addition of an attribute to an item in DynamoDB. 78 This will eventually result in an UpdateItem request being issued 79 with an update action of ADD when the save method is called. 80 81 :type attr_name: str 82 :param attr_name: Name of the attribute you want to alter. 83 84 :type attr_value: int|long|float|set 85 :param attr_value: Value which is to be added to the attribute. 86 """ 87 self._updates[attr_name] = ("ADD", attr_value) 88 89 def delete_attribute(self, attr_name, attr_value=None): 90 """ 91 Queue the deletion of an attribute from an item in DynamoDB. 92 This call will result in a UpdateItem request being issued 93 with update action of DELETE when the save method is called. 94 95 :type attr_name: str 96 :param attr_name: Name of the attribute you want to alter. 97 98 :type attr_value: set 99 :param attr_value: A set of values to be removed from the attribute. 100 This parameter is optional. If None, the whole attribute is 101 removed from the item. 102 """ 103 self._updates[attr_name] = ("DELETE", attr_value) 104 105 def put_attribute(self, attr_name, attr_value): 106 """ 107 Queue the putting of an attribute to an item in DynamoDB. 108 This call will result in an UpdateItem request being issued 109 with the update action of PUT when the save method is called. 110 111 :type attr_name: str 112 :param attr_name: Name of the attribute you want to alter. 113 114 :type attr_value: int|long|float|str|set 115 :param attr_value: New value of the attribute. 116 """ 117 self._updates[attr_name] = ("PUT", attr_value) 118 119 def save(self, expected_value=None, return_values=None): 120 """ 121 Commits pending updates to Amazon DynamoDB. 122 123 :type expected_value: dict 124 :param expected_value: A dictionary of name/value pairs that 125 you expect. This dictionary should have name/value pairs 126 where the name is the name of the attribute and the value is 127 either the value you are expecting or False if you expect 128 the attribute not to exist. 129 130 :type return_values: str 131 :param return_values: Controls the return of attribute name/value pairs 132 before they were updated. Possible values are: None, 'ALL_OLD', 133 'UPDATED_OLD', 'ALL_NEW' or 'UPDATED_NEW'. If 'ALL_OLD' is 134 specified and the item is overwritten, the content of the old item 135 is returned. If 'ALL_NEW' is specified, then all the attributes of 136 the new version of the item are returned. If 'UPDATED_NEW' is 137 specified, the new versions of only the updated attributes are 138 returned. 139 """ 140 return self.table.layer2.update_item(self, expected_value, 141 return_values) 142 143 def delete(self, expected_value=None, return_values=None): 144 """ 145 Delete the item from DynamoDB. 146 147 :type expected_value: dict 148 :param expected_value: A dictionary of name/value pairs that 149 you expect. This dictionary should have name/value pairs 150 where the name is the name of the attribute and the value 151 is either the value you are expecting or False if you expect 152 the attribute not to exist. 153 154 :type return_values: str 155 :param return_values: Controls the return of attribute 156 name-value pairs before then were changed. Possible 157 values are: None or 'ALL_OLD'. If 'ALL_OLD' is 158 specified and the item is overwritten, the content 159 of the old item is returned. 160 """ 161 return self.table.layer2.delete_item(self, expected_value, 162 return_values) 163 164 def put(self, expected_value=None, return_values=None): 165 """ 166 Store a new item or completely replace an existing item 167 in Amazon DynamoDB. 168 169 :type expected_value: dict 170 :param expected_value: A dictionary of name/value pairs that 171 you expect. This dictionary should have name/value pairs 172 where the name is the name of the attribute and the value 173 is either the value you are expecting or False if you expect 174 the attribute not to exist. 175 176 :type return_values: str 177 :param return_values: Controls the return of attribute 178 name-value pairs before then were changed. Possible 179 values are: None or 'ALL_OLD'. If 'ALL_OLD' is 180 specified and the item is overwritten, the content 181 of the old item is returned. 182 """ 183 return self.table.layer2.put_item(self, expected_value, return_values) 184 185 def __setitem__(self, key, value): 186 """Overrwrite the setter to instead update the _updates 187 method so this can act like a normal dict""" 188 if self._updates is not None: 189 self.put_attribute(key, value) 190 dict.__setitem__(self, key, value) 191 192 def __delitem__(self, key): 193 """Remove this key from the items""" 194 if self._updates is not None: 195 self.delete_attribute(key) 196 dict.__delitem__(self, key) 197 198 # Allow this item to still be pickled 199 def __getstate__(self): 200 return self.__dict__ 201 def __setstate__(self, d): 202 self.__dict__.update(d) 203