person
:
first_name
last_name
email
(many emails for one person)email
:
type
address
Database 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 = TrueYou 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.