person:
 first_namelast_nameemail (many emails for one person)email:
 typeaddressDatabase programming in Python is based on the DB-API. Some supported databases:
Example:
import MySQLdb
conn = MySQLdb.connect(
    db='test', username='ianb')
cur = conn.cursor()
cur.execute(
    "SELECT id, first_name, last_name FROM person")
result = cur.fetchall()
for id, first_name, last_name in result:
    print "%2i %s, %s" % (id, last_name, first_name)
MySQLdb,
psycopg, sqlite, etc) module.connect(...) to create a
connection. connection.cursor() to get a cursor.  Cursors do
all the work. cursor.execute(sql_query) to run
something. cursor.fetchall() to get results. 
The table:
| person | ||
|---|---|---|
| id | first_name | last_name | 
| 1 | John | Doe | 
| 2 | Tom | Jones | 
| ... | ||
The class:
| 
 | 
class Person(SQLObject):
    # id is implicit
    first_name = StringCol()
    last_name = StringCol()
 | |||||||||||||||
An instance:
| 
 | >>> john = Person.get(1) >>> john.first_name 'John' | |||||||||||||||
__connection__ = 'postgres://pgsql@localhost/test'
class Person(SQLObject):
    first_name = StringCol()
    last_name = StringCol()
    emails = MultipleJoin('Email')
class Email(SQLObject):
    person = ForeignKey('Person')
    type = EnumCol(['home', 'work'])
    address = StringCol()
def createTables():
    for table in (Person, Email):
        table.createTable(ifNotExists=True)
>>> john = Person(first_name='John', last_name='Doe') >>> email = Email(person=john, type='home', ... address='john@work.com') >>> john.emails []
>>> tom = Person(first_name='Tom', last_name='Jones') >>> tom is Person.get(tom.id) True >>> list(Person.selectBy(first_name='John')) []
You can add extra methods:
class Person(SQLObject):
    ...
    def _get_name(self):
        return self.first_name + ' ' + self.last_name
>>> tom.name 'Tom Jones'
_get_attr() methods are called whenever the
obj.attr attribute is called _set_attr() methods are called whenever the
obj.attr = value statement is run _set_attr() is optional You can override columns:
class Person(SQLObject):
    ...
    last_name_searchable = StringCol()
    def _set_last_name(self, value):
        self._SO_set_last_name(self, value)
        self.last_name_lower = re.sub(r'[^a-zA-Z]', '', value).lower()
You can fiddle with the naming:
class Person(SQLObject):
    _table = "people"
    first_name = StringCol(dbName="fname")
    ...
Foreign Keys:
class Email(SQLObject):
    person = ForeignKey('Person')
    ...
Note we use a string for 'Person'.
The other side of one-to-many:
class Person(SQLObject):
    ...
    emails = MultipleJoin('Email')
Many to many relationships imply a "hidden" table:
class Address(SQLObject):
    people = RelatedJoin('Person')
    ...
class Person(SQLObject):
    addresses = RelatedJoin('Address')
The intermediate table created:
CREATE TABLE address_person ( address_id INT NOT NULL, person_id INT NOT NULL );
SQLObject can use reflection to figure out what columns your table has:
class Person(SQLObject):
    _fromDatabase = True
You can start with _fromDatabase = True and add 
in explicit columns, overriding defaults.
Person.get(id) to retrieve a rowPerson(first_name=...) to insert a row (the 
new object is returned)aPerson.destroySelf() to delete a rowaPerson.first_name = "new_name"UPDATE is executed immediately aPerson.set(first_name="new_fname", last_name="new_lname")  You can use Python expressions (kind of):
query = Person.q.first_name == "Joe"
Person.select(query)
Class.q.column_name creates a magical
object that creates queries.sqlrepr() turns these query objects into SQL (mostly
hidden)sqlrepr(Person.q.first_name == "Joe", 'mysql')
creates the SQL person.first_name =
'Joe'Complicated joins are possible:
Person.select((Person.q.id == Email.q.personID)
              & (Email.q.address.startswith('joe')))
Becomes:
SELECT person.id, person.first_name, person.last_name
FROM person, email
WHERE person.id = email.person_id
      AND email.address LIKE 'joe%'
LIMIT and OFFSET statements are added to your queryselect_result.count() to run
aggregate functions list(select_result)ORMs live in the world between Object Oriented programmers (and programs) and Relational programmers (and programs). Neither will be happy.
for loop is natural) 
See the Python Wiki at: python.org/moin
The page is HigherLevelDatabaseProgramming.