| OLD | NEW |
| (Empty) | |
| 1 # Copyright (c) 2006,2007 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 import xml.sax |
| 23 import threading |
| 24 import boto |
| 25 from boto import handler |
| 26 from boto.connection import AWSQueryConnection |
| 27 from boto.sdb.domain import Domain, DomainMetaData |
| 28 from boto.sdb.item import Item |
| 29 from boto.sdb.regioninfo import SDBRegionInfo |
| 30 from boto.exception import SDBResponseError |
| 31 |
| 32 class ItemThread(threading.Thread): |
| 33 """ |
| 34 A threaded :class:`Item <boto.sdb.item.Item>` retriever utility class. |
| 35 Retrieved :class:`Item <boto.sdb.item.Item>` objects are stored in the |
| 36 ``items`` instance variable after :py:meth:`run() <run>` is called. |
| 37 |
| 38 .. tip:: The item retrieval will not start until |
| 39 the :func:`run() <boto.sdb.connection.ItemThread.run>` method is called. |
| 40 """ |
| 41 def __init__(self, name, domain_name, item_names): |
| 42 """ |
| 43 :param str name: A thread name. Used for identification. |
| 44 :param str domain_name: The name of a SimpleDB |
| 45 :class:`Domain <boto.sdb.domain.Domain>` |
| 46 :type item_names: string or list of strings |
| 47 :param item_names: The name(s) of the items to retrieve from the specifi
ed |
| 48 :class:`Domain <boto.sdb.domain.Domain>`. |
| 49 :ivar list items: A list of items retrieved. Starts as empty list. |
| 50 """ |
| 51 threading.Thread.__init__(self, name=name) |
| 52 #print 'starting %s with %d items' % (name, len(item_names)) |
| 53 self.domain_name = domain_name |
| 54 self.conn = SDBConnection() |
| 55 self.item_names = item_names |
| 56 self.items = [] |
| 57 |
| 58 def run(self): |
| 59 """ |
| 60 Start the threaded retrieval of items. Populates the |
| 61 ``items`` list with :class:`Item <boto.sdb.item.Item>` objects. |
| 62 """ |
| 63 for item_name in self.item_names: |
| 64 item = self.conn.get_attributes(self.domain_name, item_name) |
| 65 self.items.append(item) |
| 66 |
| 67 #boto.set_stream_logger('sdb') |
| 68 |
| 69 class SDBConnection(AWSQueryConnection): |
| 70 """ |
| 71 This class serves as a gateway to your SimpleDB region (defaults to |
| 72 us-east-1). Methods within allow access to SimpleDB |
| 73 :class:`Domain <boto.sdb.domain.Domain>` objects and their associated |
| 74 :class:`Item <boto.sdb.item.Item>` objects. |
| 75 |
| 76 .. tip:: |
| 77 While you may instantiate this class directly, it may be easier to |
| 78 go through :py:func:`boto.connect_sdb`. |
| 79 """ |
| 80 DefaultRegionName = 'us-east-1' |
| 81 DefaultRegionEndpoint = 'sdb.us-east-1.amazonaws.com' |
| 82 APIVersion = '2009-04-15' |
| 83 ResponseError = SDBResponseError |
| 84 |
| 85 def __init__(self, aws_access_key_id=None, aws_secret_access_key=None, |
| 86 is_secure=True, port=None, proxy=None, proxy_port=None, |
| 87 proxy_user=None, proxy_pass=None, debug=0, |
| 88 https_connection_factory=None, region=None, path='/', |
| 89 converter=None, security_token=None, validate_certs=True): |
| 90 """ |
| 91 For any keywords that aren't documented, refer to the parent class, |
| 92 :py:class:`boto.connection.AWSAuthConnection`. You can avoid having |
| 93 to worry about these keyword arguments by instantiating these objects |
| 94 via :py:func:`boto.connect_sdb`. |
| 95 |
| 96 :type region: :class:`boto.sdb.regioninfo.SDBRegionInfo` |
| 97 :keyword region: Explicitly specify a region. Defaults to ``us-east-1`` |
| 98 if not specified. You may also specify the region in your ``boto.cfg
``: |
| 99 |
| 100 .. code-block:: cfg |
| 101 |
| 102 [SDB] |
| 103 region = eu-west-1 |
| 104 |
| 105 """ |
| 106 if not region: |
| 107 region_name = boto.config.get('SDB', 'region', self.DefaultRegionNam
e) |
| 108 for reg in boto.sdb.regions(): |
| 109 if reg.name == region_name: |
| 110 region = reg |
| 111 break |
| 112 |
| 113 self.region = region |
| 114 AWSQueryConnection.__init__(self, aws_access_key_id, |
| 115 aws_secret_access_key, |
| 116 is_secure, port, proxy, |
| 117 proxy_port, proxy_user, proxy_pass, |
| 118 self.region.endpoint, debug, |
| 119 https_connection_factory, path, |
| 120 security_token=security_token, |
| 121 validate_certs=validate_certs) |
| 122 self.box_usage = 0.0 |
| 123 self.converter = converter |
| 124 self.item_cls = Item |
| 125 |
| 126 def _required_auth_capability(self): |
| 127 return ['sdb'] |
| 128 |
| 129 def set_item_cls(self, cls): |
| 130 """ |
| 131 While the default item class is :py:class:`boto.sdb.item.Item`, this |
| 132 default may be overridden. Use this method to change a connection's |
| 133 item class. |
| 134 |
| 135 :param object cls: The new class to set as this connection's item |
| 136 class. See the default item class for inspiration as to what your |
| 137 replacement should/could look like. |
| 138 """ |
| 139 self.item_cls = cls |
| 140 |
| 141 def _build_name_value_list(self, params, attributes, replace=False, |
| 142 label='Attribute'): |
| 143 keys = sorted(attributes.keys()) |
| 144 i = 1 |
| 145 for key in keys: |
| 146 value = attributes[key] |
| 147 if isinstance(value, list): |
| 148 for v in value: |
| 149 params['%s.%d.Name' % (label, i)] = key |
| 150 if self.converter: |
| 151 v = self.converter.encode(v) |
| 152 params['%s.%d.Value' % (label, i)] = v |
| 153 if replace: |
| 154 params['%s.%d.Replace' % (label, i)] = 'true' |
| 155 i += 1 |
| 156 else: |
| 157 params['%s.%d.Name' % (label, i)] = key |
| 158 if self.converter: |
| 159 value = self.converter.encode(value) |
| 160 params['%s.%d.Value' % (label, i)] = value |
| 161 if replace: |
| 162 params['%s.%d.Replace' % (label, i)] = 'true' |
| 163 i += 1 |
| 164 |
| 165 def _build_expected_value(self, params, expected_value): |
| 166 params['Expected.1.Name'] = expected_value[0] |
| 167 if expected_value[1] is True: |
| 168 params['Expected.1.Exists'] = 'true' |
| 169 elif expected_value[1] is False: |
| 170 params['Expected.1.Exists'] = 'false' |
| 171 else: |
| 172 params['Expected.1.Value'] = expected_value[1] |
| 173 |
| 174 def _build_batch_list(self, params, items, replace=False): |
| 175 item_names = items.keys() |
| 176 i = 0 |
| 177 for item_name in item_names: |
| 178 params['Item.%d.ItemName' % i] = item_name |
| 179 j = 0 |
| 180 item = items[item_name] |
| 181 if item is not None: |
| 182 attr_names = item.keys() |
| 183 for attr_name in attr_names: |
| 184 value = item[attr_name] |
| 185 if isinstance(value, list): |
| 186 for v in value: |
| 187 if self.converter: |
| 188 v = self.converter.encode(v) |
| 189 params['Item.%d.Attribute.%d.Name' % (i, j)] = attr_
name |
| 190 params['Item.%d.Attribute.%d.Value' % (i, j)] = v |
| 191 if replace: |
| 192 params['Item.%d.Attribute.%d.Replace' % (i, j)]
= 'true' |
| 193 j += 1 |
| 194 else: |
| 195 params['Item.%d.Attribute.%d.Name' % (i, j)] = attr_name |
| 196 if self.converter: |
| 197 value = self.converter.encode(value) |
| 198 params['Item.%d.Attribute.%d.Value' % (i, j)] = value |
| 199 if replace: |
| 200 params['Item.%d.Attribute.%d.Replace' % (i, j)] = 't
rue' |
| 201 j += 1 |
| 202 i += 1 |
| 203 |
| 204 def _build_name_list(self, params, attribute_names): |
| 205 i = 1 |
| 206 attribute_names.sort() |
| 207 for name in attribute_names: |
| 208 params['Attribute.%d.Name' % i] = name |
| 209 i += 1 |
| 210 |
| 211 def get_usage(self): |
| 212 """ |
| 213 Returns the BoxUsage (in USD) accumulated on this specific SDBConnection |
| 214 instance. |
| 215 |
| 216 .. tip:: This can be out of date, and should only be treated as a |
| 217 rough estimate. Also note that this estimate only applies to the |
| 218 requests made on this specific connection instance. It is by |
| 219 no means an account-wide estimate. |
| 220 |
| 221 :rtype: float |
| 222 :return: The accumulated BoxUsage of all requests made on the connection
. |
| 223 """ |
| 224 return self.box_usage |
| 225 |
| 226 def print_usage(self): |
| 227 """ |
| 228 Print the BoxUsage and approximate costs of all requests made on |
| 229 this specific SDBConnection instance. |
| 230 |
| 231 .. tip:: This can be out of date, and should only be treated as a |
| 232 rough estimate. Also note that this estimate only applies to the |
| 233 requests made on this specific connection instance. It is by |
| 234 no means an account-wide estimate. |
| 235 """ |
| 236 print 'Total Usage: %f compute seconds' % self.box_usage |
| 237 cost = self.box_usage * 0.14 |
| 238 print 'Approximate Cost: $%f' % cost |
| 239 |
| 240 def get_domain(self, domain_name, validate=True): |
| 241 """ |
| 242 Retrieves a :py:class:`boto.sdb.domain.Domain` object whose name |
| 243 matches ``domain_name``. |
| 244 |
| 245 :param str domain_name: The name of the domain to retrieve |
| 246 :keyword bool validate: When ``True``, check to see if the domain |
| 247 actually exists. If ``False``, blindly return a |
| 248 :py:class:`Domain <boto.sdb.domain.Domain>` object with the |
| 249 specified name set. |
| 250 |
| 251 :raises: |
| 252 :py:class:`boto.exception.SDBResponseError` if ``validate`` is |
| 253 ``True`` and no match could be found. |
| 254 |
| 255 :rtype: :py:class:`boto.sdb.domain.Domain` |
| 256 :return: The requested domain |
| 257 """ |
| 258 domain = Domain(self, domain_name) |
| 259 if validate: |
| 260 self.select(domain, """select * from `%s` limit 1""" % domain_name) |
| 261 return domain |
| 262 |
| 263 def lookup(self, domain_name, validate=True): |
| 264 """ |
| 265 Lookup an existing SimpleDB domain. This differs from |
| 266 :py:meth:`get_domain` in that ``None`` is returned if ``validate`` is |
| 267 ``True`` and no match was found (instead of raising an exception). |
| 268 |
| 269 :param str domain_name: The name of the domain to retrieve |
| 270 |
| 271 :param bool validate: If ``True``, a ``None`` value will be returned |
| 272 if the specified domain can't be found. If ``False``, a |
| 273 :py:class:`Domain <boto.sdb.domain.Domain>` object will be dumbly |
| 274 returned, regardless of whether it actually exists. |
| 275 |
| 276 :rtype: :class:`boto.sdb.domain.Domain` object or ``None`` |
| 277 :return: The Domain object or ``None`` if the domain does not exist. |
| 278 """ |
| 279 try: |
| 280 domain = self.get_domain(domain_name, validate) |
| 281 except: |
| 282 domain = None |
| 283 return domain |
| 284 |
| 285 def get_all_domains(self, max_domains=None, next_token=None): |
| 286 """ |
| 287 Returns a :py:class:`boto.resultset.ResultSet` containing |
| 288 all :py:class:`boto.sdb.domain.Domain` objects associated with |
| 289 this connection's Access Key ID. |
| 290 |
| 291 :keyword int max_domains: Limit the returned |
| 292 :py:class:`ResultSet <boto.resultset.ResultSet>` to the specified |
| 293 number of members. |
| 294 :keyword str next_token: A token string that was returned in an |
| 295 earlier call to this method as the ``next_token`` attribute |
| 296 on the returned :py:class:`ResultSet <boto.resultset.ResultSet>` |
| 297 object. This attribute is set if there are more than Domains than |
| 298 the value specified in the ``max_domains`` keyword. Pass the |
| 299 ``next_token`` value from you earlier query in this keyword to |
| 300 get the next 'page' of domains. |
| 301 """ |
| 302 params = {} |
| 303 if max_domains: |
| 304 params['MaxNumberOfDomains'] = max_domains |
| 305 if next_token: |
| 306 params['NextToken'] = next_token |
| 307 return self.get_list('ListDomains', params, [('DomainName', Domain)]) |
| 308 |
| 309 def create_domain(self, domain_name): |
| 310 """ |
| 311 Create a SimpleDB domain. |
| 312 |
| 313 :type domain_name: string |
| 314 :param domain_name: The name of the new domain |
| 315 |
| 316 :rtype: :class:`boto.sdb.domain.Domain` object |
| 317 :return: The newly created domain |
| 318 """ |
| 319 params = {'DomainName':domain_name} |
| 320 d = self.get_object('CreateDomain', params, Domain) |
| 321 d.name = domain_name |
| 322 return d |
| 323 |
| 324 def get_domain_and_name(self, domain_or_name): |
| 325 """ |
| 326 Given a ``str`` or :class:`boto.sdb.domain.Domain`, return a |
| 327 ``tuple`` with the following members (in order): |
| 328 |
| 329 * In instance of :class:`boto.sdb.domain.Domain` for the requested |
| 330 domain |
| 331 * The domain's name as a ``str`` |
| 332 |
| 333 :type domain_or_name: ``str`` or :class:`boto.sdb.domain.Domain` |
| 334 :param domain_or_name: The domain or domain name to get the domain |
| 335 and name for. |
| 336 |
| 337 :raises: :class:`boto.exception.SDBResponseError` when an invalid |
| 338 domain name is specified. |
| 339 |
| 340 :rtype: tuple |
| 341 :return: A ``tuple`` with contents outlined as per above. |
| 342 """ |
| 343 if (isinstance(domain_or_name, Domain)): |
| 344 return (domain_or_name, domain_or_name.name) |
| 345 else: |
| 346 return (self.get_domain(domain_or_name), domain_or_name) |
| 347 |
| 348 def delete_domain(self, domain_or_name): |
| 349 """ |
| 350 Delete a SimpleDB domain. |
| 351 |
| 352 .. caution:: This will delete the domain and all items within the domain
. |
| 353 |
| 354 :type domain_or_name: string or :class:`boto.sdb.domain.Domain` object. |
| 355 :param domain_or_name: Either the name of a domain or a Domain object |
| 356 |
| 357 :rtype: bool |
| 358 :return: True if successful |
| 359 |
| 360 """ |
| 361 domain, domain_name = self.get_domain_and_name(domain_or_name) |
| 362 params = {'DomainName':domain_name} |
| 363 return self.get_status('DeleteDomain', params) |
| 364 |
| 365 def domain_metadata(self, domain_or_name): |
| 366 """ |
| 367 Get the Metadata for a SimpleDB domain. |
| 368 |
| 369 :type domain_or_name: string or :class:`boto.sdb.domain.Domain` object. |
| 370 :param domain_or_name: Either the name of a domain or a Domain object |
| 371 |
| 372 :rtype: :class:`boto.sdb.domain.DomainMetaData` object |
| 373 :return: The newly created domain metadata object |
| 374 """ |
| 375 domain, domain_name = self.get_domain_and_name(domain_or_name) |
| 376 params = {'DomainName':domain_name} |
| 377 d = self.get_object('DomainMetadata', params, DomainMetaData) |
| 378 d.domain = domain |
| 379 return d |
| 380 |
| 381 def put_attributes(self, domain_or_name, item_name, attributes, |
| 382 replace=True, expected_value=None): |
| 383 """ |
| 384 Store attributes for a given item in a domain. |
| 385 |
| 386 :type domain_or_name: string or :class:`boto.sdb.domain.Domain` object. |
| 387 :param domain_or_name: Either the name of a domain or a Domain object |
| 388 |
| 389 :type item_name: string |
| 390 :param item_name: The name of the item whose attributes are being |
| 391 stored. |
| 392 |
| 393 :type attribute_names: dict or dict-like object |
| 394 :param attribute_names: The name/value pairs to store as attributes |
| 395 |
| 396 :type expected_value: list |
| 397 :param expected_value: If supplied, this is a list or tuple consisting |
| 398 of a single attribute name and expected value. The list can be |
| 399 of the form: |
| 400 |
| 401 * ['name', 'value'] |
| 402 |
| 403 In which case the call will first verify that the attribute "name" |
| 404 of this item has a value of "value". If it does, the delete |
| 405 will proceed, otherwise a ConditionalCheckFailed error will be |
| 406 returned. The list can also be of the form: |
| 407 |
| 408 * ['name', True|False] |
| 409 |
| 410 which will simply check for the existence (True) or |
| 411 non-existence (False) of the attribute. |
| 412 |
| 413 :type replace: bool |
| 414 :param replace: Whether the attribute values passed in will replace |
| 415 existing values or will be added as addition values. |
| 416 Defaults to True. |
| 417 |
| 418 :rtype: bool |
| 419 :return: True if successful |
| 420 """ |
| 421 domain, domain_name = self.get_domain_and_name(domain_or_name) |
| 422 params = {'DomainName' : domain_name, |
| 423 'ItemName' : item_name} |
| 424 self._build_name_value_list(params, attributes, replace) |
| 425 if expected_value: |
| 426 self._build_expected_value(params, expected_value) |
| 427 return self.get_status('PutAttributes', params) |
| 428 |
| 429 def batch_put_attributes(self, domain_or_name, items, replace=True): |
| 430 """ |
| 431 Store attributes for multiple items in a domain. |
| 432 |
| 433 :type domain_or_name: string or :class:`boto.sdb.domain.Domain` object. |
| 434 :param domain_or_name: Either the name of a domain or a Domain object |
| 435 |
| 436 :type items: dict or dict-like object |
| 437 :param items: A dictionary-like object. The keys of the dictionary are |
| 438 the item names and the values are themselves dictionaries |
| 439 of attribute names/values, exactly the same as the |
| 440 attribute_names parameter of the scalar put_attributes |
| 441 call. |
| 442 |
| 443 :type replace: bool |
| 444 :param replace: Whether the attribute values passed in will replace |
| 445 existing values or will be added as addition values. |
| 446 Defaults to True. |
| 447 |
| 448 :rtype: bool |
| 449 :return: True if successful |
| 450 """ |
| 451 domain, domain_name = self.get_domain_and_name(domain_or_name) |
| 452 params = {'DomainName' : domain_name} |
| 453 self._build_batch_list(params, items, replace) |
| 454 return self.get_status('BatchPutAttributes', params, verb='POST') |
| 455 |
| 456 def get_attributes(self, domain_or_name, item_name, attribute_names=None, |
| 457 consistent_read=False, item=None): |
| 458 """ |
| 459 Retrieve attributes for a given item in a domain. |
| 460 |
| 461 :type domain_or_name: string or :class:`boto.sdb.domain.Domain` object. |
| 462 :param domain_or_name: Either the name of a domain or a Domain object |
| 463 |
| 464 :type item_name: string |
| 465 :param item_name: The name of the item whose attributes are |
| 466 being retrieved. |
| 467 |
| 468 :type attribute_names: string or list of strings |
| 469 :param attribute_names: An attribute name or list of attribute names. |
| 470 This parameter is optional. If not supplied, all attributes will |
| 471 be retrieved for the item. |
| 472 |
| 473 :type consistent_read: bool |
| 474 :param consistent_read: When set to true, ensures that the most recent |
| 475 data is returned. |
| 476 |
| 477 :type item: :class:`boto.sdb.item.Item` |
| 478 :keyword item: Instead of instantiating a new Item object, you may |
| 479 specify one to update. |
| 480 |
| 481 :rtype: :class:`boto.sdb.item.Item` |
| 482 :return: An Item with the requested attribute name/values set on it |
| 483 """ |
| 484 domain, domain_name = self.get_domain_and_name(domain_or_name) |
| 485 params = {'DomainName' : domain_name, |
| 486 'ItemName' : item_name} |
| 487 if consistent_read: |
| 488 params['ConsistentRead'] = 'true' |
| 489 if attribute_names: |
| 490 if not isinstance(attribute_names, list): |
| 491 attribute_names = [attribute_names] |
| 492 self.build_list_params(params, attribute_names, 'AttributeName') |
| 493 response = self.make_request('GetAttributes', params) |
| 494 body = response.read() |
| 495 if response.status == 200: |
| 496 if item == None: |
| 497 item = self.item_cls(domain, item_name) |
| 498 h = handler.XmlHandler(item, self) |
| 499 xml.sax.parseString(body, h) |
| 500 return item |
| 501 else: |
| 502 raise SDBResponseError(response.status, response.reason, body) |
| 503 |
| 504 def delete_attributes(self, domain_or_name, item_name, attr_names=None, |
| 505 expected_value=None): |
| 506 """ |
| 507 Delete attributes from a given item in a domain. |
| 508 |
| 509 :type domain_or_name: string or :class:`boto.sdb.domain.Domain` object. |
| 510 :param domain_or_name: Either the name of a domain or a Domain object |
| 511 |
| 512 :type item_name: string |
| 513 :param item_name: The name of the item whose attributes are being |
| 514 deleted. |
| 515 |
| 516 :type attributes: dict, list or :class:`boto.sdb.item.Item` |
| 517 :param attributes: Either a list containing attribute names which |
| 518 will cause all values associated with that attribute |
| 519 name to be deleted or a dict or Item containing the |
| 520 attribute names and keys and list of values to |
| 521 delete as the value. If no value is supplied, |
| 522 all attribute name/values for the item will be |
| 523 deleted. |
| 524 |
| 525 :type expected_value: list |
| 526 :param expected_value: If supplied, this is a list or tuple consisting |
| 527 of a single attribute name and expected value. The list can be |
| 528 of the form: |
| 529 |
| 530 * ['name', 'value'] |
| 531 |
| 532 In which case the call will first verify that the attribute "name" |
| 533 of this item has a value of "value". If it does, the delete |
| 534 will proceed, otherwise a ConditionalCheckFailed error will be |
| 535 returned. The list can also be of the form: |
| 536 |
| 537 * ['name', True|False] |
| 538 |
| 539 which will simply check for the existence (True) or |
| 540 non-existence (False) of the attribute. |
| 541 |
| 542 :rtype: bool |
| 543 :return: True if successful |
| 544 """ |
| 545 domain, domain_name = self.get_domain_and_name(domain_or_name) |
| 546 params = {'DomainName':domain_name, |
| 547 'ItemName' : item_name} |
| 548 if attr_names: |
| 549 if isinstance(attr_names, list): |
| 550 self._build_name_list(params, attr_names) |
| 551 elif isinstance(attr_names, dict) or isinstance(attr_names, self.ite
m_cls): |
| 552 self._build_name_value_list(params, attr_names) |
| 553 if expected_value: |
| 554 self._build_expected_value(params, expected_value) |
| 555 return self.get_status('DeleteAttributes', params) |
| 556 |
| 557 def batch_delete_attributes(self, domain_or_name, items): |
| 558 """ |
| 559 Delete multiple items in a domain. |
| 560 |
| 561 :type domain_or_name: string or :class:`boto.sdb.domain.Domain` object. |
| 562 :param domain_or_name: Either the name of a domain or a Domain object |
| 563 |
| 564 :type items: dict or dict-like object |
| 565 :param items: A dictionary-like object. The keys of the dictionary are |
| 566 the item names and the values are either: |
| 567 |
| 568 * dictionaries of attribute names/values, exactly the |
| 569 same as the attribute_names parameter of the scalar |
| 570 put_attributes call. The attribute name/value pairs |
| 571 will only be deleted if they match the name/value |
| 572 pairs passed in. |
| 573 * None which means that all attributes associated |
| 574 with the item should be deleted. |
| 575 |
| 576 :return: True if successful |
| 577 """ |
| 578 domain, domain_name = self.get_domain_and_name(domain_or_name) |
| 579 params = {'DomainName' : domain_name} |
| 580 self._build_batch_list(params, items, False) |
| 581 return self.get_status('BatchDeleteAttributes', params, verb='POST') |
| 582 |
| 583 def select(self, domain_or_name, query='', next_token=None, |
| 584 consistent_read=False): |
| 585 """ |
| 586 Returns a set of Attributes for item names within domain_name that |
| 587 match the query. The query must be expressed in using the SELECT |
| 588 style syntax rather than the original SimpleDB query language. |
| 589 Even though the select request does not require a domain object, |
| 590 a domain object must be passed into this method so the Item objects |
| 591 returned can point to the appropriate domain. |
| 592 |
| 593 :type domain_or_name: string or :class:`boto.sdb.domain.Domain` object |
| 594 :param domain_or_name: Either the name of a domain or a Domain object |
| 595 |
| 596 :type query: string |
| 597 :param query: The SimpleDB query to be performed. |
| 598 |
| 599 :type consistent_read: bool |
| 600 :param consistent_read: When set to true, ensures that the most recent |
| 601 data is returned. |
| 602 |
| 603 :rtype: ResultSet |
| 604 :return: An iterator containing the results. |
| 605 """ |
| 606 domain, domain_name = self.get_domain_and_name(domain_or_name) |
| 607 params = {'SelectExpression' : query} |
| 608 if consistent_read: |
| 609 params['ConsistentRead'] = 'true' |
| 610 if next_token: |
| 611 params['NextToken'] = next_token |
| 612 try: |
| 613 return self.get_list('Select', params, [('Item', self.item_cls)], |
| 614 parent=domain) |
| 615 except SDBResponseError, e: |
| 616 e.body = "Query: %s\n%s" % (query, e.body) |
| 617 raise e |
| OLD | NEW |