Open Source Python library for accessing Amazon SimpleDB API
Pull request Compare This branch is 20 commits ahead of saymedia:master.
Fetching latest commit…
Cannot retrieve the latest commit at this time.
Failed to load latest commit information.


Python SimpleDB Client and API SDK

Python library for accessing Amazon SimpleDB API Version 2009-04-15.

Amazon SimpleDB <> is "a web service providing the core
database functions of data indexing and querying." It is a schemaless persistent data
store that is accessed via a simple HTTP API.

To get started you'll need to instantiate an instance of the SimpleDB class, passing
in your AWS key and secret (your access identifiers, available at

    >>> sdb = SimpleDB(<AWS KEY>, <AWS SECRET>)

Your Amazon SimpleDB account can have zero or more domains, which are discrete partitions
that contain your data. If you have a small, homogenous data set you may choose to use
a single domain. If you have a large data set with varying data types that are easily
partitionable you may choose to use multiple domains, which may improve performance.

You can create a domain by calling SimpleDB's create_domain method. Note that this 
operation may take 10 or more seconds to complete.

    >>> users = sdb.create_domain('users')

You may access an existing domain by using dictionary syntax on a SimpleDB instance,
which implements a subset of the python dictionary API. You can also use a SimpleDB
instance to iterate over your domains.

    >>> domain1 = sdb['domain1']
    >>> ', '.join( for domain in sdb)
    'domain1, domain2, foo, test, users'

SimpleDB Domains are represented by instances of simpledb.Domain. Once again, you
can use dictionary syntax to reference items stored in SimpleDB.

    >>> users = sdb['users']
    >>> users['mmalone']
    {'age': '24', 'name': 'Mike', 'location': 'San Francisco, CA'}

Item's are represented by instances of simpledb.Item. Note that your data will not
be persisted to SimpleDB until you call the Item's save method.

    >>> user = users['mmalone']
    >>> user['age'] = '25'

You can also assign a dictionary directly to an item in a domain, which will
automatically create and save a simpledb.Item instance.

    >>> users['rcrowley'] = {'name': 'Richard', 'age': '24', 'location': 'San Francisco, CA'}
    >>> users['rcrowley']
    {'age': '24', 'name': 'Richard', 'location': 'San Francisco, CA'}

You can delete items, keys, or entire domains using del.

    >>> del users['rcrowley']['location']
    >>> users['rcrowley']
    {'age': '24', 'name': 'Richard'}
    >>> del users['rcrowley']
    >>> users['rcrowley']
    >>> sdb['users']
    <Domain: users>
    >>> del sdb['users']
    >>> sdb['users']['mmalone']
    Traceback (most recent call last):
      File "", line 198, in make_request
         raise SimpleDBError(error.find('Message').text)
    simpledb.SimpleDBError: The specified domain does not exist.

== Querying ==

The simpledb.Domain select operation returns a list of sipmledb.Item instances
that match the select expression. See Amazon's documentation for details re: the
Select expression syntax.

    >>>"SELECT name FROM users WHERE name LIKE 'Kim%'")
    [{'name': 'Kim'}, {'name': 'Kimberlee'}, {'name': 'Kimberley'}, {'name': 'Kimberly'}]

A more Pythonic query interface is available via the filter method. The filter
method returns a simpledb.Query object that lazily evaluates your query. Thus,
you can construct a query incrementally and pass Query objects around in your code

    >>> users.filter(name__like='Kim%').values('name')
    [{'name': 'Kim'}, {'name': 'Kimberlee'}, {'name': 'Kimberley'}, {'name': 'Kimberly'}]

Query's are evaluated when you perform any of the following operations on them:
    * Iteration
    * Slicing
    * repr()
    * len()
    * list()

Most Query methods return a new Query, allowing you to chain operations. The 
following methods return a new Query object:

    * filter(*args, **kwargs): Returns a new Query containing objects that match
      the given lookup parameters. Attribute lookup syntax is described below.
    * values(*attributes): Returns a new Query that restricts the set of attributes
      that will be retrieved.
    * item_names(): Returns a new Query that returns a list of Item names.
    * order_by(attribute): Returns a new Query that has been modified to order
      the results by the specified attribute. If the attribute name has a '-' 
      prefix, the results will be in descending order.
    * all(): Returns a copy of the current Query. This is useful if you want to
      be able to pass a Domain or a Query object into a function. You can safely
      call the all() method on either object.
    * count(): Returns a count of the number of objects that match the query. If
      the Query has not been evaluated, a COUNT(*) operation is performed on the 
      database. Otherwise the length of the result set is returned.

== Attribute Lookups ==

Attribute lookups are how you construct the WHERE clause of your expression. They're
specified as keyword arguments to the Query method filter() and to simpledb.where,
simpledb.every, and simpledb.item_name clauses, which are described later.

    * EQ: Exact match. If the value provided for comparison is None, it will be
      interpreted as NULL. This is the default lookup type, so if you leave the type
      off an equals statement will be constructed.

      >>> users.filter(name__eq='Kim')
      >>> users.filter(location=None)

    * NOTEQ: The attribute is not equal to the value provided for comparison. If
      the value provided for comparison is None, it will be interpreted as NULL.

      >>> users.filter(name__noteq='Mike')
      >>> users.filter(location__noteq=None)

    * GT: Greater than. Note that all comparisons are done lexicographically, so
      you'll need to pad numbers and offset negatives for proper comparison, and
      convert dates to a format that sorts lexicographically like ISO 8601. 
      >>> users.filter(age__gt='24')

    * LT: Less than.
      >>> users.filter(age__lt='50')

    * GTE: Greater than or equal to.
    * LTE: Less than or equal to.

    * LIKE: Attribute value contains the specified value. The like operator can be
      used to evaluate the start of a string ('string%'), the end of the string
      ('%string'), or any part of a string ('%string%').

      >>> users.filter(location__like='San%')

    * NOTLIKE: Attribute value does not contain the specified value.

    * BTWN: Attribute value falls within the specified range.
      >>> users.filter(age__btwn=('20','30'))

    * IN: Attribute value is equal to one of the specified values.
      >>> users.filter(name__in=('Mike', 'Michael')) 

== Advanced Lookups ==

By default, all filter operations are combined with the AND keyword, producing an
increasingly refined result set. If you need to combine multiple where clauses with 
an OR connector, you can manually constuct `where` clauses and logically combine them
using the `&` and `|` operators, then pass them directly to the filter method:

    >>> users.filter(simpledb.where(age='24') | simpledb.where(age='25'))

SimpleDB allows an Attribute to have multiple values, and if any one of the attribute
values matches your query, that attribute will be included in the result set. If you
want _every_ attribute to match your query, you can use an `every` clause:

    >>> users.filter(simpledb.every(location='San Francisco, CA'))

Finally, you can query items based on their names by using an `item_name` clause. Note
that with item_name clauses the keyword arguments do not include an attribute name,
only the lookup type:

    >>> users.filter(simpledb.item_name(like='%malone'))
    >>> users.filter(simpledb.item_name('mmalone'))
    >>> users.filter(simpledb.item_name(btwn=('a', 'b')))

== Models ==

Models give you a higher level API for interacting with SimpleDB. Instead of directly
querying the database, you subclass `models.Model` and add one or more `models.Field`

In Python, model fields will appear as ordinary Python objects. When stored in SimpleDB,
the field values are encoded and stored as UTF8 strings. Care is taken to convert the
Python values into strings that are lexicographically sortable and behave sensibly when
querying SimpleDB.

A Model must have one ItemName attribute. This field's value will be used as the item's
name in SimpleDB.

A Model must have a Meta class with two attributes:
    `connection`: specifies the SimpleDB connection for the Model
    `domain`: specifes the domain the Model is stored in

A simple User model might look like this:
    class User(models.Model):
        username = models.ItemName()
        name = models.Field()
        birth_date = models.DateTimeField()
        net_worth = models.NumberField(padding=10, precision=2, offset=1000000000)

        class Meta:
            connection = simpledb.SimpleDB(settings.AWS_KEY, settings.AWS_SECRET)
            domain = 'new_users'

You query models via their Manager object which, by default, will be attached to the Model's
`objects` attribute. The Query syntax is the same as that provided by the simpledb
module, but your result set will consist of instances of your Model class, with attribute
values appropriately decoded to Python objects.

    >>> Person.objects.filter(age__gt=20)
    >>> Person.objects.get('mmalone')