Oreilly Essential Sqlalchemy June 2008
Oreilly Essential Sqlalchemy June 2008
Essential SQLAlchemy
Rick Copeland
Published by O’Reilly Media, Inc., 1005 Gravenstein Highway North, Sebastopol, CA 95472
O’Reilly books may be purchased for educational, business, or sales promotional use. Online editions
are also available for most titles (http://safari.oreilly.com). For more information, contact our corporate/
institutional sales department: (800) 998-9938 or [email protected].
Printing History:
June 2008: First Edition
Nutshell Handbook, the Nutshell Handbook logo, and the O’Reilly logo are registered trademarks of
O’Reilly Media, Inc. Essential SQLAlchemy, the image of <image>, and related trade dress are trade-
marks of O’Reilly Media, Inc.
Many of the designations uses by manufacturers and sellers to distinguish their products are claimed as
trademarks. Where those designations appear in this book, and O’Reilly Media, Inc. was aware of a
trademark claim, the designations have been printed in caps or initial caps
While every precaution has been taken in the preparation of this book, the publisher and author assume
no responsibility for errors or omissions, or for damages resulting from the use of the information con-
tained herein.
ISBN: 978-0-596-51614-7
[M]
1210573118
Table of Contents
Preface . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . vii
1. Introduction to SQLAlchemy . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1
What Is SQLAlch 1
The Object/Relational “Impedance Mismatch” 4
SQLAlchemy Philosophy 7
SQLAlchemy Architecture 10
2. Getting Started . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21
Installing SQLAlchemy 21
SQLAlchemy Tutorial 24
v
7. Querying and Updating at the ORM Level . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 127
The SQLAlchemy ORM Session Object 127
Querying at the ORM Level 139
Contextual or Thread-Local Sessions 153
Index . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 207
vi | Table of Contents
Preface
Audience
First of all, this book is intended for those who want to learn more about how to use
relational databases with their Python programs, or have heard about SQLAlchemy
and want more information on it. Having said that, to get the most out of this book,
vii
the reader should have intermediate-to-advanced Python skills and at least moderate
exposure to SQL databases. SQLAlchemy provides support for many advanced SQL
constructs, so the experienced DBA will also find plenty of information here.
The beginning Python or database programmer would probably be best served by
reading a Python book such as Learning Python and/or a SQL book such as Learning
SQL, either prior to this book or as a reference to read in parallel with this book.
viii | Preface
Chapter 5, Running Queries and Updates
This chapter illustrates how to perform inserts, updates, and deletes. It covers result
set objects, retrieving partial results, and using SQL functions to aggregate and sort
data in the database server.
Chapter 6, Building an Object Mapper
This chapter describes the object-relational mapper (ORM) used in SQLAlchemy.
It describes the differences between the object mapper pattern (used in SQLAl-
chemy) and the active record pattern used in other ORMs. It then describes how
to set up a mapper, and how the mapper maps your tables by default. You will also
learn how to override the default mapping and how to specify various relationships
between tables.
Chapter 7, Querying and Updating at the ORM Level
This chapter shows how to create objects, save them to a session, and flush them
to the database. You will learn how Session and Query objects are defined, their
methods, and how to use them to insert, update, retrieve, and delete data from the
database at the ORM level. You will learn how to use result set mapping to populate
objects from a non-ORM query and when it should be used.
Chapter 8, Inheritance Mapping
This chapter describes how to use SQLAlchemy to model object-oriented inheri-
tance. The various ways of modeling inheritance in the relational model are
described, as well has the support SQLAlchemy provides for each.
Chapter 9, Elixir: A Declarative Extension to SQLAlchemy
This chapter describes the Elixir extension to SQLAlchemy, which provides a de-
clarative, active record pattern for use with SQLAlchemy. You will learn how to
use Elixir extensions such as acts_as_versioned to create auxilliary tables auto-
matically, and when Elixir is appropriate instead of “bare” SQLAlchemy.
Chapter 10, SqlSoup: An Automatic Mapper for SQLAlchemy
This chapter introduces the SQLSoup extension, which provides an automatic
metadata and object model based on database reflection. You will learn how to use
SQLSoup to query the database with a minimum of setup, and learn the pros and
cons of such an approach.
Chapter 11, Other SQLAlchemy Extensions
This chapter covers other, less extensive, extensions to SQLAlchemy. This chapter
describes the extensions that are currently used in the 0.4 release series of SQLAl-
chemy, as well as briefly describing deprecated extensions, and the functionality
in SQLAlchemy that supplants them.
Preface | ix
Italic
Indicates new terms, URLs, email addresses, filenames, file extensions, pathnames,
directories, and Unix utilities.
Constant width
Indicates commands, options, switches, variables, attributes, keys, functions,
types, classes, namespaces, methods, modules, properties, parameters, values, ob-
jects, events, event handlers, the contents of files, or the output from commands.
Constant width italic
Shows text that should be replaced with user-supplied values.
ALL CAPS
Shows SQL keywords and queries.
How to Contact Us
Please address comments and questions concerning this book to the publisher:
O’Reilly Media, Inc.
x | Preface
1005 Gravenstein Highway North
Sebastopol, CA 95472
800-998-9938 (in the United States or Canada)
707-829-0515 (international or local)
707 829-0104 (fax)
We have a web page for this book, where we list errata, examples, and any additional
information. You can access this page at:
http://www.oreilly.com/catalog/9780596516147
To comment or ask technical questions about this book, send email to:
[email protected]
For more information about our books, conferences, Resource Centers, and the O’Re-
illy Network, see our web site at:
http://www.oreilly.com
Acknowledgments
Many thanks go to Tatiana Apandi, TECHNICAL REVIEWERS HERE for their critical
pre-publication feedback, without whom this book would have undoubtedly had many
technical snafus.
My appreciation goes out to Noah Gift, whose recommendation led to this book being
written in the first place. I still remember how his phone call started: “You know
SQLAlchemy, right?...”
Thanks to my employer, Predictix, for allowing me the time and energy to finish the
book, and to my co-workers for being unwitting guinea pigs for many of the ideas and
techniques in this book.
Finally, my heartfelt gratitude goes to my beloved wife Nancy, whose support in the
presence of a husband glued to the computer was truly the fuel that allowed this book
to be written at all.
Preface | xi
CHAPTER 1
Introduction to SQLAlchemy
What Is SQLAlch
SQLALchemy is a Python Library created by Mike Bayer to provide a high-level, Py-
thonic (idiomatically Python) interface to relational databases such as Oracle, DB2,
MySQL, PostgreSQL, and SQLite. SQLAlchemy attempts to be unobtrusive to your
Python code, allowing you to map plain old Python objects (POPOs) to database tables
without substantially changing your existing Python code. SQLAlchemy includes both
a database server–independent SQL expression language and an object-relational map-
per (ORM) that lets you use SQL to persist your application objects automatically. This
chapter will introduce you to SQLAlchemy, illustrating some of its more powerful fea-
tures. Later chapters will provide more depth for the topics covered here.
If you have used lower-level database interfaces with Python, such as the DB-API, you
may be used to writing code such as the following to save your objects to the database:
sql="INSERT INTO user(user_name, password) VALUES (%s, %s)"
cursor = conn.cursor()
cursor.execute(sql, ('rick', 'parrot'))
Although this code gets the job done, it is verbose, error-prone, and tedious to write.
Using string manipulation to build up a query as done here can lead to various logical
errors and vulnerabilities such as opening your application up to SQL injection attacks.
Generating the string to be executed by your database server verbatim also ties your
code to the particular DB-API driver you are currently using, making migration to a
different database server difficult. For instance, if we wished to migrate the previous
example to the Oracle DB-API driver, we would need to write:
sql="INSERT INTO user(user_name, password) VALUES (:1, :2)"
cursor = conn.cursor()
cursor.execute(sql, 'rick', 'parrot')
1
SQL Injection Attacks
SQL injection is a type of programming error where carefully crafted user input can
cause your application to execute arbitrary SQL code. For instance, suppose that the
DB-API code in the earlier listing had been written as follows:
sql="INSERT INTO user(user_name, password) VALUES ('%s', '%s')"
cursor = conn.cursor()
cursor.execute(sql % (user_name, password))
In most cases, this code will work. For instance, with the user_name and password var-
iables just shown, the SQL that would be executed is INSERT INTO user(user_name,
password) VALUES ('rick', 'parrot'). A user could, however, supply a maliciously
crafted password: parrot'); DELETE FROM user; --. In this case, the SQL executed is
INSERT INTO user(user_name, password) VALUES ('rick', 'parrot'); DELETE FROM
user; --', which would probably delete all users from your database. The use of bind
parameters (as in the first example in the text) is an effective defense against SQL in-
jection, but as long as you are manipulating strings directly, there is always the
possibility of introducting a SQL injection vulnerability into your code.
In the SQLAlchemy SQL expression language, you could write the following instead:
statement = user_table.insert(user_name='rick', password='parrot')
statement.execute()
To migrate this code to Oracle, you would write, well, exactly the same thing.
SQLAlchemy also allows you to write SQL queries using a Pythonic expression-builder.
For instance, to retrieve all the users created in 2007, you would write:
statement = user_table.select(and_(
user_table.c.created >= date(2007,1,1),
user_table.c.created < date(2008,1,1))
result = statement.execute()
In order to use the SQL expression language, you need to provide SQLAlchemy with
information about your database schema. For instance, if you are using the user table
mentioned previously, your schema definition might be the following:
metadata=MetaData('sqlite://') # use an in-memory SQLite database
user_table = Table(
'tf_user', metadata,
Column('id', Integer, primary_key=True),
Column('user_name', Unicode(16), unique=True, nullable=False),
Column('email_address', Unicode(255), unique=True, nullable=False),
Column('password', Unicode(40), nullable=False),
Column('first_name', Unicode(255), default=''),
Column('last_name', Unicode(255), default=''),
Column('created', DateTime, default=datetime.now))
If you would rather use an existing database schema definition, you still need to tell
SQLAlchemy which tables you have, but SQLAlchemy can reflect the tables using the
Although the SQLAlchemy SQL expression language is quite powerful, it can still be
tedious to manually specify the queries and updates necessary to work with your tables.
To help with this problem, SQLAlchemy provides an ORM to automatically populate
your Python objects from the database and to update the database based on changes
to your Python objects. Using the ORM is as simple as writing your classes, defining
your tables, and mapping your tables to your classes. In the case of the user table, you
could perform a simple mapping via the following code:
class User(object): pass
mapper(User, user_table)
Notice that there is nothing particularly special about the User class defined here. It is
used to create “plain old Python objects,” or POPOs. All the magic of SQLAlchemy is
performed by the mapper. Although the class definition just shown is empty, you may
define your own methods and attributes on a mapped class. The mapper will create
attributes corresponding to the column names in the mapped table as well as some
private attributes used by SQLAlchemy internally. Once your table is mapped, you can
use a Session object to populate your objects based on data in the user table and flush
any changes you make to mapped objects to the database:
>>> Session = sessionmaker()
>>> session = Session()
>>>
>>> # Insert a user into the database
... u = User()
>>> u.user_name='rick'
>>> u.email_address='[email protected]'
>>> u.password='parrot'
>>> session.save(u)
>>>
>>> # Flush all changes to the session out to the database
... session.flush()
>>>
>>> query = session.query(User)
>>> # List all users
... list(query)
[<__main__.User object at 0x2abb96dae3d0>]
>>>
>>> # Get a particular user by primary key
... query.get(1)
<__main__.User object at 0x2abb96dae3d0>
>>>
>>> # Get a particular user by some other column
... query.get_by(user_name='rick')
<__main__.User object at 0x2abb96dae3d0>
>>>
>>> u = query.get_by(user_name='rick')
>>> u.password = 'foo'
What Is SQLAlch | 3
>>> session.flush()
>>> query.get(1).password
'foo'
As you can see, SQLAlchemy makes persisting your objects simple and concise. You
can also customize and extend the set of properties created by SQLAlchemy, allowing
your objects to model, for instance, a many-to-many relationship with simple Python
lists.
Notice the two auxilliary tables used to provide many-to-many joins between users and
groups, and between groups and users. Once we have this schema in place, a common
scenario is to check whether a particular user has a particular permission. In SQL, we
might write:
SELECT COUNT(*) FROM tf_user, tf_group, tf_permission WHERE
tf_user.user_name='rick' AND tf_user.id=user_group.user_id
AND user_group.group_id = group_permission.group_id
AND group_permission.permission_id = tf_permission.id
AND permission_name='admin';
class Group(object):
users=[]
permissions=[]
class Permission(object):
groups=[]
Suppose we wanted to print out a summary of all of a given user’s groups and permis-
sions, something an object-oriented style would do quite well. We might write some-
thing like the following:
print 'Summary for %s' % user.user_name
for g in user.groups:
print ' Member of group %s' % g.group_name
for p in g.permissions:
print ' ... which has permission %s' % p.permission_name
On the other hand, if we wanted to determine whether a user has a particular permis-
sion, we would need to do something like the following:
In this case, we needed to write a nested loop, examining every group the user is a
member of to see if that group had a particular permission. SQLAlchemy lets you use
object-oriented programming where appropriate (such as checking for a user’s per-
mission to do something) and relational programming where appropriate (such as
printing a summary of groups and permissions). In SQLAlchemy, we could print the
summary information exactly as shown, and we could detect membership in a group
with a much simpler query. First, we need to create mappings between our tables and
our objects, telling SQLAlchemy a little bit about the many-to-many joins:
mapper(User, user_table, properties=dict(
groups=relation(Group, secondary=user_group, backref='users')))
mapper(Group, group_table, properties=dict(
permissions=relation(Permission, secondary=group_permission,
backref='groups')))
mapper(Permission, permission_table)
Now, our model plus the magic of the SQLAlchemy ORM allows us to detect whether
the given user is an administrator:
q = session.query(Permission)
rick_is_admin = q.count_by(permission_name='admin',
... user_name='rick')
SQLAlchemy was able to look at our mappers, determine how to join the tables, and
use the relational model to generate a single call to the database. The SQL generated
by SQLAlchemy is actually quite similar to what we would have written ourselves:
SELECT count(tf_permission.id)
FROM tf_permission, tf_user, group_permission, tf_group, user_group
WHERE (tf_user.user_name = ?
AND ((tf_permission.id = group_permission.permission_id
AND tf_group.id = group_permission.group_id)
AND (tf_group.id = user_group.group_id
AND tf_user.id = user_group.user_id)))
AND (tf_permission.permission_name = ?)
SQLAlchemy’s real power comes from its ability to bridge the object/relational divide
and allow you to use whichever model is appropriate to your task at hand. Aggregation
is another example of using SQLAlchemy’s relational model rather than the object-
oriented model. Suppose we wanted a count of how many users had each permission
type. In the traditional object-oriented world, we would probably loop over each per-
mission, then over each group, and finally count the users in the group (without
forgetting to remove duplicates!). This leads to something like this:
for p in permissions:
users = set()
In SQLAlchemy, we can drop into the SQL expression language to create the following
query:
q=select([Permission.c.permission_name,
func.count(user_group.c.user_id)],
and_(Permission.c.id==group_permission.c.permission_id,
Group.c.id==group_permission.c.group_id,
Group.c.id==user_group.c.group_id),
group_by=[Permission.c.permission_name],
distinct=True)
rs=q.execute()
for permission_name, num_users in q.execute():
print 'Permission %s has %d users' % (permission_name, num_users)
Although the query is a little longer in this case, we are doing all of the work in the
database, allowing us to reduce the data transferred and potentially increase perform-
ance substantially due to reduced round-trips to the database. The important thing to
note is that SQLAlchemy makes “simple things simple, and complex things possible.”
SQLAlchemy Philosophy
SQLAlchemy was created with the goal of letting your objects be objects, and your
tables be tables. The SQLAlchemy home page puts it this way:
SQLAlchemy Philosophy
SQL databases behave less and less like object collections the more size and performance
start to matter; object collections behave less and less like tables and rows the more
abstraction starts to matter. SQLAlchemy aims to accommodate both of these principles.
—From http://www.sqlalchemy.org
Using the object mapper pattern (where plain Python objects are mapped to SQL tables
via a mapper object, rather than requiring persistent objects to be derived from some
Persistable class) achieves much of this separation of concerns. There has also been a
concerted effort in SQLAlchemy development to expose the full power of SQL, should
you wish to use it.
In SQLAlchemy, your objects are POPOs until you tell SQLAlchemy about them. This
means that it is entirely possible to “bolt on” persistence to an existing object model
by mapping the classes to tables. For instance, consider an application that uses users,
groups, and permissions, as shown. You might prototype your application with the
following class definitions:
class User(object):
SQLAlchemy Philosophy | 7
self.user_name = user_name
self.password = password
self._groups = groups
class Group(object):
class Permission(object):
Once your application moves beyond the prototype stage, you might expect to have to
write code to manually load objects from the database or perhaps some other kind of
persistent object store. If you are using SQLAlchemy, on the other hand, you would
just define your tables:
user_table = Table(
'tf_user', metadata,
Column('id', Integer, primary_key=True),
Column('user_name', Unicode(16), unique=True, nullable=False),
Column('password', Unicode(40), nullable=False))
group_table = Table(
permission_table = Table(
'tf_permission', metadata,
Column('id', Integer, primary_key=True),
Column('permission_name', Unicode(16), unique=True,
nullable=False))
user_group = Table(
'user_group', metadata,
Column('user_id', None, ForeignKey('tf_user.id'),
primary_key=True),
Column('group_id', None, ForeignKey('tf_group.id'),
primary_key=True))
group_permission = Table(
'group_permission', metadata,
Column('group_id', None, ForeignKey('tf_group.id'),
primary_key=True),
Column('permission_id', None, ForeignKey('tf_permission.id'),
primary_key=True))
and you’re done. No modification of your objects is required—they are still simply
new-style (derived from the object class) Python classes, and they still have whatever
methods you have defined, as well as a few attributes added by SQLAlchemy (described
in the sidebar “Instrumentation on Mapped Classes”). Your old methods join_group,
leave_group, etc. still work, even without modifying the class code. This means that
you can modify mapped “collection” properties (properties modeling 1:N or M:N re-
lationships) with regular list operations, and SQLAlchemy will track your changes and
flush them to the database automatically.
SQLAlchemy Philosophy | 9
_state
SQLAlchemy uses this property to track whether a mapped object is “clean” (fresh-
ly fetched from the databaes), “dirty” (modified since fetching from the database),
or “new” (as-yet unsaved to the database). This property generally should not be
modified by the application programmer.
mapped properties
One attribute will be added to the mapped class for each property specified in the
mapper, as well as any “auto-mapped” properties, such as columns. In the previous
example, the mapper adds user_name, password, id, and _groups to the User class.
So, if you are planning on using SQLAlchemy, you should stay away from naming any
class attributes c or _state, and you should be aware that SQLAlchemy will instrument
your class based on the properties defined by the mapper.
SQLAlchemy also allows you the full expressiveness of SQL, including compound
(multi-column) primary keys and foreign keys, indices, access to stored procedures,
the ability to “reflect” your tables from the database into your application, and even
the ability to specify cascading updates and deletes on your foreign key relationships
and value constraints on your data.
SQLAlchemy Architecture
SQLALchemy consists of several components, including the aforementioned database-
independent SQL expression language object-relational mapper. In order to enable
these components, SQLAlchemy also provides an Engine class, which manages con-
nection pools and SQL dialects, a MetaData class, which manages your table informa-
tion, and a flexible type system for mapping SQL types to Python types.
Engine
The beginning of any SQLAlchemy application is the Engine. The engine manages the
SQLAlchemy connection pool and the database-independent SQL dialect layer. In our
previous examples, the engine was created implicitly when the MetaData was created:
metadata=MetaData('sqlite://')
engine = metadata.bind
This engine can later be bound to a MetaData object just by setting the bind attribute
on the MetaData:
metadata.bind = engine
The engine can be used to execute queries directly on the database via dynamic SQL:
for row in engine.execute("select user_name from tf_user"):
print 'user name: %s' % row['user_name']
Most of the time, you will be using the higher-level facilities of the SQL expression
language and ORM components of SQLAlchemy, but it’s nice to know that you can
always easily drop down all the way to raw SQL if you need to.
Connection Pooling
Thus far, we have glossed over the use of database connections. In fact, all of our
examples up to this point have used SQLAlchemy’s powerful connection pooling sub-
system. In order to execute queries against a database, a connection is required, and
the establishment of a new connection is typically an expensive operation, involving a
network connection, authentication of the user, and any database session setup re-
quired. In order to amortize the costs, the typical solution is to maintain a pool of
database connections that are used over and over again in the application.
The Engine object in SQLAlchemy is responsible for managing a pool of low-level DB-
API connections. In fact, both the engine and the low-level connection objects obey a
Connectable protocol, allowing you to execute dynamic SQL queries either directly
against a connection, or against the engine (in which case the engine will automatically
allocate a connection for the query).
In another instance of making simple things simple and complex things possible,
SQLAlchemy does “The Right Thing” most of the time with connections, and allows
you to override its strategy when required. SQLAlchemy’s default strategy is to acquire
a connection for each SQL statement, and when that connection is no longer used
(when its result set is closed or garbage-collected), to return it to the pool. If you would
like to manually manage your collections, you can also do that via the connect( ) meth-
od on the engine object:
engine = create_engine('sqlite://')
connection = engine.connect()
result = connection.execute("select user_name from tf_user")
for row in result:
print 'user name: %s' % row['user_name']
result.close()
SQLAlchemy has another strategy for connection pooling that has some performance
benefits in many cases: the “thread-local” strategy. In the thread-local strategy, a con-
nection that is currently in use by a thread will be reused for other statements within
that thread. This can reduce database server load, which is especially important when
you could have several applications accessing the database simultaneously. If you want
SQLAlchemy Architecture | 11
to use the thread-local strategy, simply create the Engine object and set the strategy to
threadlocal:
engine = create_engine('sqlite://', strategy='threadlocal')
MetaData Management
The MetaData object in SQLAlchemy is used to collect and organize information about
your table layout (i.e., your database schema). We alluded to MetaData management
before in describing how to create tables. A MetaData object must be created before any
tables are defined, and each table must be associated with a MetaData object. Meta
Data objects can be created “bound” or “unbound”, based on whether they are asso-
ciated with an engine. The following is an example of the different ways you can create
MetaData objects:
# create an unbound MetaData
unbound_meta = MetaData()
group_table = Table(
'tf_group', meta,
Column('id', Integer, primary_key=True),
Column('group_name', Unicode(16), unique=True, nullable=False))
As mentioned previously, you can also reflect your schema by setting the autoload
parameter to True in your Table creation. Reflection, however, requires a database
connection to function properly. (SQLAlchemy must query the database to determine
the structure of the tables.) Binding the MetaData to an engine is a convenient way to
provide this connection. Note, however, that you are never required to bind the Meta
Data object; any operation that you can perform with a bound MetaData or a table
defined on it can also be performed by passing the engine or connection to the indi-
vidual method. This might be useful if you wish to use the same MetaData object for
multiple distinct database engines:
meta = MetaData()
engine1 = create_engine('sqlite:///test1.db')
engine2 = create_engine('sqlite:///test2.db')
# Use the engine parameter to load tables from the first engine
user_table = Table(
'tf_user', meta, autoload=True, autoload_with=engine1)
group_table = Table(
'tf_group', meta, autoload=True, autoload_with=engine1)
permission_table = Table(
'tf_permission', meta, autoload=True, autoload_with=engine1)
user_group_table = Table(
'user_group', meta, autoload=True, autoload_with=engine1)
group_permission_table = Table(
'group_permission', meta, autoload=True, autoload_with=engine1)
SQLAlchemy Architecture | 13
# Select some data
result_set = engine1.execute(user_table.select())
Types System
In many cases, SQLAlchemy can map SQL types to Python types in a straightforward
way. In order to do this, SQLAlchemy provides a set of TypeEngine-derived classes that
convert SQL data to Python data in the sqlalchemy.types module. TypeEngine sub-
classes are used to define the MetaData for tables.
Sometimes, in keeping with the SQLAlchemy philosophy of letting your objects be
objects, you may find that the provided TypeEngine classes do not express all of the data
types you wish to store in your database. In this case, you can write a custom TypeEn
gine that converts data being saved to the database to a database-native type, and con-
verts data being loaded from the database to a Python native type. Suppose, for instance,
that we wished to have a column that stored images from the Python Imaging Library
(PIL). In this case, we might use the following TypeEngine definition:
class ImageType(sqlalchemy.types.Binary):
Once we have defined ImageType, we can use that type in our table definitions, and the
corresponding PIL image will be automatically created when we select from the data-
base or serialized when we insert or update the database.
Of course, you aren’t required to use the SQL expression language; you can always
insert custom SQL instead:
q = user_table.select("""tf_user.user_name LIKE 'r%'""")
SQLAlchemy Architecture | 15
You can also use SQL functions in your queries by using the SQLAlchemy-supplied
func object:
q=select([Permission.c.permission_name,
func.count(user_group.c.user_id)],
and_(Permission.c.id==group_permission.c.permission_id,
Group.c.id==group_permission.c.group_id,
Group.c.id==user_group.c.group_id),
group_by=[Permission.c.permission_name],
distinct=True)
group_table = Table(
'tf_group', metadata,
Column('id', Integer, primary_key=True),
Column('group_name', Unicode(16), unique=True, nullable=False))
user_group = Table(
'user_group', metadata,
Column('user_id', None, ForeignKey('tf_user.id'), primary_key=True),
Column('group_id', None, ForeignKey('tf_group.id'),
... primary_key=True))
Here, the mapper would create properties on the User class for the columns of the table:
id, user_name, email_address, password, first_name, last_name, and created. On the
Group class, the id and group_name properties would be defined. The mapper, however,
has a great deal more flexibility. If we wished to store only a hash of the user’s password
in the database, rather than the actual plaintext password, we might modify the User
class and mapper to the following:
import sha
class User(object):
def _get_password(self):
return self._password
def _set_password(self, value):
self._password = sha.new(value).hexdigest()
password=property(_get_password, _set_password)
Now we can access all the groups that a user is a member of by simply accessing the
groups property. We can also add a user to a group by either appending the user to the
group’s users property, or appending the group to the user’s groups property:
# user1's "groups" property will automatically be updated
group1.users.append(user1)
The ORM uses a Session object to keep track of objects loaded from the database and
the changes made to them. Sessions are used to persist objects created by the applica-
tion, and they provide a query interface to retrieve objects from the database. Rather
than executing the database code to synchronize your objects with your tables every
SQLAlchemy Architecture | 17
time an object is modified, the Session simply tracks all changes until its flush( ) meth-
od is called, at which point all the changes are sent to the database in a single unit of
work.
A Session class is created using the sessionmaker( ) function, and a Session object is
created by instantiating the class returned from sessionmaker( ). Although you can
instantiate the Session object directly, the sessionmaker function is a convenient way
to fix the parameters that will be passed to the Session’s constructor, rather than re-
peating them wherever a Session is instantiated.
To insert objects into the database, we simply need to save them to the session:
Session=sessionmaker()
session=Session()
u = User()
u.user_name='rick'
u.password='foo'
u.email_address='[email protected]'
session.save(u) # tell SQLAlchemy to track the object
session.flush() # actually perform the insert
To retrieve objects from the database, we need to first obtain a query object from the
session and then use its methods to specify which objects we retrieve:
q = session.query(User)
Note that the filter_by( ) method takes keyword arguments whose names match the
mapped properties. This is often a useful shortcut because you avoid having to type
out “User.c.” over and over, but is less flexible than the filter method, which can take
arbitrary SQL expressions as its criteria for selection. One powerful feature of SQLAl-
chemy is its ability, in the filter_by( ) method, to automatically search your joined
tables for a matching column:
# Retrieve all users in a group named 'admin'
users = list(q.filter_by(group_name='admin'))
SQLAlchemy will automatically search for tables with foreign key relationships that
contain the queried object to find columns to satisfy the keyword arguments. This can
be very powerful, but can also sometimes find the wrong column, particularly if you
are querying based on a common column name, such as name, for instance. In this case,
you can manually specify the joins that SQLAlchemy will perform in the query via the
join( ) method.
You can even specify a “join chain” by using a list of properties for the argument to
join( ):
q = session.query(User)
# groups is a property of a User, permissions is a property of a
... Group
q = q.join(['groups', 'permissions'])
q = q.filter(Permission.c.permission_name=='admin')
users = list(q)
The power of SQLAlchemy to construct complex queries becomes clear when we com-
pare the previous code to the SQL generated:
SELECT tf_user.first_name AS tf_user_first_name,
tf_user.last_name AS tf_user_last_name,
tf_user.created AS tf_user_created,
tf_user.user_name AS tf_user_user_name,
tf_user.password AS tf_user_password,
tf_user.email_address AS tf_user_email_address,
tf_user.id AS tf_user_id
FROM tf_user
JOIN user_group ON tf_user.id = user_group.user_id
JOIN tf_group ON tf_group.id = user_group.group_id
JOIN group_permission ON tf_group.id = group_permission.group_id
JOIN tf_permission ON tf_permission.id =
... group_permission.permission_id
WHERE tf_permission.permission_name = ? ORDER BY tf_user.oid
SQLAlchemy Architecture | 19
CHAPTER 2
Getting Started
This chapter guides you through installing version 0.4 of SQLAlchemy (the version
documented by this book) via EasyInstall. It will also give you a quick tutorial on the
basic features of SQLAlchemy to “get your hands dirty” as soon as possible.
Installing SQLAlchemy
In order to use SQLAlchemy, you need to install both the SQLAlchemy package as well
as a Python database driver for your database. This section will guide you through
installing both.
21
To install SetupTools, first download the bootstrap script ez_setup.py from http://
peak.telecommunity.com/dist/ez_setup.py. You will then need to run the script to down-
load the rest of SetupTools.
In Windows, you must make certain that you have administrator priv-
iledges before running easy_install or ez_setup.py, as both of these
scripts modify your Python site-packages directory.
In Windows, it is also generally a good idea to make sure that both
Python and your Python scripts directories are on your path. In the de-
fault Python installation, these directories are c:\python25 and c:\py
thon25\scripts.
In Unix-like systems, including Linux, BSD, and OS X, you can install SetupTools as
follows:
$ sudo python ez_setup.py
In Windows, you will need to open a command prompt and run the bootstrap script
as follows:
c:\>python ez_setup.py
Once you have installed SetupTools using ez_setup, you are ready to install SQLAl-
chemy.
On Windows, the corresponding command is as follows (as long as your scripts direc-
tory, generally c:\python25\scripts, is on your path):
c:\>easy_install -UZ SQLAlchemy
This will download and install SQLAlchemy to your Python site-packages directory. If
you wish to install a particular version of SQLAlchemy, add a version specifier to the
easy_install command line. In Unix, this would be:
$ sudo easy_install -UZ SQLAlchemy==0.4.1
This book covers the 0.4 release of SQLAlchemy, so confirm that the version installed
on your system is at least 0.4.0.
SQLAlchemy also has an extensive unit test suite that can be downloaded separately
(not via easy_install) from http://sqlalchemy.org if you wish to test the installation more
extensively.
Installing SQLAlchemy | 23
Other supported drivers
If you wish to connect to other databases, you must install the appropriate DB-API
driver module. The complete list of supported databases and drivers follows:
PostgreSQL
psycopg2 at http://www.initd.org/pub/software/psycopg/
SQLite
pysqlite at http://initd.org/pub/software/pysqlite/ or sqlite3 (included with Python
versions 2.5 and greater)
MySQL
MySQLdb at http://sourceforge.net/projects/mysql-python
Oracle
cx_Oracle at http://www.cxtools.net/
SQL Server
Support for Microsoft SQL server is provided by multiple drivers as follows:
• pyodbc at http://pyodbc.sourceforge.net/ (recommended driver)
• adodbapi at http://adodbapi.sourceforge.net/
• pymssql at http://pymssql.sourceforge.net/
Firebird
kinterbasdb at http://kinterbasdb.sourceforge.net/
Informix
informixdb at http://informixdb.sourceforge.net/
SQLAlchemy Tutorial
Once you have installed SQLAlchemy and the SQLite driver (either pysqlite or
sqlite3), you can start really exploring SQLAlchemy. This tutorial shows off some of
the basic features of SQLAlchemy that you can use to become immediately productive.
This tutorial is based on a stripped-down version of a user authentication module that
might be used in a web application.
The MetaData object we create is bound to a particular database Engine, in this case a
SQLite engine connected to the database located in the file tutorial.sqlite. If tutori
al.sqlite does not already exist, it will be created automatically by SQLite.
Once we have created our MetaData, we can define our tables. The first table defined is
the user table:
user_table = Table(
'tf_user', metadata,
Column('id', Integer, primary_key=True),
Column('user_name', Unicode(16),
unique=True, nullable=False),
Column('password', Unicode(40), nullable=False),
Column('display_name', Unicode(255), default=''),
Column('created', DateTime, default=datetime.now))
Notice how the Table constructor is given the SQL name of the table ('tf_user'), a
reference to the metadata object, and a list of columns. The columns are similarly de-
fined with their SQL names, data types, and various optional constraints. In this case,
since we defined an 'id' column as a primary key, SQLAlchemy will automatically
create the column with an auto-increment default value. Also note that we can specify
uniqueness and nullability constraints on columns, provide literal defaults, or provide
Python callables (e.g. datetime.now) as defaults.
Next, we define our group and permission tables:
group_table = Table(
'tf_group', metadata,
Column('id', Integer, primary_key=True),
Column('group_name', Unicode(16),
unique=True, nullable=False))
permission_table = Table(
'tf_permission', metadata,
Column('id', Integer, primary_key=True),
Column('permission_name', Unicode(16),
unique=True, nullable=False))
Each table is simply defined with an auto-increment primary key and a unique name.
Finally, we define the join tables that provide a many-to-many relationship between
users and groups and groups and permissions:
user_group_table = Table(
'tf_user_group', metadata,
Column('user_id', None, ForeignKey('tf_user.id'),
primary_key=True),
Column('group_id', None, ForeignKey('tf_group.id'),
primary_key=True))
SQLAlchemy Tutorial | 25
group_permission_table = Table(
'tf_group_permission', metadata,
Column('permission_id', None, ForeignKey('tf_permission.id'),
primary_key=True),
Column('group_id', None, ForeignKey('tf_group.id'),
primary_key=True))
Note in particular the use of compound primary keys (each table is keyed by two col-
umns) and the use of foreign key constraints. We also specified the data type of the
foreign key columns as None. When a foreign key column is specified with this datatype,
SQLAlchemy will examine the column on the related table (e.g., 'tf_user.id') to de-
termine the data type for the foreign key column.
Once the tables have been defined, we can create them in the database using the fol-
lowing code:
metadata.create_all()
If you were not creating the database, but rather connecting to an existing database,
you could, of course, leave out the call to metadata.create_all( ). SQLAlchemy will in
any case create tables using the IF NOT EXISTS syntax, so a metadata.create_all( ) is a
safe operation.
Once the insert statement has been created, it can be executed multiple times with
different values:
stmt.execute(user_name='rick', password='secret',
display_name='Rick Copeland')
stmt.execute(user_name='rick1', password='secret',
display_name='Rick Copeland Clone')
If we wish to see the actual SQL generated, we can instruct SQLAlchemy to log the
queries using the metadata.bind.echo property:
>>> metadata.bind.echo = True
>>> stmt.execute(user_name='rick2', password='secret',
... display_name='Rick Copeland Clone 2')
2007-09-06 10:19:52,317 INFO sqlalchemy.engine.base.Engine.0x..50
... INSERT INTO tf_user (user_name, password, display_name, created)
...
... VALUES (?, ?, ?, ?)
2007-09-06 10:19:52,318 INFO sqlalchemy.engine.base.Engine.0x..50
... ['rick2', 'secret', 'Rick Copeland Clone 2', '2007-09-06
... 10:19:52.317540']
2007-09-06 10:19:52,319 INFO sqlalchemy.engine.base.Engine.0x..50
... COMMIT
Note again that SQLAlchemy uses bind parameters for the values to be inserted, and
that SQLAlchemy automatically generates the created column value based on the result
of calling datetime.now( ) when the insert was executed.
To select data back out of the table, we can use the table’s select( ) method as follows:
>>> stmt = user_table.select()
>>> result = stmt.execute()
>>> for row in result:
... print row
...
(1, u'rick', u'secret1', u'Rick Copeland',
... datetime.datetime(2007, 9, 7, 10, 6, 4, 415754))
(2, u'rick1', u'secret', u'Rick Copeland Clone',
... datetime.datetime(2007, 9, 7, 10, 6, 4, 476505))
(3, u'rick2', u'secret', u'Rick Copeland Clone 2',
... datetime.datetime(2007, 9, 7, 10, 6, 4, 543650))
We can also retrieve values from each row of the result using dict-like indexing or
simple attribute lookup as follows:
>>> result = stmt.execute()
>>> row =result.fetchone()
>>> row['user_name']
u'rick'
>>> row.password
u'secret1'
>>> row.created
datetime.datetime(2007, 9, 7, 10, 6, 4, 415754)
>>> row.items()
[(u'id', 1), (u'user_name', u'rick'), (u'password', u'secret1'),
... (u'display_name', u'Rick Copeland'),
... (u'created', datetime.datetime(2007, 9, 7, 10, 6, 4, 415754))]
To restrict the rows that are returned from the select( ) method, we can supply a where
clause. SQLAlchemy provides a powerful SQL expression language to assist in the
construction of where clauses, as shown in the following example:
>>> stmt = user_table.select(user_table.c.user_name=='rick')
>>> print stmt.execute().fetchall()
[(1, u'rick', u'secret1', u'Rick Copeland',
... datetime.datetime(2007, 9, 7, 10, 6, 4, 415754))]
SQLAlchemy Tutorial | 27
>>>
>>> # Create a delete statement that deletes all users
... # except for 'rick'
... stmt = user_table.delete(user_table.c.user_name != 'rick')
>>> stmt.execute()
<sqlalchemy.engine.base.ResultProxy object at 0x2b12bf430210>
>>> # Select the users back from the database
... user_table.select().execute().fetchall()
[(1, u'rick', u'secret123', u'Rick Copeland',
... datetime.datetime(2007, 9, 7, 18, 35, 35, 529412))]
>>> # Add the users back
... stmt = user_table.insert()
>>> stmt.execute(user_name='rick1', password='secret',
... display_name='Rick Copeland Clone')
<sqlalchemy.engine.base.ResultProxy object at 0xa20c90>
>>> stmt.execute(user_name='rick2', password='secret',
... display_name='Rick Copeland Clone 2')
<sqlalchemy.engine.base.ResultProxy object at 0xa20cd0>
>>>
SQLAlchemy also provides for more generalized queries via the insert( ), select( ),
update( ), and delete( ) functions (rather than the methods on Table objects) to allow
you to specify more complex SQL queries. Again, this is covered in more detail in
Chapter 5.
The simplest case of mapping is to just declare empty classes for our application objects
and declare an empty mapper:
class User(object): pass
class Group(object): pass
class Permission(object): pass
mapper(User, user_table)
mapper(Group, group_table)
mapper(Permission, permission_table)
Now that we have declared the mapping between our classes and tables, we can start
doing queries. First off, though, we need to understand the unit of work (UOW) pat-
tern. In UOW as implemented by SQLAlchemy, there is an object known as a
Session that tracks changes to mapped objects and can flush( ) them out en masse to
the database in a single “unit of work.” This can lead to substantial performance im-
provement when compared to executing multiple separate updates. In SQLAlchemy,
Once we have the session object, we use it to obtain access to a Query object for our class:
query = session.query(User)
The simplest way to use the Query object is as an iterator for all the objects in the
database. Since we have already inserted a row in the user_table, we can retrieve that
row as a User object:
>>> list(query)
[<__main__.User object at 0xb688d0>,
... <__main__.User object at 0xb68910>,
... <__main__.User object at 0xb68c10>]
>>> for user in query:
... print user.user_name
...
rick
rick1
rick2
We can also retrieve an object from the database by using its primary key with the get(
) method on the Query object:
>>> query.get(1)
<__main__.User object at 0xb688d0>
If we want to filter the results retrieved by the Query object, we can use the filter( )
and filter_by( ) methods:
>>> for user in query.filter_by(display_name='Rick Copeland'):
... print user.id, user.user_name, user.password
...
1 rick secret123
>>> for user in query.filter(User.c.user_name.like('rick%')):
... print user.id, user.user_name, user.password
...
1 rick secret123
2 rick1 secret
3 rick2 secret
Note the use of the .c attribute of the User object. It was added by the mapper as a
convenience to access the names of mapped columns. If we wanted to, we could freely
substitute user_table.c.user_name for User.c.user_name, and vice versa.
To insert objects into the database, we simply create an object in Python and then use
the save( ) method to notify the session about the object:
>>> newuser = User()
>>> newuser.user_name = 'mike'
SQLAlchemy Tutorial | 29
>>> newuser.password = 'password'
>>> session.save(newuser)
Due to the UOW pattern, the new user has not yet been saved to the database. If we
try to count the users using the user_table, we still get 3:
>>> len(list(user_table.select().execute()))
3
If, however, we try to use the Query object, the ORM recognizes the need to perform a
flush( ) on the Session, inserts the new user, and we get a count of 4:
>>> metadata.bind.echo = True
>>> query.count()
2007-09-09 21:33:09,482 INFO sqlalchemy.engine.base.Engine.0x..50
... INSERT INTO tf_user (user_name, password, display_name, created)
...
... VALUES (?, ?, ?, ?)
2007-09-09 21:33:09,482 INFO sqlalchemy.engine.base.Engine.0x..50
... ['mike', 'password', '', '2007-09-09 21:33:09.481854']
2007-09-09 21:33:09,485 INFO sqlalchemy.engine.base.Engine.0x..50
... SELECT count(tf_user.id)
FROM tf_user
2007-09-09 21:33:09,486 INFO sqlalchemy.engine.base.Engine.0x..50 []
4
To delete an object, simply call the session’s delete( ) method with the object to be
deleted. To flush the session and commit the transaction, we call session.commit( ):
>>> session.delete(newuser)
>>>
>>> session.commit()
The SQLAlchemy ORM also includes support for managing relationships between
classes, as well as flexible overrides of its column-mapping conventions. The ORM is
covered in more detail in Chapters 6, 7, 8.
SQLAlchemy Tutorial | 31
CHAPTER 3
Engines and MetaData
This chapter introduces SQLAlchemy’s Engine and MetaData classes. The Engine class
provides database connectivity, including a connection pool with various strategies for
acquiring connections from the pool. The MetaData class maintains information about
your database schema, including any tables and indices defined. In this chapter, you
will learn how to define a new database schema using MetaData as well as how to connect
a MetaData instance to an existing schema.
The first argument to create_engine( ) is the RFC-1738 style URL specifying the data-
base. The general form of the url is: driver://username:password@host:port/database.
33
Of course, the various database drivers interpret these URLs in slightly different ways,
as illustrated here. It is also possible to pass additional arguments to the low-level DB-
API driver created by SQLAlchemy via either a URL query string:
url='postgres://rick:foo@localhost/pg_db?arg1=foo&arg2=bar'
engine = create_engine(url)
If you wish complete control over the connection creation process, you can even pass
a function (or other callable object) that returns a DB-API connection to create_engine(
) in the creator argument:
import psycopg
def connect_pg():
return psycopg.connect(user='rick', host='localhost')
engine = create_engine('postgres://', creator=connect_pg)
The full set of keyword arguments accepted by create_engine( ) are specified in here:
connect_args
A dictionary of options to be passed to the DBAPI’s connect( ) method. The default
is {}.
convert_unicode
Indicates whether the engine should convert all unicode values into raw byte strings
before going into the database, and convert raw byte strings to unicode coming
out of result sets. This can be useful, for instance, when dealing with a database
server or schema that does not provide unicode support natively. The default is
False.
creator
A callable that returns a DB-API connection. The default is None.
echo
A flag that tells SQLAlchemy to echo all statements and bind parameter values to
its logger. The default is None.
echo_pool
A flag that tells SQLAlchemy to log all connection pool checkins and checkouts.
The default is False.
encoding
Specifies the encoding to use in all translations between raw byte strings and Python
unicode objects. The default is False.
module
Specifies which module to use when a database implementation can use more than
one (such as PostgreSQL and Oracle). The default is None.
The loggers used with SQLAlchemy are listed next. Note that several of these loggers
deal with material covered in later chapters (in particular, the sqlalchemy.orm.* log-
gers):
• sqlalchemy.engine -- control SQL echoing. logging.INFO logs SQL query output,
logging.DEBUG logs result sets as well.
• sqlalchemy.pool -- control connection pool logging. logging.INFO logs checkins
and checkouts.
• sqlalchemy.orm -- control logging of ORM functions. logging.INFO logs configu-
rations and unit of work dumps.
— sqlalchemy.orm.attributes -- Logs instrumented attribute operations.
— sqlalchemy.orm.mapper -- Logs mapper configurations and operations.
— sqlalchemy.orm.unitofwork -- Logs unit of work operations, including depend-
ency graphs.
— sqlalchemy.orm.strategies -- Logs relation loader operations (lazy and eager
loads).
— sqlalchemy.orm.sync -- Logs synchronization of attributes from one object to
another during a flush.
The ResultProxy object has several useful methods and attributes for returning infor-
mation about the query:
__iter__( )
Allows iteration over a result proxy, generating RowProxy objects
fetchone( )
Fetches the next RowProxy object from the ResultProxy
fetchall( )
Fetches all RowProxy objects at once
scalar( )
Fetches the next row from the cursor and treat it as a scalar (i.e. not a RowProxy)
keys
List of the column names in the result set
rowcount
The total number of rows in the result set
close( )
Closes the ResultProxy, possibly returning the underlying Connection to the pool
The RowProxy object generated by the ResultProxy provides several useful methods that
allow you to retrieve data, such as a tuple, dictionary, or object:
__getattr__( )
Provides access to data via object.column name
__getitem__( )
Provides access to data via object[column name] or object[column position]
keys( )
Provides a list of all the column names in the row
values( )
Provides a list of all the values in the row
items( )
Provides a list of (column name, value) tuples for the row
Connection Pooling
SQLAlchemy provides the connection pool as an easy and efficient way to manage
connections through the database. Normally, you don’t need to worry about the con-
nection pool, because it is automatically managed by the Engine class. The connection
connection = psycopg.connect(database='mydb',
username='rick', password='foo')
The pool.manage( ) call sets up a connection pool (the exact object is an instance of
sqlalchemy.pool.DBProxy). The connect( ) method then works just as the Engine’s
connect( ) method, returning a proxy for the DB-API connection from the managed
connection pool. When the connection proxy is garbage collected, the underlying DB-
API connection is returned to the connection pool.
By default, the connect( ) method returns the same connection object if it is called
multiple times in a given thread (the same “threadlocal” strategy used by the Engine).
To specify that the pool should generate a new connection each time that connect( ) is
called, pass use_threadlocal=False to the pool.manage( ) function.
If you wish to use a particular connection pool class instead of the DBProxy as shown
previously, SQLAlchemy provides the ability to directly instantiate the pool:
from sqlalchemy import pool
import psycopg2
import sqlite
def getconn_pg():
c = psycopg2.connect(database='mydb', username='rick',
password='foo')
return c
def getconn_sl():
c = sqlite.connect(filename='devdata.sqlite')
return c
Some of the various pool types that are available in the sqlalchemy.pool module are:
AssertionPool
Allows only one connection to be checked out at a time and raises an AssertionEr
ror when this constraint is violated.
NullPool
Does no pooling; instead, actually opens and closes the underlying DB-API con-
nection on each check out/check in of a connection.
MetaData
SQLAlchemy provides the MetaData class, which collects objects that describe tables,
indices, and other schema-level objects. Before using any of the higher-level features of
SQLAlchemy, such as the SQL query language and the ORM, the schema of the data-
base must be described using metadata. In some cases, you can reflect the structure of
schema items into the MetaData from the database. In this case, you need only specify
the name of the entity, and its structure will be loaded from the database directly.
MetaData | 39
Note that you are never required to bind the MetaData object; all operations that rely
on a database connection can also be executed by passing the Engine explicitly as the
keyword parameter bind. This is referred to as explicit execution. If a MetaData instance
is bound, then the bind parameter can be omitted from method calls that rely on the
database connection. This is referred to as implicit execution. The “bound-ness” of a
MetaData object is shared by all Tables, Indexes, and Sequences in the MetaData, so a
Table attached to a bound MetaData, for instance, would be able to create itself via:
table.create()
Defining Tables
The most common use of the MetaData object is in defining the tables in your schema.
In order to define tables in the MetaData, you use the Table and Column classes as shown
in the following example:
from sqlalchemy import *
from datetime import datetime
metadata=MetaData()
user_table = Table(
'tf_user', metadata,
Column('id', Integer, primary_key=True),
Column('user_name', Unicode(16), unique=True, nullable=False),
Column('email_address', Unicode(255), unique=True, nullable=False),
Column('password', Unicode(40), nullable=False),
Column('first_name', Unicode(255), default=''),
Column('last_name', Unicode(255), default=''),
Column('created', DateTime, default=datetime.now))
Unlike some other database mapping libraries, SQLAlchemy fully supports the use of
composite and non-integer primary and foreign keys:
brand_table = Table(
'brand', metadata,
Column('id', Integer, primary_key=True),
Column('name', Unicode(255), unique=True, nullable=False))
product_table = Table(
'product', metadata,
Column('brand_id', Integer, ForeignKey('brand.id'),
... primary_key=True),
Column('sku', Unicode(80), primary_key=True))
style_table = Table(
'style', metadata,
Column('brand_id', Integer, primary_key=True),
Column('sku', Unicode(80), primary_key=True),
Column('code', Unicode(80), primary_key=True),
To actually create a table, you can call the create( ) method on it. Here, we will create
the style table on an in-memory SQLite database and view the generated SQL.
We see that the composite primary key and foreign key constraints are correctly gen-
erated. Although the foreign key constraints are ignored by SQLite, it is still useful to
generate them, as SQLAlchemy can use this information to perform joins automatically
based on the foreign key relationships between tables.
The Table constructor Table.__init__(self, name, metadata,*args, **kwargs), takes the
following arguments:
name
The table name as known by the database (may be combined with the schema pa-
rameter).
metadata
The MetaData object to which to attach this table.
*args
The series of Column and Constraint objects to define for this table.
schema
The schema name for this table, if required by the database. in **kwargs, the default
is None.
autoload
Indicates whether to reflect the columns from the database. in **kwargs, the default
is False.
autoload_with
The Connectable used to autoload the columns. in **kwargs, the default is None.
MetaData | 41
include_columns
The list of column names (strings) to be reflected if autoload=True. If None, all col-
umns are reflected. If not None, any columns omitted from the list will not be
represented on the reflected Table object. In **kwargs, the default is None.
mustexist
Indicates that the table must already be defined elsewhere in the Python application
(as part of this MetaData). An exception is raised if this is not true. In **kwargs, the
default is False.
useexisting
Directs SQLAlchemy to use the previous Table definition for this table name if it
exists elsewhere in the application. (SQLAlchemy disregards the rest of the con-
structor arguments if this is True.) in **kwargs, the default is False.
owner
Specifies the owning user of the table. This is useful for some databases (such as
Oracle) to help with table reflection. In **kwargs, the default is None.
quote
Forces the table name to be escaped and quoted before being sent to the database
(useful for table names that conflict with SQL keywords, for example). in
**kwargs, the default is False.
quote_schema
Forces the schema name to be escaped and quoted before being sent to the data-
base. In **kwargs, the default False.
The Table constructor also supports database-specific keyword arguments. For in-
stance, the MySQL driver supports the mysql_engine argument to specify the backend
database driver (i.e. 'InnoDB' or 'MyISAM', for instance).
Table reflection
Tables can also be defined using reflection from an existing database. This requires a
database connection, and so either a bound MetaData must be used, or a Connectable
must be supplied via the autoload_with parameter:
db = create_engine('sqlite:///devdata.sqlite')
brand_table = Table('brand', metadata, autoload=True,
... autoload_with=db)
You can also override the reflected columns if necessary. This can be particularly useful
when specifying custom column data types, or when the database’s introspection fa-
cilities fail to identify certain constraints.
brand_table = Table('brand', metadata,
Column('name', Unicode(255)), # override the reflected type
autoload=True)
If you want to reflect the entire database schema, you may do so by specifying
reflect=True in the metadata constructor. Of course, in this case, the MetaData must
You can also use the reflect( ) method of the MetaData to load the schema. Meta
Data.reflect( bind=None, schema=None, only=None) takes the following arguments:
bind
A Connectable used to access the database; required only when the MetaData is
unbound. The default is None.
schema
Specifies an alternate schema from which to reflect tables. The default is None.
only
Directs the MetaData to load only a subset of the available tables. This can be speci-
fied either as a sequence of the names to be loaded or as a boolean callable that
will be called for each available table with the parameters only( metadata, table
name). If the callable returns True, the table will be reflected. The default is None.
Column Definitions
The Column constructor Column.__init__(self, name, type_, *args, **kwargs) takes the
following arguments:
name
The name of the column as it is known by the database.
type_
The TypeEngine for this column. This can also be None if the column is a Foreign
Key, in which case the type will be the same as the referenced column.
*args
Constraint, ForeignKey, ColumnDefault, and Sequence objects that apply to the col-
umn.
key
An alias for this column. If specified, the column will be identified everywhere in
Python by this name rather than by its SQL-native name. In **kwargs, the default
is None.
primary_key
If True, marks the column as part of the primary key. (Alternatively, the Table can
have a PrimaryKeyConstraint defined.) In **kwargs, the default is False.
MetaData | 43
nullable
If set to False, this does not allow None as a value for the column. in **kwargs, the
default is True, unless the column is a primary key.
default
A Python callable or a SQL expression language construct specifying a default value
for this column. Note that this is an active (Python-generated) default when a call-
able is specified; the SQL has the generated value inserted as a literal. In
**kwargs, the default is None.
index
Indicates that the column is indexed (with an autogenerated index name). Alter-
natively, use an Index object in the table declaration instead. In **kwargs, the default
False.
unique
Indicates that the column has a unique constraint. Alternatively, use an UniqueCon
straint object in the table declation instead. In **kwargs, default False.
onupdate
Specifies an active default value (generated by SQLAlchemy rather than the data-
base server) to be used when updating (but not inserting) a row in the table. In
**kwargs, the default is None.
autoincrement
Indicates that integer-based primary keys should have autoincrementing behavior.
This is applicable only if the column has no default value and is a type or subtype
of Integer. In **kwargs, the default is True.
quote
This forces the column name to be escaped and quoted before being sent to the
database (useful for column names that conflict with SQL keywords, for example).
In **kwargs, the default is False.
Constraints
SQLAlchemy also supports a variety of constraints, both at the column level and at the
table level. All constraints are derived from the Constraint class, and take an optional
name parameter.
Primary keys
The usual way to declare primary key columns is to specify primary_key=True in the
Column constructor:
You can also specify primary keys using the PrimaryKeyConstraint object:
product_table = Table(
'product', metadata,
Column('brand_id', Integer, ForeignKey('brand.id')),
Column('sku', Unicode(80)),
PrimaryKeyConstraint('brand_id', 'sku', name='prikey'))
To see the SQL generated to create such a table, we can create it on the in-memory
SQLite database:
Foreign keys
Foreign keys are references from a row in one table to a row in another table. The usual
way to specify simple (non-complex) foreign keys is by passing a ForeignKey object to
the Column constructor. The ForeignKey constructor ForeignKey.__init__( self, col-
umn, constraint=None, use_alter=False, name=None, onupdate=None, ondelete=None)
takes the following parameters:
column
Either a Column object or a database-recognized string, such as tablename .colum-
nname or schemaname.tablename.columnname, that specifies the referenced column.
constraint
The owning ForeignKeyConstraint, if any. If left unspecified, a new ForeignKeyCon
straint will be created and added to the parent table. The default is None.
use_alter
Use an ALTER TABLE command to create the constraint (passed along to the
owning ForeignKeyConstraint). Otherwise, the constraint will be created in the
CREATE TABLE statement. The default is False.
MetaData | 45
name
The name of the constraint (passed along to the owning ForeignKeyConstraint).
The default is None.
onupdate
Generates an ON UPDATE clause in the SQL for the constraint (e.g., onup-
date='CASCADE' would generate “ON UPDATE CASCADE” to cascade changes in
the referenced columns to the foreign key). Commonly supported values for ON
UPDATE are RESTRICT (raise an error), CASCADE (shown previously), SET
NULL (set the column to NULL), and SET DEFAULT (set the column to it’s pas-
sive default). The default for this parameter is None. Not supported for all database
drivers/backends.
ondelete
Generates an ON DELETE clause in the SQL for the constraint (e.g. onde-
lete='CASCADE' would generate “ON DELETE CASCADE” to cascade deletions
of the referenced row to the row with the foreign key). The default is None. Not
supported for all database drivers/backends.
If you need to reference a compound primary key, SQLAlchemy provides the Foreign
KeyConstraint class for increased flexibility. To use the ForeignKeyConstraint, simply
pass a list of columns in the local table (the compound foreign key) and a list of columns
in the referenced table (the compound primary key):
style_table = Table(
'style', metadata,
Column('brand_id', Integer, primary_key=True),
Column('sku', Unicode(80), primary_key=True),
Column('code', Unicode(80), primary_key=True),
ForeignKeyConstraint(
['brand_id', 'sku'],
['product.brand_id', 'product.sku']))
CHECK constraints
CheckConstraints can also be specified, either at the column level (in which case they
should only refer to the column on which they are defined), or at the Table level (in
which case they should refer only to any column in the table). CheckConstraints are
specified with a text constraint that will be passed directly through to the underlying
database implementation, so care should be taken if you want to maintain database
independence in the presence of CheckConstraints. MySQL and SQLite, in particular,
do not actively support such constraints.
For instance, if you wanted to validate that payments were always positive amounts,
you might create a payment table similar to the following:
payment_table = Table(
'payment', metadata,
Column('amount', Numeric(10,2), CheckConstraint('amount > 0')))
To see the SQL generated, we can execute the table creation statements on SQLite
(recognizing that SQLite will not enforce the CHECK constraint).
MetaData | 47
CREATE TABLE payment (
amount NUMERIC(10, 2) CHECK (amount > 0)
)
Defaults
SQLAlchemy provides several methods of generating default values for columns when
inserting and updating rows. These default values fall into one of two categories: active
defaults or passive defaults.
Active defaults
Active defaults are values that are generated by SQLAlchemy and then sent to the da-
tabase in a separate statement. Active defaults include constants, Python callables, SQL
expressions (including function calls) to be executed before the insert or update, or a
pre-executed sequence. In all of these cases, SQLAlchemy manages the generation of
the default value and the statement that actually sends the default to the database.
Active defaults are divided into two classes: insert defaults and the update defaults,
which are specified separately (allowing a different default on insert and update, if that
Here, we have created several defaults with constants, as well as two “created” defaults.
One is the standard library function datetime.now( ), and the other is the SQL function
CURRENT_TIMESTAMP. The created_apptime column, upon insertion, will contain
the current time on the application’s machine, whereas the created_dbtime column will
contain the database server’s current time. The SQL generated is illustrative:
>>> e=create_engine('sqlite://', echo=True)
>>> user_table.create(bind=e)
2007-08-25 14:52:17,595 INFO sqlalchemy.engine.base.Engine.0x..50
CREATE TABLE tf_user (
id INTEGER NOT NULL,
user_name VARCHAR(16) NOT NULL,
password VARCHAR(40) NOT NULL,
first_name VARCHAR(255),
last_name VARCHAR(255),
created_apptime TIMESTAMP,
created_dbtime TIMESTAMP,
modified TIMESTAMP,
PRIMARY KEY (id),
UNIQUE (user_name)
)
MetaData | 49
2007-08-25 14:52:17,606 INFO sqlalchemy.engine.base.Engine.0x..50
... ['rick', 'foo', '', '', '2007-08-25 14:52:17.604140',
... u'2007-08-25 18:52:17']
2007-08-25 14:52:17,607 INFO sqlalchemy.engine.base.Engine.0x..50
... COMMIT
<sqlalchemy.engine.base.ResultProxy object at 0x2aff31673690>
>>> e.execute(user_table.update(user_table.c.user_name=='rick'),
... password='secret')
2007-08-25 15:01:48,804 INFO sqlalchemy.engine.base.Engine.0x..50
... UPDATE tf_user SET password=?, modified=?
... WHERE
... tf_user.user_name = ?
2007-08-25 15:01:48,805 INFO sqlalchemy.engine.base.Engine.0x..50
... ['secret', '2007-08-25 15:01:48.774391', 'rick']
2007-08-25 15:01:48,805 INFO sqlalchemy.engine.base.Engine.0x..50
... COMMIT
<sqlalchemy.engine.base.ResultProxy object at 0x2adaf2551e50>
>>>
The SQL generated for the table creation had no reference to the default values. This
is because these values were active defaults, as opposed to the passive defaults covered
in the next section.
The current_timestamp is selected from the database for use in the insert statement.
Two different timestamps are sent to the database, one for created_apptime, and one
for created_dbtime. In this case, the application machine’s native Python time res-
olution is greater than the current_timestamp provided by SQLite.
Though we did not specify an update to the modified column, SQLAlchemy provides
an update value based on the onupdate parameter of the column definition.
Passive defaults
Passive defaults are default values provided by the database itself. If a column is marked
with a PassiveDefault instance, then the column will have a database-level default value
and SQLAlchemy will make the Engine aware of the passive default. The Engine will,
in turn, mark the ResultProxy as having passive default values. The ResultProxy is ac-
tually inspected by the object-relational mapping system to determine whether or not
to re-fetch the row after an insert to get the default column values.
We can enhance the previous example by providing a passive default for the cre
ated_dbtime column:
user_table = Table(
'tf_user', MetaData(),
Column('id', Integer, primary_key=True),
Column('user_name', Unicode(16), unique=True, nullable=False),
Column('password', Unicode(40), nullable=False),
MetaData | 51
The SQL generated for the table creation does contain a reference to the default
created_dbtime, unlike the active default example.
The created_dbtime is not provided to the database in the insert statement; it will be
provided by the database itself.
The result set is flagged as having a passive default via the lastrow_has_defaults( )
function, and so we recognize the need to fetch the row back from the database.
PostgreSQL does not support passive defaults for primary keys. This is
due to the fact that SQLAlchemy does not use the PostgreSQL OIDs to
determine the identity of rows inserted (OIDs are actually disabled by
default in PostgreSQL version 8.), and psycopg2’s cursor.lastrowid( )
function only returns OIDs. Thus the only way to know the primary key
of a row that is being inserted is to provide it as an active default.
Defining Indexes
Once your database grows to a certain size, you will probably need to consider adding
indexes to your tables to speed up certain selects. The easiest way to index a column
is to simply specify index=True when defining the Column.
user_table = Table(
'tf_user', MetaData(),
Column('id', Integer, primary_key=True),
Column('user_name', Unicode(16), unique=True, nullable=False,
... index=True),
Column('password', Unicode(40), nullable=False),
Column('first_name', Unicode(255), default=''),
Column('last_name', Unicode(255), default='', index=True))
In this case, the index will be created with an auto-generated name. If a column is
defined with both index=True and unique=True, then the UNIQUE constraint is created
on the index rather than on the column. The SQL generated for the previous table
definition is illustrative:
>>> e = create_engine('sqlite://', echo=True)
>>> user_table.create(bind=e)
2007-08-25 16:30:36,542 INFO sqlalchemy.engine.base.Engine.0x..90
CREATE TABLE tf_user (
id INTEGER NOT NULL,
user_name VARCHAR(16) NOT NULL,
password VARCHAR(40) NOT NULL,
first_name VARCHAR(255),
last_name VARCHAR(255),
PRIMARY KEY (id)
)
If the index is defined before the table is created, then the index will be created along
with the table. Otherwise, you can create the index independently via its own create(
) function:
>>> i = Index('idx_name', user_table.c.first_name,
... user_table.c.last_name,
... unique=True)
>>> i.create(bind=e)
2007-08-25 16:30:36,566 INFO sqlalchemy.engine.base.Engine.0x..90
... CREATE UNIQUE INDEX idx_name ON tf_user (first_name, last_name)
2007-08-25 16:30:36,566 INFO sqlalchemy.engine.base.Engine.0x..90
... None
2007-08-25 16:30:36,567 INFO sqlalchemy.engine.base.Engine.0x..90
... COMMIT
Index("idx_name", Column('first_name', Unicode(length=255),
... default=ColumnDefault('')),
... Column('last_name',Unicode(length=255),
... default=ColumnDefault('')), unique=True)
MetaData | 53
Creating Explicit Sequences
In our examples up to this point, in order to generate a unique integer key for inserted
rows, we have simply specified that the table’s primary key was an integer value. In this
case, SQLAlchemy does what is generally “The Right Thing”: either generates a column
with an auto-incrementing data type (AUTOINCREMENT, SERIAL, etc.) if one is available in
the Dialect being used, or if an auto-incrementing data type is not available (as in the
case of PostgreSQL and Oracle), implicitly generates a sequence and fetches values from
that sequence.
SQLAlchemy also provides for the explicit use of a Sequence object to generate default
values for columns (not just primary keys). To use such a sequence, simply add it to
the parameter list of the Column object:
brand_table = Table(
'brand', metadata,
Column('id', Integer, Sequence('brand_id_seq'), primary_key=True),
Column('name', Unicode(255), unique=True, nullable=False))
MetaData Operations
SQLAlchemy uses the MetaData object internally for several purposes, particularly in-
side the object relational mapper (ORM), which is covered in Chapter 6. MetaData can
also be used in connection with Engine and other Connectable instances to create or
drop tables, indices, and sequences from the database.
Binding MetaData
As mentioned previously, MetaData can be bound to a database Engine. This is done in
one of three ways:
• Specify the Engine URI in the MetaData constructor
• Specify an actual Engine or other Connectable object in the MetaData constructor
• Assign the bind attribute of an “unbound” MetaData to an Engine or other Connect
able
The various ways of binding MetaData are illustrated in the following examples:
# Create a bound MetaData with an implicitly created engine
bound_meta2 = MetaData('sqlite:///test2.db')
MetaData | 55
# Create an Engine and bind the MetaData to it
db1 = create_engine('sqlite://')
unbound_meta.bind = db1
Binding the MetaData object to an engine allows the MetaData and the objects attached
to it (Tables, Indexes, Sequences, etc.) to perform database operations without explicitly
specifying an Engine:
from sqlalchemy import *
metadata = MetaData('sqlite://')
user_table = Table(
'tf_user', metadata,
Column('id', Integer, primary_key=True),
Column('user_name', Unicode(16), unique=True, nullable=False,
... index=True),
Column('password', Unicode(40), nullable=False),
Column('first_name', Unicode(255), default=''),
Column('last_name', Unicode(255), default='', index=True))
MetaData | 57
CHAPTER 4
SQLAlchemy Type Engines
This chapter introduces the SQLAlchemy type system. It covers the built-in types pro-
vided by SQLAlchemy, both database-independent types and database-specific types.
It then describes how to create your own custom types for use in mapping application
data onto your database schema.
Built-in Types
SQLAlchemy provides a fairly complete set of built-in TypeEngines for support of basic
SQL column types. The SQLAlchemy-provided TypeEngines are broken into the generic
types (those portable across multiple database engines) and the dialect-specific types,
which work only on particular databases.
59
If you want to keep your application portable across database servers,
it is a good idea to stick to the generic types and (possibly) application-
specific custom types, as any code that relies on database dialect-specific
TypeEngines will need to be modified if the database changes. In the
SQLAlchemy tradition of not getting in your way, however, full support
is provided for dialect-specific TypeEngines if you wish to exploit data-
base server-specific types.
Generic Types
The generic TypeEngines provided by SQLAlchemy are found in the sqlalchemy.types
package. These TypeEngines cover a fairly complete set of portable column types. The
TypeEngines supported, their corresponding Python type, and their SQL representation,
are listed in Table 4-1. Note that there are several TypeEngines defined in all caps (such
as CLOB). These are derived from other TypeEngines and may or may not be further
specialized to allow finer-grained specification of the underlying database type.
Table 4-1. Built-in Generic TypeEngines
Class name Python Type SQL Type (for SQLite Arguments
driver)
String string TEXT or VARCHAR length (default is unbounded)
Integer int INTEGER none
SmallInteger int SMALLINT none
Numeric float, Decimal NUMERIC precision=10, length=2
Float(Numeric) float NUMERIC precision=10
DateTime datetime.date TIMESTAMP none
time
Date datetime.date DATE none
Time datetime.time TIME none
Binary byte string BLOB length (default is unbounded)
Boolean bool BOOLEAN none
Unicode unicode TEXT or VARCHAR length (default is unbounded)
PickleType any object that can be BLOB none
pickled
FLOAT(Numeric) float, Decimal NUMERIC precision=10,length=2
TEXT(String) string TEXT length (default is unbounded)
DECIMAL(Numeric) float, Decimal NUMERIC precision=10,length=2
INT, INTEGER(Integer) int INTEGER none
TIMESTAMP(DateTime) datetime.date TIMESTAMP none
time
When using TypeEngines to specify columns in Tables, you can use either an instance
of the TypeEngine class or the class itself. If you use the class, the default parameters
will be used when constructing the SQL type. For instance, the following Python code:
test_table3 = Table(
'test3', metadata,
Column('c0', Numeric),
Column('c1', Numeric(4,6)),
Column('c3', String),
Column('c4', String(10)))
Dialect-Specific Types
In order to generate appropriate dialect-specific SQL CREATE TABLE statements from
these generic types, SQLAlchemy compiles those generic TypeEngines into dialect-spe-
cific TypeEngines. In some cases, in addition to implementing the generic types, a dialect
may provide dialect-specific types (such as IP address, etc.).
Some of the dialect-specific types don’t actually provide any special support for con-
verting between database values and Python values; these are generally used for
completeness, particularly when reflecting tables. In this case, no conversion is done
between the value supplied by the DB-API implementation and the application. This
behavior is indicated in the following tables by listing “none” as the Python type for
that TypeEngine. Tables 4-2 through 4-5 list some of the types provided by particular
database engines that are not automatically used by SQLAlchemy.
Built-in Types | 61
Table 4-2. MS SQL server types
Class name Python type SQL type Arguments
MSMoney none MONEY none
MSSmallMoney none SMALLMONEY none
AdoMSNVarchar unicode NVARCHAR length
MSBigInteger int BIGINT none
MSTinyInteger int TINYINT none
MSVariant none SQL_VARIANT none
MSUniqueIdentifier none UNIQUEIDENTIFIER none
Implementing a TypeDecorator
In order to implement a TypeDecorator, you must provide the base TypeEngine you are
“implementing” as well as two functions, convert_bind_param( ) and
convert_result_value( ). convert_bind_param( self, value, engine) is used to convert
Python values to SQL values suitable for the DB-API driver, and convert_result_value(
self, value, engine) is used to convert SQL values from the DB-API driver back into
Python values. The implemented TypeEngine is specified in the impl attribute on the
TypeDecorator.
For instance, if you wish to implement a type for validating that a particular Integer
column contains only the values 0, 1, 2, and 3 (e.g., to implement an enumerated type
in a database that does not support enumerated types), you would implement the fol-
lowing TypeDecorator:
from sqlalchemy import types
class MyCustomEnum(types.TypeDecorator):
impl=types.Integer
It is not necessary to specify in a TypeDecorator the SQL type used to implement the
column, as this will be obtained from the impl attribute. The TypeDecorator is used only
when an existing TypeEngine provides the correct SQL type for the type you are imple-
menting.
Performance-Conscious TypeDecorators
SQLAlchemy has a second, undocumented (at the time of this book’s writing) interface
for providing bind parameter and result value conversion. If you provide a
bind_processor( ) or result_processor( ) method in your TypeDecorator, then these will
be used instead of the convert_bind_param( ) and convert_result_value( ) methods. The
new “processor” interface methods take a database dialect as a parameter and return
a conversion function (a “processor”) that takes a single value parameter and returns
the (possibly converted) value. If no processing is necessary, you can simply return
None rather than a new processor:
def get_col_spec(self):
return 'NEWTYPE(%s)' % ','.join(self._args)
SQLAlchemy provides a rich Pythonic interface for constructing SQL updates and
queries, known as the SQL Expression Language. This language is based around the
concept of an SQL statement, which represents some database-independent SQL syntax
that may have one or more bind variables, and that can be executed on an SQL
Engine or other Connectable. This chapter introduces the various kinds of data manip-
ulation supported by SQLAlchemy (SQL INSERT, UPDATE, and DELETE) and
performed on the query interface (SQL SELECT).
>>> metadata=MetaData()
>>>
>>> simple_table = Table(
... 'simple', metadata,
... Column('id', Integer, primary_key=True),
... Column('col1', Unicode(20)))
>>>
>>> stmt = simple_table.insert()
>>> print stmt
INSERT INTO simple (id, col1) VALUES (:id, :col1)
Note in the previous example that SQLAlchemy has created bind parameters for each
of the columns in the table we created in the insert statement. We can examine the bind
67
parameters in a statement by compiling the statement and looking at its params attrib-
ute:
Note that the bind parameter values are supplied to the execute( ) method as keyword
parameters. These parameters can either be supplied either directly to the execute( )
method or in the statement construction phase.
If parameters are supplied in both the statement construction and the execute( ) call,
the parameters supplied with the execute( ) call override those supplied when creating
the statement.
Insert Statements
The Insert construct is perhaps the simplest. In order to create an Insert statement,
you can use either the Table.insert( ) method or the insert( ) function. (The method
is actually just a wrapper for the function.) The insert takes two arguments: the table
into which a row is being inserted, and an optional dictionary of values to be inserted.
Each key in the dictionary represents a column and may be either the metadata Col
umn object or its string identifier. The values provided can be one of the following:
• A literal Python value to be inserted.
• A SQL expression to be inserted, such as func.current_timestamp( ), which will
create the SQL INSERT INTO simple2 (col1, col2) VALUES (?, current_time
stamp).
• A Select statement (covered later in this chapter). In this case, the value to be
inserted is provided by a subquery.
It is also possible to use the DB-API’s executemany( ) to insert multiple rows in one
database call. To do this, simply provide an list (or other iterable) of binding dictionaries
to the execute( ) method on the statement or engine:
Update Statements
Update statements are similar to inserts, except that they can specify a “where” clause
that indicates which rows to update. Like insert statements, update statements can be
created by either the update( ) function or the update( ) method on the table being up-
dated. The only parameters to the update( ) function are the table being updated
(omitted if using the update( ) method), the where clause, and the values to be set.
The where clause of the update( ) query can be either a SQL clause object (covered later
in this chapter) or a text string specifying the update condition. In order to update every
row of a table, you can simply leave off the where clause. To update this simple table,
we can execute the following statement:
>>> stmt = simple_table.update(
... whereclause=text("col1='First value'"),
... values=dict(col1='1st Value'))
>>> stmt.execute()
<sqlalchemy.engine.base.ResultProxy object at 0xc77910>
>>> stmt = simple_table.update(text("col1='Second value'"))
>>> stmt.execute(col1='2nd Value')
...
<sqlalchemy.engine.base.ResultProxy object at 0xc77950>
>>> stmt = simple_table.update(text("col1='Third value'"))
>>> print stmt
UPDATE simple SET id=?, col1=? WHERE col1='Third value'
...
>>> engine.echo = True
>>> stmt.execute(col1='3rd value')
2007-09-25 08:57:11,253 INFO sqlalchemy.engine.base.Engine.0x..d0
... UPDATE simple SET col1=? WHERE col1='Third value'
2007-09-25 08:57:11,254 INFO sqlalchemy.engine.base.Engine.0x..d0
Here, we create an UPDATE statement, complete with both values to update and a
where clause.
Here, the where clause is bound when the statement is created, but the actual values
to be updated are passed to the execute( ) method.
Note that prior to execution, the SQL has a bind parameter for the id column, but
when the statement is executed, id is omitted because no value was provided for it.
Correlated update statements can also be generated using the SQL expression language.
A correlated update is an update whose values are provided by a select statement.
Suppose that we have a product catalog with the schema in the following listing, and
the data in Tables 5-1 through 5-3.
product_table = Table(
'product', metadata,
Column('sku', String(20), primary_key=True),
Column('msrp', Numeric))
store_table = Table(
'store', metadata,
Column('id', Integer, primary_key=True),
Column('name', Unicode(255)))
product_price_table = Table(
'product_price', metadata,
Column('sku', None, ForeignKey('product.sku'), primary_key=True),
Column('store_id', None, ForeignKey('store.id'), primary_key=True),
Column('price', Numeric, default=0))
sku msrp
"123" 12.34
"456" 22.12
"789" 41.44
id name
1 "Main Store"
2 "Secondary Store"
If we wish to globally set the price for all products in all stores to their MSRP price, we
could execute the following update:
This would cause the updated product_price_table to contain the values in Table 5-4.
Table 5-4. Contents of product_price table (after update)
Delete Statements
The Delete construct is used to delete data from the database. To create a Delete con-
struct, you can use either the delete( ) function or the delete( ) method on the table
from which you are deleting data. Unlike insert( ) and update( ), delete( ) takes no
values parameter, only an optional where clause (omitting the where clause will delete
Queries
The real power of the SQL expression language is in its query interface. This includes
both the actual queries (SQL “SELECT” statements) as well as the syntax for specifying
“WHERE” clauses (which may be used in UPDATEs and DELETEs, as well).
The goal of the SQL expression language, like the goal of SQLAlchemy in general, is
to provide functionality that doesn’t “get in your way” when you need to be more
specific about the SQL you need. In that vein, you can always use the Text construct
(used previously in the UPDATE and DELETE examples) to specify the exact SQL text
you would like to use. For most operations, however, the SQL expression language
makes for a succinct, secure, and less error-prone way of expressing your queries.
To achieve the same result using the select( ) function, simply provide the table in lieu
of columns:
>>> stmt = select([product_table])
>>> for row in stmt.execute():
... print row
...
(u'123', Decimal("12.34"))
(u'456', Decimal("22.12"))
(u'789', Decimal("41.44"))
The actual parameters used by select( ) are listed next. They are discussed in more
detail later in the chapter.
columns=None
A list of ClauseElement structures to be returned from the query.
bind=None
An engine on a connectable object on which to execute the statement. If this is
omitted, an engine binding will be inferred from referenced columns and/or tables,
if possible.
whereclause=None
A ClauseElement expression used to for the WHERE clause.
from_obj=[]
A list of Tables or other selectable objects that will be used to form the FROM
clause. If this is not specified, the FROM clause is inferred from the tables refer-
enced in other clauses.
order_by=None
A list of ClauseElements used to construct the ORDER BY clause.
group_by=None
A list of ClauseElements used to construct the GROUP BY clause.
having=None
A ClauseElement used to construct the HAVING clause.
distinct=False
Adds a DISTINCT qualifier to the column list in the SELECT statement.
Queries | 73
for_update=False
Adds a FOR UPDATE qualifier to the SELECT statement. Some databases support
other values for this parameter, such as MySQL, which supports "read" (translating
to LOCK IN SHARE MODE), or Oracle, which supports "nowait" (translating to
FOR UPDATE NOWAIT).
limit=None
The numerical limit for the number of rows returned. Typically this uses the LIMIT
clause, but SQLAlchemy provides some support for LIMIT even when the under-
lying database does not support it directly.
offset=None
The numerical offset for the starting row that is returned. Typically this uses the
OFFSET clause, but SQLAlchemy provides some support for OFFSET even when
the underlying database does not support it directly.
correlate=True
Indicates that this SELECT statement is to be “correlated” with its enclosing SE-
LECT statement if it is used as a subquery. In particular, any selectables present in
both this statement’s from_obj list and the enclosing statement’s from_obj list will
be omitted from this statement’s FROM clause.
use_labels=False
Generates unique labels for each column in the columns list, to ensure there are no
name collisions.
prefixes=None
A list of ClauseElements to be included directly after the SELECT keyword in the
generated SQL. This is used for dialect-specific SQL extensions, to insert text be-
tween the SELECT keyword and the column list.
>>> x = product_table.c.sku=="123"
>>> print type(x)
<class 'sqlalchemy.sql._BinaryExpression'>
>>> print x
product.sku = ?
>>> stmt=product_table.select(product_table.c.sku=="123")
Queries | 75
>>> print stmt
SELECT product.sku, product.msrp
FROM product
WHERE product.sku = ?
...
>>> print stmt.execute().fetchall()
2007-09-30 16:34:44,800 INFO sqlalchemy.engine.base.Engine.0x..10
... SELECT product.sku, product.msrp
FROM product
WHERE product.sku = ?
2007-09-30 16:34:44,800 INFO sqlalchemy.engine.base.Engine.0x..10
... ['123']
[(u'123', 12.34)]
Note that the “123” literal has been replaced by a “?” placeholder. This is an example
of SQLAlchemy using a bind parameter. By using bind parameters, SQLAlchemy
ensures that the entire SQL string passed to the database driver was constructed by
SQLAlchemy, and that it is safe from SQL-injection attacks. (Of course, this can be
subverted via the Text construct, which passes whatever the programmer specifies
to the database driver.)
Here, SQLAlchemy provides the value of the bind parameter to the database driver
directly.
All SQLAlchemy-provided operators generate a ClauseElement-derived object as a re-
sult of the operation. ClauseElements provide the overloaded operators (and other SQL-
constructing features) of the SQL expression language. This allows complex SQL
expressions to be built up from complex Python expressions. SQLAlchemy provides
overloading for most of the standard Python operators. This includes all the standard
comparison operators (==, !=, <, >, <=, >=). Note in particular the conversion of “==
None” to “IS NULL”.
Support is also provided for the arithmetic operators (+, -, *, /, and %), with special
support for database-independent string concatenation:
Arbitrary SQL operators (such as MySQL’s NULL-safe equality operator, <=>) are also
supported via the op( ) method on ClauseElements:
SQLAlchemy also provides for use of the SQL boolean operators AND, OR, and NOT,
as well as the LIKE operator for comparing strings. The bitwise logical operators &, |,
and ~ are used to implement AND, OR, and NOT, while the like( ) method on Clau
seElements is used to implement LIKE. Special care must be taken when using the AND,
OR, and NOT overloads because of Python operator precendence rules. For instance,
& binds more closely than <, so when you write A < B & C < D, what you are actually
writing is A < (B&C) < D, which is probably not what you intended. You can also use
the SQLAlchemy-provided functions and_, or_, and not_ to represent AND, OR, and
NOT if you prefer.
SQLAlchemy also provides for the use of arbitrary SQL functions via the func variable,
which generates functions using attribute access. You can also use the special function
func._ to add parentheses around a sub-expression if necessary.
>>> print func.now()
now()
>>> print func.current_timestamp
current_timestamp
>>> print func.abs(product_table.c.msrp)
abs(product.msrp)
>>> print func._(text('a=b'))
(a=b)
Queries | 77
SQLAlchemy provides several other useful methods on ClauseElements, summarized
next.
between(cleft, cright)
Produces a BETWEEN clause like column BETWEEN cleft AND cright.
distinct( )
Adds a DISTINCT modifier like DISTINCT column.
startswith(other)
Produces the clause column LIKE 'other%.
endswith(other)
Produces the clause column LIKE '%other‘.
in_(*other)
Produces an IN clause like column IN (other[0], other[1], ...). other can also be a
subquery.
like(other)
Produces a LIKE clause like column LIKE other.
op(operator)
Produces an arbitrary operator like column operator.
label(name)
Produces an AS construct for the column (a column alias) like column AS name.
Queries | 79
>>> print str(stmt2) == str(stmt)
True
We can use bind parameters with text( ) by using the “named colon” format (:name)
for the bind parameters. We can also bind the clause constructed to a particular engine
using the bind parameter to the text( ) function.
We could use the grouping provided by group_by (possibly filtered by having) to retrieve
how many stores carry each product:
We have already seen how we can use the distinct( ) method on ClauseElements to
specify that a column should be distinct in a result set. SQLAlchemy also provides
support for selecting only distinct rows in a result set via the distinct parameter to
select( ).
Queries | 81
FROM product_price
>>> print stmt.execute().fetchall()
[(u'456', 22.120000000000001), (u'789', 41.439999999999998),
... (u'456', 22.120000000000001), (u'789', 41.439999999999998)]
>>> stmt = select([product_price_table.c.sku,
... product_price_table.c.price],
... distinct=True)
>>> print stmt
SELECT DISTINCT product_price.sku, product_price.price
FROM product_price
>>> print stmt.execute().fetchall()
[(u'456', 22.120000000000001), (u'789', 41.439999999999998)]
Limiting and offseting is done after ordering and grouping, so you can use this construct
to provide a “paged” view of sorted data. This can be very useful, for instance, when
displaying sortable data on a web form.
Now, suppose we have a user interface that displays all the “product” records in the
system, optionally filtered by various criteria (manufacturer, department, etc.). We
might write the following function to return the filtered user list:
def get_prods(manufacturer=None,
department=None,
category=None,
class_=None,
subclass=None,
offset=None,
limit=None):
where_parts = []
if manufacturer is not None:
where_parts.append(product_table.c.manufacturer
== manufacturer)
if department is not None:
where_parts.append(product_table.c.department
== department)
if category is not None:
where_parts.append(product_table.c.category
== category)
if class_ is not None:
where_parts.append(product_table.c.class_
== class_)
if subclass is not None:
where_parts.append(product_table.c.subclass
== subclass)
whereclause=and_(*where_parts)
query = product_table.select(whereclause,
offset=offset, limit=limit)
return query
Now, we can use arbitrary filters, and the appropriate SQL WHERE clause will auto-
matically be constructed for us automatically:
>>> q = get_prods()
>>> print q
SELECT product.id, product.sku, product.manufacturer,
... product.department, product.category, product.class,
... product.subclass
FROM product
>>> q = get_prods(manufacturer="Neon")
>>> print q
SELECT product.id, product.sku, product.manufacturer,
... product.department, product.category, product.class,
... product.subclass
FROM product
WHERE product.manufacturer = ?
>>> q = get_prods(manufacturer="Neon", department="Auto")
>>> print q
SELECT product.id, product.sku, product.manufacturer,
... product.department, product.category, product.class,
Queries | 83
... product.subclass
FROM product
WHERE product.manufacturer = ? AND product.department = ?
The generative interface allows us to rewrite the previous function as the following:
def get_prods(manufacturer=None,
department=None,
category=None,
class_=None,
subclass=None,
offset=None,
limit=None):
query = product_table.select()
if manufacturer is not None:
query = query.where(product_table.c.manufacturer
== manufacturer)
if department is not None:
query = query.where(product_table.c.department
== department)
if category is not None:
query = query.where(product_table.c.category
== category)
if class_ is not None:
query = query.where(product_table.c.class_
== class_)
if subclass is not None:
query = query.where(product_table.c.subclass
== subclass)
query = query.offset(offset)
query = query.limit(limit)
return query
Although the two functions have the same functionality, the second one (using the
generative interface) is more flexible. Suppose we wanted to refactor the original func-
tion into multiple parts, with each part potentially adding a different filtering criterion.
In that case, we would need to pass a where_parts list through all the intermediate
functions. In the generative approach, all the information about the query is “wrapped
up” in the query itself, allowing us to build up a query piecemeal in several different
functions, without passing anything around but the query itself.
The generative interface actually consists of a set of methods on the statement con-
structed by the select( ) function or method. Those methods are summarized next.
Note that none of these functions actually modify the query object in place; rather, they
return a new query object with the new condition applied.
where( whereclause)
Add a constraint to the WHERE clause. All constraints added this way will be
AND-ed together to create the whole WHERE clause.
order_by( *clauses)
Generate an ORDER BY clause (or append the given clauses to an existing ORDER
BY clause).
Queries | 85
intersect_all( other, **kwargs)
Return an INTERSECT ALL with this selectable and another (covered in more
detail under “Joins and Set Operations“).
except_( other, **kwargs)
Return an EXCEPT with this selectable and another (covered in more detail under
“Joins and Set Operations“).
except_all( other, **kwargs)
Return an EXCEPT ALL with this selectable and another (covered in more detail
under “Joins and Set Operations“).
join( right, *args, **kwargs)
Return a INNER JOIN between this selectable and another (covered in more detail
under “Joins and Set Operations“).
outerjoin( right, *args, **kwargs)
Return a LEFT OUTER JOIN between this selectable and another (covered in more
detail under “Joins and Set Operations“).
as_scalar( )
Allows the query to be embedded in a column list of an enclosing query.
label( name)
Label the result of this query with name for use in the column list of an enclosing
query. Also implies as_scalar( ).
correlate( fromclause)
Specify a table on which to correlate, or use None to disable SQLAlchemy’s auto-
correlation on embedded subqueries.
select( whereclauses, **params)
Generate an enclosing SELECT statment that selects all columns of this select.
Joining selectables
To join two selectablesin (tables or other select statements) together, SQLAlchemy
provides the join( ) (implementing INNER JOIN) and outerjoin( ) (implementing
OUTER JOIN) functions, as well as join( ) and outerjoin( ) methods on all selectables.
The only difference between the *join( ) methods and the *join( ) functions is that the
methods implicitly use self as the left-hand side of the join.
Notice how we had to specify the join criteria for each of the joins in the statement.
Wouldn’t it be nice if the database could infer the ON clauses based on the foreign key
constraints? Well, SQLAlchemy does this automatically:
In some cases, we are not using the JOINed table to filter results, but we would like to
see the results from a JOINed table alongside results from the table we are using. In
this case, we can either use the select( ) function or use the column( ) method of the
query object:
But what if we want to return results that may not have matching rows in the JOINed
table? For this, we use the outerjoin function/method:
Queries | 87
>>> query = query.select_from(from_obj)
>>> query = query.column('product.msrp')
>>> print query
SELECT store.id, store.name, product.msrp
FROM store LEFT OUTER JOIN product_price
... ON store.id = product_price.store_id
LEFT OUTER JOIN product
... ON product.sku = product_price.sku
In this case, if there is not a matching entry in the product_price table or the product
table, then the query will insert None for the msrp column.
Although SQLAlchemy can automatically infer the correct join condition most of the
time, support is also provided for custom ON clauses via the onclause argument to
join( ) and outerjoin( ), a ClauseElement specifying the join condition.
Using aliases
When using joins, it is often necessary to refer to a table more than once. In SQL, this
is accomplished by using aliases in the query. For instance, suppose we have the fol-
lowing (partial) schema that tracks the reporting structure within an organization:
employee_table = Table(
'employee', metadata,
Column('id', Integer, primary_key=True),
Column('manager', None, ForeignKey('employee.id')),
Column('name', String(255)))
Now, suppose we want to select all the employees managed by an employee named
Fred. In SQL, we might write the following:
SELECT employee.name
FROM employee, employee AS manager
WHERE employee.manager_id = manager.id
AND manager.name = 'Fred'
SQLAlchemy also allows the use of aliasing selectables in this type of situation via the
alias( ) function or method:
SQLAlchemy can also choose the alias name automatically, which is useful for guar-
anteeing that there are no name collisions:
Queries | 89
Subqueries
SQLAlchemy provides rich support for subqueries (using a query inside another query).
We have already seen one type of subquery in the use of the join and in set operation
support. SQLAlchemy also allows subqueries to appear in the column list of a select
statement, in the right hand side of the SQL IN operator (using the SQLAlchemy-
provided in_( ) method on ClauseElements), and as an argument to the from_obj
parameter on the select( ) function.
Because the inner query is uncorrelated, rather than totaling the number of stores that
carry the given product, the query repeatedly calculates the number of rows in the
product_price table with any valid SKU.
>>> subquery =
... employee_table.select(employee_table.c.manager_id==None)
>>> stmt = select([subquery.c.id, subquery.c.manager_id,
... subquery.c.name],
... whereclause=subquery.c.name.like('Ted%'),
... from_obj=[subquery])
Queries | 91
>>> print stmt
SELECT id, manager_id, name
FROM (SELECT employee.id AS id, employee.manager_id AS manager_id,
... employee.name AS name
FROM employee
WHERE employee.manager_id IS NULL)
WHERE name LIKE ?
Introduction to ORMs
ORMs provide methods of updating the database by using your application objects.
For instance, to update a column in a mapped table in SQLAlchemy, you merely have
to update the object, and SQLAlchemy will take care of making sure that the change
is reflected in the database. ORMs also allow you to construct application objects based
on database queries. Chapter 7 will focus on how to use SQLAlchemy’s ORM to update
and query objects in the database.
93
Figure 6-1. Data mapper pattern
level_table = Table(
'level', metadata,
Column('id', Integer, primary_key=True),
Column('parent_id', None, ForeignKey('level.id')),
Column('name', String(20)))
category_table = Table(
'category', metadata,
Column('id', Integer, primary_key=True),
Column('level_id', None, ForeignKey('level.id')),
Column('parent_id', None, ForeignKey('category.id')),
Column('name', String(20)))
product_table = Table(
'product', metadata,
Column('sku', String(20), primary_key=True),
Column('msrp', Numeric))
product_summary_table = Table(
'product_summary', metadata,
Column('sku', None, ForeignKey('product.sku'), primary_key=True),
Column('name', Unicode(255)),
Column('description', Unicode))
product_category_table = Table(
'product_category', metadata,
Column('product_id', None, ForeignKey('product.sku'), primary_key=True),
Column('category_id', None, ForeignKey('category.id'), primary_key=True))
region_table = Table(
'region', metadata,
Column('id', Integer, primary_key=True),
Column('name', Unicode(255)))
store_table = Table(
'store', metadata,
Column('id', Integer, primary_key=True),
Column('region_id', None, ForeignKey('region.id')),
Column('name', Unicode(255)))
product_price_table = Table(
'product_price', metadata,
class Level(object):
def __repr__(self):
return '<Level %s>' % self.name
class Category(object):
def __repr__(self):
return '<Category %s.%s>' % (self.level.name, self.name)
class Product(object):
def __repr__(self):
return '<Product %s>' % self.sku
def __repr__(self):
return '<ProductSummary %s>' % self.name
class Region(object):
def __repr__(self):
return '<Region %s>' % self.name
class Store(object):
def __repr__(self):
return '<Store %s>' % self.name
class Price(object):
def __repr__(self):
return '<Price %s at %s for $%.2f>' % (
self.product.sku, self.store.name, self.price)
As shown previously, the mapper( ) function has added a few attributes to our class. The
attributes we’re interested in are c, id, and name. This c attribute is a proxy for the
store_table’s c attribute, and allows access to all the columns of the store_table.
The id and name attributes are actually class properties that track access to these at-
tributes in order to synchronize them with the database later. These are mapped
because the default behavior of the SQLAlchemy mapper is to provide a property for
each column in the selectable mapped, and the store_table has two columns, id and
name.
Note that we can still use the object just as if it had not been mapped (unless, of course,
we were relying on existing properties id and name, or an existing attribute c):
>>> r0 = Region(name="Northeast")
>>> r1 = Region(name="Southwest")
>>> print r0
<Region Northeast>
>>> print r1
<Region Southwest>
The difference now is that these objects can be loaded or saved to the database using
a session object (covered in more detail in the next chapter):
Note how SQLAlchemy automatically inserted the store names we specified into the
database, and then populated the mapped id attribute based on the synthetic key value
generated by the database. We can also update mapped properties once an object has
been saved to the database:
We can also customize the mapped property names on a column-by-column basis using
the properties parameter:
Using synonyms
SQLAlchemy provides certain functions and methods (covered in the next chapter)
that expect mapped property names as keyword arguments. This can be cumbersome
to use if we have mapped the column names to other property names (perhaps to allow
for user-defined getters and setters). In order to alleviate the burden of using the actual
property names, SQLAlchemy provides the synonym( ) function to allow a name to be
used “as if” it were a real property. Suppose, for instance, that we wish to verify that
all store names end in “Store”. We might use the following approach:
This defines the synonym “name” to be usable in all SQLAlchemy functions where
“_name” is useable.
Here we tried to create an object with an invalid name and were rejected.
Using the synonym, we can still select stores by name without abusing the private
attribute.
If you wish to create a property that is a true proxy for the original mapped property
(so you don’t have to write the getter and setter), you can use synonym(name,
proxy=True) to define it.
Mapping subqueries
In some cases, we may wish to create a property that is a combination of a few columns
or the result of a subquery. For instance, suppose we wanted to map the prod
uct_table, providing a property that will yield the average price of the product across
all stores. To do this, we use the column_property( ) function:
In this case, our application expects RouteSegments to have a beginning and an ending
MapPoint object, defined as follows:
class RouteSegment(object):
def __init__(self, begin, end):
self.begin = begin
self.end = end
class MapPoint(object):
def __init__(self, lat, long):
self.coords = lat, long
def __composite_values__(self):
return self.coords
def __eq__(self, other):
return self.coords == other.coords
def __ne__(self, other):
return self.coords != other.coords
def __repr__(self):
return '(%s lat, %s long)' % self.coords
We can then map the class and use it with the composite( ) function:
Now, if we select a product, we can observe that SQLAlchemy delays loading the de-
ferred column until its mapped property is actually accessed:
>>> metadata.bind.echo=True
>>> q = session.query(Product)
>>> prod=q.get_by(sku='123')
2007-10-08 11:27:45,582 INFO sqlalchemy.engine.base.Engine.0x..d0
... SELECT product.sku AS product_sku, product.msrp AS product_msrp
FROM product
WHERE product.sku = ? ORDER BY product.oid
LIMIT 1 OFFSET 0
2007-10-08 11:27:45,583 INFO sqlalchemy.engine.base.Engine.0x..d0
... ['123']
>>> print prod.image
If the default deferred behavior is not desired, columns can be individually deferred or
undeferred at query creation time by using the defer( ) and undefer( ) functions along
with the options( ) method of the Query object (described more completely in the next
chapter).
mapper(FullProduct, q)
Basic Relationships
The three main relationships modeled by SQLAlchemy are 1:N, M:N, and 1:1 (which
is actually a special case of 1:N). In a 1:N relationship, one table (the “N” side) generally
has a foreign key to another table (the “1” side). In M:N, two tables (the “primary”
tables) are related via a scondary, “join” table that has foreign keys into both primary
tables. A 1:1 relationship is simply a 1:N relationship where there is only one “N"-side
row with a foreign key to any particular “1"-side row.
1:N relations
To model each type of relationship, SQLAlchemy uses the relation( ) function in the
properties dict of the mapper. In many cases, SQLAlchemy is able to infer the proper
join condition for 1:N relations. For instance, since the stores in our data model are
members of regions (a 1:N relationship region:store), we can model this on our
Region class as follows:
SQLAlchemy is able to infer the 1:N relation type by the foreign key relationship
between region_table and store_table.
Adding a store to the region is as simple as appending on to the property. Generally,
when working at the ORM level, it is not necessary to worry about foreign keys. The
SELECT statement is necessary for SQLAlchemy to retrieve the inital contents of
the “stores” property.
SQLAlchemy automatically infers that a new store must be inserted with the
region_id properly set.
In some cases, SQLAlchemy is unable to infer the proper join condition (for instance,
when there are multiple foreign key relations between the two tables). In this case, we
can simply use the primaryjoin parameter to the relation( ) function:
mapper(Region, region_table, properties=dict(
stores=relation(Store,
primaryjoin=(store_table.c.region_id
==region_table.c.id))))
M:N relations
It is often useful to model many-to-many (M:N) type relations between objects. In the
database, this is accomplished by the use of an association or join table. In the following
schema, the relation between the product_table and the category_table is a many-to-
many:
category_table = Table(
'category', metadata,
Column('id', Integer, primary_key=True),
Column('level_id', None, ForeignKey('level.id')),
Column('parent_id', None, ForeignKey('category.id')),
Column('name', String(20)))
product_table = Table(
'product', metadata,
Column('sku', String(20), primary_key=True),
Column('msrp', Numeric))
product_category_table = Table(
'product_category', metadata,
Column('product_id', None, ForeignKey('product.sku'),
... primary_key=True),
Column('category_id', None, ForeignKey('category.id'),
... primary_key=True))
In SQLAlchemy, we can model this relationship with the relation( ) function and the
secondary parameter:
As in the case of the 1:N join, we can also explicitly specify the join criteria by using
the primaryjoin (the join condition between the table being mapped and the join table)
and the secondaryjoin (the join condition between the join table and the table being
related to) parameters:
mapper(Category, category_table, properties=dict(
products=relation(Product, secondary=product_category_table,
primaryjoin=(product_category_table.c.category_id
== category_table.c.id),
secondaryjoin=(product_category_table.c.product_id
== product_table.c.sku))))
mapper(Product, product_table, properties=dict(
categories=relation(Category, secondary=product_category_table,
primaryjoin=(product_category_table.c.product_id
== product_table.c.sku),
secondaryjoin=(product_category_table.c.category_id
== category_table.c.id))))
1:1 relations
SQLAlchemy also supports 1:1 mappings as a type of 1:N mappings. This is modeled
in our schema with the product_table and the product_summary_table:
product_table = Table(
'product', metadata,
Column('sku', String(20), primary_key=True),
Column('msrp', Numeric))
product_summary_table = Table(
'product_summary', metadata,
Column('sku', None, ForeignKey('product.sku'), primary_key=True),
Column('name', Unicode(255)),
Column('description', Unicode))
Note in particular the foreign key relationship between product_table and product_sum
mary_table. This relationship allows, in SQL, many product_summary_table rows to ex-
Using BackRefs
In most cases, when mapping a relation between two tables, we want to create a prop-
erty on both classes. We can certainly do this in SQLAlchemy by using two relation(
) calls, one for each mapper, but this is verbose and potentially leads to the two prop-
erties becoming out-of-sync with each other. To eliminate these problems, SQLAl-
chemy provides the backref parameter to the relation( ) function:
If we declare a backref on the products property, however, the two lists are kept in sync:
Rather than specifying just the backref’s name, we can also use the SQLAlchemy-pro-
vided backref( ) function. This function allows us to pass along arguments to the
relation that is created by the backref. For instance, if we wanted to declare the prod
uct property on the ProductSummary class rather than declaring the summary property on
the Product class, we could use backref( ) with uselist=False as follows:
mapper(ProductSummary, product_summary_table, properties=dict(
product=relation(Product,
backref=backref('summary', uselist=False))))
mapper(Product, product_table)
To specify the parent-child relationship between different levels, we can use the
relation( ) function with a little extra work. When there is a relation specified with a
self-referential foreign key constraint, SQLAlchemy assumes that the relation will be a
1:N relation. If we want to get only the “children” property working, then the mapper
setup is as simple as the following:
mapper(Level, level_table, properties=dict(
children=relation(Level)))
However, we would also like to get the backref to the parent working as well. For this,
we need to specify the “remote side” of the backref. In the case of the “parent” attribute,
the “local side” is the parent_id column, and the “remote side” is the id column. To
specify the remote side of a relation (or backref), we use the remote_side parameter:
Note that a list is used for the remote_side parameter to allow for compound foreign
keys in the relation.
All of the cascade values in the following list refer to various functions that are per-
formed by the Session object (covered in more detail in Chapter 7). The default value
for the cascade parameter on a relation is "save-update,merge".
all
Specifies that all options should be enabled except delete-orphan:
delete
When the parent object is marked for deletion via session.delete( ), mark the
child(ren) as well.
In some cases, SQLAlchemy can even instrument custom collection classes that are
not derived from Python’s built-in collection types by inspecting the class definition
and determining whether it is list-like, set-like, or dict-like. This inference is not
perfect, however, so SQLAlchemy provides two methods to override it. The first is the
__emulates__ class attribute. If you supply a built-in type as the value for this attribute,
SQLAlchemy will assume that your custom collection class is “like” the type you name.
So, to implement a collection that is set-like but includes a list-like append( ) method,
we could do the following:
class SetAndListLike(object):
__emulates__ = set
def __init__(self):
self._c = set()
def append(self, o):
self._c.add(o)
def remove(self, o):
self._c.remove(o)
def __iter__(self):
return iter(self._c)
class SetAndListLike(object):
__emulates__ = set
def __init__(self):
self._c = set()
@collection.appender
def append(self, o):
self._c.add(o)
def remove(self, o):
self._c.remove(o)
def __iter__(self):
return iter(self._c)
The following decorators are available for manually instrumenting your custom col-
lection class:
or the attribute:
mapper(Region, region_table, properties=dict(
stores=relation(Store,
collection_class=attribute_mapped_collection('name')))
If you wish to determine the key value to be used in some other way, you can also use
the SQLAlchemy-supplied MappedCollection class as base class for your custom dict-
like classes. MappedCollection takes a keyfunc parameter in its constructor just like the
mapped_collection( ) function.
Extending Mappers
Although the mapper function, combined with the various property creation functions,
is extremely powerful, it is sometimes useful to extend the functionality of a mapper.
To that end, SQLAlchemy provides the MapperExtension class, which can be extended
to provide mapper behavior modification via a series of hooks. Multiple MapperExten
Vertical Partitioning
In vertical partitioning, different mapped classes are retrieved from different database
servers. In the following example, we create product_table in one in-memory sqlite
database and product_summary_table in another:
engine1 = create_engine('sqlite://')
engine2 = create_engine('sqlite://')
metadata = MetaData()
product_table = Table(
'product', metadata,
Column('sku', String(20), primary_key=True),
Column('msrp', Numeric))
product_summary_table = Table(
'product_summary', metadata,
Column('sku', None, ForeignKey('product.sku'), primary_key=True),
Column('name', Unicode(255)),
Column('description', Unicode))
product_table.create(bind=engine1)
product_summary_table.create(bind=engine2)
Now, we can create and map the Product and ProductSummary classes:
class Product(object):
def __init__(self, sku, msrp, summary=None):
self.sku = sku
self.msrp = msrp
self.summary = summary
def __repr__(self):
return '<Product %s>' % self.sku
class ProductSummary(object):
def __init__(self, name, description):
self.name = name
self.description = description
def __repr__(self):
return '<ProductSummary %s>' % self.name
clear_mappers()
mapper(ProductSummary, product_summary_table, properties=dict(
product=relation(Product,
backref=backref('summary', uselist=False))))
mapper(Product, product_table)
Finally, we configure the session to load the Product class from engine1 and Product
Summary from engine2:
Note that the appropriate engine is invoked depending on which class is being queried,
completely transparently to the user.
Horizontal Partitioning
In horizontal partitioning, or “sharding,” the database schema (or part of it) is repli-
cated across multiple databases (“shards”). This essentially means that some rows of
a mapped table will be loaded from one database and some from another. In order to
use sharding, you must provide functions that identify which database to access in
various situations. These arguments are passed to the sessionmaker( ) function, along
with a class_ parameter specifying that we will be creating a ShardedSession:
Session = sessionmaker(class_=ShardedSession)
The first function that must be provided is the shard_chooser( mapper, instance,
clause=None) function. This function is responsible for returning a “shard ID” that
should contain the row for the given mapper and instance. The ID may be based off of
the instance’s properties, or it may simply be the result of a round-robin selection
scheme. If it is not based on attributes of the instance, the shard_chooser( ) should
modify the instance in some way to mark it as participating in the returned shard.
The next function that must be provided is the id_chooser( query, ident ) function. This
function, when presented with a query and a tuple of identity values (the primary key
of the mapped class), should return a list of shard IDs where the objects sought by the
query might reside. In a round-robin implementation, all of the shard IDs might be
returned. In other implementations, the shard ID might be inferred from the ident
parameter.
The final function that must be provided when using sharding is the
query_chooser( query) function, which should return a list of shard IDs where results
for a given query might be found. Note that both id_chooser( ) and query_chooser( )
may simply return a list of all the shard IDs, in which case each shard will be searched
for the results of the query.
In the following example, we create a sharded implementation of the product database
where products are stored according to the first digit of their SKU. If the first digit is
even, the products are stored in engine1; otherwise they are stored in engine2. All other
types of objects will be stored in engine2.
metadata = MetaData()
product_table = Table(
'product', metadata,
Column('sku', String(20), primary_key=True),
Column('msrp', Numeric))
metadata.create_all(bind=engine1)
metadata.create_all(bind=engine2)
class Product(object):
def __init__(self, sku, msrp):
self.sku = sku
self.msrp = msrp
def __repr__(self):
return '<Product %s>' % self.sku
clear_mappers()
product_mapper = mapper(Product, product_table)
def query_chooser(query):
return ['even', 'odd']
Session = sessionmaker(class_=ShardedSession)
session = Session(
shard_chooser=shard_chooser,
id_chooser=id_chooser,
query_chooser=query_chooser,
shards=dict(even=engine1,
odd=engine2))
This chapter introduces the SQLAlchemy Session object. You will learn how to use the
Session to perform queries and updates of mapped classes, as well as how to customize
the Session class and create a “contextual” session that simplifies session management.
Creating a Session
The first step in creating a session is to obtain a Session object from SQLAlchemy. One
way to do this is to directly instantiate the sqlalchemy.orm.session.Session class.
However, this constructor for the SQLAlchemy-provided Session has a number of
keyword arguments, making instantiating Sessions in this manner verbose and tedious.
In order to alleviate this burden, SQLAlchemy provides the sessionmaker( ) function,
which returns a subclass of orm.session.Session with default arguments set for its
constructor.
Once you have this customized Session class, you can instantiate it as many times as
necessary in your application without needing to retype the keyword arguments (which
in many applications will not change between Session instantiations). If you wish to
override the defaults supplied to sessionmaker, you can do so at Session instantiation
time. You can also modify the default arguments bound to a particular Session subclass
by calling the class method Session.configure( ):
127
# Create a Session class with the default
# options
Session = sessionmaker(bind=engine)
The sessionmaker( ) and the associated Session subclass’s configure class method and
constructor take the following keyword arguments:
bind=None
The database Engine or Connection to which to bind the session.
binds=None
Optional dictionary that provides more detailed binding information. The keys to
this dictionary can be mapped classes, mapper( ) instances, or Tables. The values
in the dictionary indicate which Engine or Connectable to use for a given mapped
class, overriding the values set in the bind parameter.
autoflush=True
When True, the Session will automatically be flush( )ed before executing any
queries against the session. This ensures that the results returned from the query
match the operations that have been done in-memory in the unit-of-work.
transactional=False
When True, the Session will automatically use transactions. To commit a set of
changes, simply use the Session’s commit( ) method. To revert changes, use the
rollback( ) method. Using transactional=True, it is never necessary to explicitly
begin( ) a transaction on a Session. It is, however, necessary to explicitly call
commit( ) at the end of your transaction.
twophase=False
This tells SQLAlchemy to use two-phase commits on all transactions (on databases
that support two-phase commits, currently MySQL and PostgreSQL, soon to in-
clude Oracle), which is useful when dealing with multiple database instances. In
this case, after flush( )ing changes to all databases but before issuing a COMMIT,
SQLAlchemy issues a PREPARE to each database, allowing the entire transaction
to be rolled back if an error is raised during any of the PREPARE executions.
echo_uow=False
When True, instructs the Session to log all unit-of-work operations. This is the
equivalent of setting a log level of logging.DEBUG for the 'sqlalchemy.orm.unitof
work' logger.
engine = create_engine('sqlite://')
metadata = MetaData(engine)
product_table = Table(
'product', metadata,
Column('sku', String(20), primary_key=True),
Column('msrp', Numeric))
class Product(object):
def __init__(self, sku, msrp, summary=None):
self.sku = sku
self.msrp = msrp
self.summary = summary
self.categories = []
self.prices = []
def __repr__(self):
return '<Product %s>' % self.sku
mapper(Product, product_table)
In order to save two products to the database, we can do the following. Note that both
the echo_uow property on the session as well as the echo property on the Engine are
True in order to display exactly what SQLAlchemy is doing in response to our
flush( ) call:
level_table = Table(
'level', metadata,
Column('id', Integer, primary_key=True),
Column('parent_id', None, ForeignKey('level.id')),
Column('name', String(20)))
category_table = Table(
'category', metadata,
Column('id', Integer, primary_key=True),
Column('level_id', None, ForeignKey('level.id')),
Column('parent_id', None, ForeignKey('category.id')),
Column('name', String(20)))
product_category_table = Table(
'product_category', metadata,
Column('product_id', None, ForeignKey('product.sku'),
... primary_key=True),
Column('category_id', None, ForeignKey('category.id'),
... primary_key=True))
class Product(object):
def __init__(self, sku, msrp, summary=None):
self.sku = sku
self.msrp = msrp
self.summary = summary
self.categories = []
self.prices = []
def __repr__(self):
return '<Product %s>' % self.sku
class Level(object):
def __init__(self, name, parent=None):
self.name = name
self.parent = parent
def __repr__(self):
return '<Level %s>' % self.name
class Category(object):
def __init__(self, name, level, parent=None):
self.name = name
self.level = level
self.parent = parent
def __repr__(self):
return '<Category %s.%s>' % (self.level.name, self.name)
Now we can create a product hierarchy and assign some categories just as if there were
no database, and the Session will infer the appropriate operations to persist the entire
data model:
Now that we have created all the objects and specified the relations between them, we
can save one object to the Session, and all related objects will be saved as well (this is
due to the default 'save-update' value of the cascade parameter in all the
relations( ) created). In this example, the department object is connected to all the
other objects through various relation( )s, so it is sufficient to save it alone. Once this
is done, we can flush the changes out to the database. For the purposes of brevity, we
will use a fresh session with echo_uow set to False.
Note in particular that SQLAlchemy has inferred the minimum change necessary to
update the relationship. Also note that SQLAlchemy allowed us to assign a normal
Python list for a relation( )-type property. This is in contrast to some other ORMs,
which require you to use specialized add/remove functions to change object relation-
ships. One caveat with SQLAlchemy is that you are still required to only use the
remove( ) and append( ) list when using dynamic relation loaders (declared with
dynamic_loader( ) or lazy='dynamic'). This is due to the fact that SQLAlchemy never
implicitly loads the entire list of related objects into memory and so cannot deduce how
to update the database if you use other methods of modifying the property.
mapper(Account, account_table)
we could deduct a certain amount from an account balance atomically by doing some-
thing like the following:
Notice that SQLAlchemy automatically removed the corresponding entries in the prod
uct_category_table. This is because we declared that to be the secondary parameter of
a many-to-many relation( ). This is a special feature of M:N relations. In 1:N relations,
unless you tell SQLAlchemy how to cascade a delete on the parent object, it will not
assume that the delete should be cascaded. In order to cascade delete( )s onto the child
objects, simply specify cascade='delete' (or 'all') in the relation( ) function call.
Extending Sessions
Similar to the MapperExtension covered in Chapter 6, SessionExtensions can be used to
hook into session operations. Unlike MapperExtensions, SessionExtensions cannot
modify the process that they “hook into” easily, making SessionExtensions more useful
for recording Session operations than influencing them directly. SessionExtensions are
installed via the extension parameter to the Session constructor.
The various methods that a subclass of SessionExtension can implement are described
here:
before_commit( self, session)
Called just before a commit is executed.
after_commit( self, session)
Called just after a commit is executed.
after_rollback( self, session)
Called just after a rollback has occurred.
before_flush( self, session, flush_context, objects)
Called just before the flush process starts. The objects parameter is the optional
list of objects passed to the Session’s flush( ) method.
Notice here that the query is generative, as were the SQL-layer queries mentioned in
Chapter 5. This means that SQLAlchemy will not actually execute the query on the
database until the results are iterated over. You can also retrieve all the results as a list
by calling the all( ) method on the query object:
>>> query.all()
2007-11-16 16:21:35,349 INFO sqlalchemy.engine.base.Engine.0x..90
... SELECT product.sku AS product_sku, product.msrp AS product_msrp
FROM product ORDER BY product.oid
2007-11-16 16:21:35,349 INFO sqlalchemy.engine.base.Engine.0x..90 []
[<Product 123>, <Product 456>, <Product 222>]
Since retrieving the entire collection of mapped objects isn’t very useful, SQLAlchemy
provides various methods to modify the query object. Note that all of these methods
actually generate and return a new query object rather than modifying the existing query
object. The most useful of these methods are filter( ) and filter_by( ). These meth-
ods work, as their names imply, by restricting the set of objects returned from the query.
Note that we can use mapped properties just like column objects in SQL expressions.
SQLAlchemy also provides access to the c attribute (and all the attached columns) from
the mapper’s underlying selectable. In addition to this, SQLAlchemy provides a number
of methods on mapped properties to facilitate the construction of complex queries.
Some of these methods are summarized in the following lists.
The following are methods on mapped columns:
asc( self )
Return a clause representing the mapped column in ascending order.
between( self, cleft, cright )
Generate a BETWEEN clause with the specified left and right values (column BE-
TWEEN cleft AND cright).
concat( self, other )
Generate a clause that concatenates the value of the column with the value given.
desc( self )
Generate a clause representing the mapped column in ascending order.
distinct( self )
Generate a clause that limits the result set to rows with distinct values for this
column.
endswith( self, other )
Generate a clause (using LIKE) that implements the Python endswith( ) string
method.
in_( self, other )
Generate an IN clause with other as the righthand side. other may be either a
sequence of literal values or a selectable.
like( self, other )
Generate a LIKE clause with other as the righthand side.
startswith( self, other )
Generate a clause (using LIKE) that implements the Python startswith( ) string
method.
The following are methods on mapped relations:
Note that we now specify the filter criteria as keyword arguments to filter_by( ). The
query then searches for mapped properties with the given name and applies appropriate
filtering to the returned query.
The SQLAlchemy Query object also supports applying offsetting and limiting to a query
via the offset( ) and limit( ) methods, as well as the slicing operator:
In many cases, we want to retrieve only one object from the database. The Query object
provides three different ways to do this:
SQLAlchemy also allows you to join across multiple property “hops.” For instance, if
we wish to see all the products with some categorization under the “Class” level, we
could do the following:
Note that filter_by( ) used the Level’s name property, rather than the Category’s
name property, when performing the filter. SQLAlchemy keeps track of a “joinpoint,”
the last class referenced in an ORM join, and applies any filter_by( ) criteria to that
joinpoint until the joinpoint changes. In order to manually reset the joinpoint to the
“root” class, simply call the reset_joinpoint( ) method.
Any new join( ) calls will also reset the joinpoint to the root of the query. To disable
this behavior (and continue joining from the current joinpoint), simply specify
from_joinpoint=True in the call to join( ).
As you may have noticed, the join( ) method constructs inner joins. SQLAlchemy also
provides an outerjoin( ) method for constructing left outer joins. So, if we wanted to
get a list of all products that either have no “Class” categorization or have a “Class” of
“Pants,” we could execute the following query:
When constructing complex queries using joins, it is often useful to join to the same
table twice. In this case, we can specify that the join( ) method use an alias for the table
being joined:
>>> session.bind.echo=True
>>> query = session.query(Product)
>>> session.clear()
>>> for prod in query:
... print prod.sku, prod.categories
...
2007-11-16 17:30:08,356 INFO sqlalchemy.engine.base.Engine.0x..90
... SELECT product.sku AS product_sku, product.msrp AS product_msrp
FROM product ORDER BY product.oid
2007-11-16 17:30:08,357 INFO sqlalchemy.engine.base.Engine.0x..90 []
1232007-11-16 17:30:08,360 INFO sqlalchemy.engine.base.Engine.0x..90
... SELECT category.id AS category_id, category.level_id AS
... category_level_id, category.parent_id AS category_parent_id,
... category.name AS category_name
FROM category, product_category
WHERE ? = product_category.product_id AND category.id =
... product_category.category_id ORDER BY product_category.oid
2007-11-16 17:30:08,361 INFO sqlalchemy.engine.base.Engine.0x..90
... [u'123']
[]
4562007-11-16 17:30:08,364 INFO sqlalchemy.engine.base.Engine.0x..90
... SELECT category.id AS category_id, category.level_id AS
... category_level_id, category.parent_id AS category_parent_id,
... category.name AS category_name
FROM category, product_category
WHERE ? = product_category.product_id AND category.id =
... product_category.category_id ORDER BY product_category.oid
2007-11-16 17:30:08,365 INFO sqlalchemy.engine.base.Engine.0x..90
... [u'456']
[]
2222007-11-16 17:30:08,367 INFO sqlalchemy.engine.base.Engine.0x..90
... SELECT category.id AS category_id, category.level_id AS
... category_level_id, category.parent_id AS category_parent_id,
... category.name AS category_name
FROM category, product_category
WHERE ? = product_category.product_id AND category.id =
... product_category.category_id ORDER BY product_category.oid
If we eagerly load the categories property, however, we execute only a single query:
>>> session.clear()
>>> query = session.query(Product)
>>> query = query.options(eagerload_all('categories.level'))
>>> for prod in query:
... print prod.sku, prod.categories
...
2007-11-16 17:30:09,392 INFO sqlalchemy.engine.base.Engine.0x..90
... SELECT category_1.id AS category_1_id, category_1.level_id AS
... category_1_level_id, category_1.parent_id AS
... category_1_parent_id, category_1.name AS category_1_name,
... level_2.id AS level_2_id, level_2.parent_id AS
... level_2_parent_id, level_2.name AS level_2_name, product.sku AS
... product_sku, product.msrp AS product_msrp
FROM product LEFT OUTER JOIN product_category AS product_category_3
... ON product.sku = product_category_3.product_id LEFT OUTER JOIN
... category AS category_1 ON category_1.id =
... product_category_3.category_id LEFT OUTER JOIN level AS level_2
... ON level_2.id = category_1.level_id ORDER BY product.oid,
... product_category_3.oid, level_2.oid
2007-11-16 17:30:09,393 INFO sqlalchemy.engine.base.Engine.0x..90 []
123 []
456 []
222 [<Category Department.Tops>, <Category Class.Shirts>, <Category
... SubClass.T-Shirts>]
The options( ) method can also be used with a variety of other options. Notice how
the eager/lazy loading can also be specified on the mapper itself. From SQLAlchemy’s
If we wish to completely replace the SQL underlying the query object, we can do so
with the from_statement( ) method. When using from_statement( ), it’s important to
make certain that all the necessary columns are returned by the underlying query. If a
mapped column is omitted, then the mapped property will be set to None:
>>> session.clear()
>>> stmt = select([product_table.c.sku])
>>> query = session.query(Product).from_statement(stmt)
>>> for prod in query:
... print prod, prod.msrp
...
<Product 123> None
<Product 456> None
<Product 222> None
>>> session.clear()
>>> joined_product = product_table.outerjoin(product_category_table)
It is also possible to eagerly load where the LEFT OUTER JOIN is with an alias. In this
case, simply supply the alias (either as a string or as an Alias object) to the
contains_eager( ) alias parameter:
>>> session.clear()
>>> alias = category_table.alias('cat1')
>>> joined_product = product_table.outerjoin(product_category_table)
>>> joined_product = joined_product.outerjoin(alias)
>>> stmt = select([product_table, alias],
... from_obj=[joined_product])
>>> query = session.query(Product).from_statement(stmt)
>>> query = query.options(contains_eager('categories',
... alias='cat1'))
>>> session.bind.echo = True
>>> for prod in query:
... print prod, [c.name for c in prod.categories ]
...
2008-01-27 19:51:55,567 INFO sqlalchemy.engine.base.Engine.0x..90
... SELECT product.sku AS product_sku, product.msrp AS product_msrp,
... cat1.id AS cat1_id, cat1.level_id AS cat1_level_id,
... cat1.parent_id AS cat1_parent_id, cat1.name AS cat1_name
FROM product LEFT OUTER JOIN product_category ON product.sku =
... product_category.product_id LEFT OUTER JOIN category AS cat1 ON
... cat1.id = product_category.category_id
2008-01-27 19:51:55,567 INFO sqlalchemy.engine.base.Engine.0x..90 []
<Product 123> []
<Product 456> []
<Product 222> [u'Tops', u'Shirts', u'T-Shirts']
We can also use the from_statement( ) method with string-based queries. In this case,
it is a good idea to use bind parameters for performance and to avoid SQL injection
attacks. Bind parameters for SQLAlchemy are always specified using the :name notation,
and they are bound to particular values using the params( ) method of the Query object:
Up until now, we have been using the Query object to generate a sequence of mapped
objects. In some cases, we may want a query to retrieve several objects per “row,” where
the objects retrieved may either be fully mapped ORM objects or simple SQL columns.
SQLAlchemy supports this via the add_entity( ) and add_column( ) Query methods:
If you know a priori what objects you wish to construct, you can create the query
initially with this knowledge, rather than using the add_entity( ) method:
As mentioned earlier, the default context is the current thread. To override this and
supply a different context, simply pass a scopefunc parameter to the
scoped_session( ) function. scopefunc should be a callable that returns a key that
uniquely identifies the context. By default, the scopefunc is the get_ident( ) function
from the thread module.
The contextual Session class also supplies class methods for all the Session instance
methods. These class methods simply proxy to the contextual Session object. This
means that we can use scoped_session( ) to declare the contextual Session class glob-
ally and use it anywhere we would normally need a Session object, without explicitly
constructing the Session object. So, if we want to save a new Product to the contextual
Session object, we can simply save it to the (globally declared) contextual Session class:
In order to use contextual sessions effectively, they must be periodically “cleared out”
of the objects they manage, or else they will grow beyond all reasonable bounds. In the
context of a web framework, for instance, the contextual session should be cleared
between requests. This can be accomplished by using either the close( ) method, which
frees all resources maintained by the contextual session, or the remove( ) method, which
actually removes the session from the current context altogether. close( ) should be
used when the current context is “permanent,” as in web servers that use a never-
shrinking pool of threads to handle requests. remove( ) should be used if the context
may “go away,” since the session object will be “leaked” if the context is not reused.
This is the appropriate choice in web frameworks, which may stop threads that previ-
ously handled requests.
we can now declare them like this (assuming that Session has already been declared
globally as a contextual Session):
Session.mapper(Product, product_table, properties=dict(
categories=relation(Category, secondary=product_category_table,
backref='products')))
Once we have mapped the classes as shown, we can use the mapped classes themselves
to perform session-like functions:
>>> Product.query().all()
[<Product 123>, <Product 456>, <Product 222>, <Product 333>]
>>> prod = Product('444', msrp=55.66)
>>> Product.query().all()
[<Product 123>, <Product 456>, <Product 222>, <Product 333>,
... <Product 444>]
Using the contextual session mapper( ) method also gives us one other benefit: a rea-
sonably usable default constructor. This constructor allows us to provide values for
any of the properties defined in the mapped class via keyword arguments. So, if we
omitted the Product constructor and used Session.mapper( ) to map it, we could initi-
alize products as follows:
>>> p = Product(sku='555', msrp=22.11)
In this chapter, you will learn the different methods of mapping object-oriented inher-
itance to relational database tables. You will learn how to use different methods of
inheritance mapping with SQLAlchemy, as well as how to use inheritance in the pres-
ence of mapped relations between classes.
class Product(object):
def __init__(self, sku, msrp):
self.sku = sku
self.msrp = msrp
def __repr__(self):
return '<%s %s>' % (
self.__class__.__name__, self.sku)
class Clothing(Product):
def __init__(self, sku, msrp, clothing_info):
Product.__init__(self, sku, msrp)
self.clothing_info = clothing_info
class Accessory(Product):
def __init__(self, sku, msrp, accessory_info):
Product.__init__(self, sku, msrp)
self.accessory_info = accessory_info
157
Figure 8-1. Sample inheritance hierarchy
Notice that we have constructed a table that contains columns for all of the attributes
across the entire hierarchy we wish to model. This means that we incur some overhead
mapper(Clothing, inherits=Product,
polymorphic_identity='C')
mapper(Accessory, inherits=Product,
polymorphic_identity='A')
Aside from the space overhead, there is one problem in using single table inheritance
mapping: the mapper will try to map all the columns of the single table unless you
manually specify columns to map at each level of the inheritance hierarchy via the
include_columns or exclude_columns arguments to the mapper. For instance, if we try
to get the clothing_info for a non-clothing product, SQLAlchemy will not complain:
This problem is alleviated in the concrete table and joined table inheritance mappings,
which each use a different table for each class in the hierarchy.
clothing_table = Table(
'clothing', metadata,
Column('sku', String(20), primary_key=True),
Column('msrp', Numeric),
Column('clothing_info', String))
accessory_table = Table(
Note that in concrete table inheritance, each table contains exactly the amount of data
that is required to implement its class; there is no wasted space, unlike single table
inheritance. Also note that there is no longer a need for the “polymorphic identity”
column, as SQLAlchemy knows that Clothing objects are created from the cloth
ing_table, Accessory objects from the accessory_table, etc.
The mapper configuration is likewise straightforward:
mapper(Product, product_table)
mapper(Clothing, clothing_table)
mapper(Accessory, accessory_table)
Now we have a nicely labeled selectable that can be selected from, just as in the single
table inheritance. To complete the mapping, we need to let the mappers know about
the union and the inheritance relationship:
Here, we have specified a different table for selects (the polymorphic_union( ) result)
and let SQLAlchemy know to use concrete table inheritance in the child classes. Oth-
erwise, the mapper configuration is identical to the single table inheritance. Now,
assuming we have created the objects in the database as we did previously, we can
perform polymorphic loads as follows:
>>> session.query(Product).get('222')
2007-11-19 15:13:55,727 INFO sqlalchemy.engine.base.Engine.0x..50
... SELECT p_union.accessory_info AS p_union_accessory_info,
... p_union.type_ AS p_union_type_, p_union.sku AS p_union_sku,
... p_union.clothing_info AS p_union_clothing_info, p_union.msrp AS
... p_union_msrp
FROM (SELECT accessory.sku AS sku, CAST(NULL AS TEXT) AS
... clothing_info, accessory.msrp AS msrp, accessory.accessory_info
... AS accessory_info, 'A' AS type_
FROM accessory UNION ALL SELECT product.sku AS sku, CAST(NULL AS
... TEXT) AS clothing_info, product.msrp AS msrp, CAST(NULL AS TEXT)
... AS accessory_info, 'P' AS type_
FROM product UNION ALL SELECT clothing.sku AS sku,
... clothing.clothing_info AS clothing_info, clothing.msrp AS msrp,
... CAST(NULL AS TEXT) AS accessory_info, 'C' AS type_
FROM clothing) AS p_union
WHERE p_union.sku = ? ORDER BY p_union.oid
2007-11-19 15:13:55,737 INFO sqlalchemy.engine.base.Engine.0x..50
... ['222']
<Accessory 222>
clothing_table = Table(
'clothing', metadata,
Column('sku', None, ForeignKey('product.sku'),
primary_key=True),
Column('clothing_info', String))
accessory_table = Table(
'accessory', metadata,
Column('sku', None, ForeignKey('product.sku'),
primary_key=True),
Column('accessory_info', String))
As you can see, the various types of products are selected from their tables appropri-
ately. Note, however, that the single query( ) call yielded not one, but five SELECT
statements. This is due to the fact that SQLAlchemy must perform an auxiliary query
for each row that represents a child object. The next section shows how we can improve
performance in this situation.
Now, when we iterate over all the Products, we see that the auxiliary queries have been
eliminated:
>>> session.clear()
>>> session.query(Product).all()
2007-11-19 21:25:44,320 INFO sqlalchemy.engine.base.Engine.0x..d0
... SELECT product.sku AS product_sku, product.msrp AS product_msrp,
... product.product_type AS product_product_type
FROM product ORDER BY product.oid
2007-11-19 21:25:44,321 INFO sqlalchemy.engine.base.Engine.0x..d0 []
[<Product 123>, <Product 456>, <Clothing 789>, <Clothing 111>,
... <Accessory 222>, <Accessory 333>]
If we access one of the child attributes, then the secondary select executes to retrieve
that value:
>>> prod=session.get(Product, '789')
>>> print prod.clothing_info
2007-11-19 21:27:11,856 INFO sqlalchemy.engine.base.Engine.0x..d0
... SELECT clothing.sku AS clothing_sku, clothing.clothing_info AS
... clothing_clothing_info
FROM clothing
WHERE ? = clothing.sku
2007-11-19 21:27:11,856 INFO sqlalchemy.engine.base.Engine.0x..d0
... [u'789']
Nice Pants
mapper(
Product, product_table,
polymorphic_on=product_table.c.product_type,
polymorphic_identity='P',
select_table=pjoin)
Now, when we iterate over all Products, we have access to all attributes of all child
classes in a single query:
>>> session.clear()
>>> for prod in session.query(Product):
... if hasattr(prod, 'clothing_info'):
... print '%s : %s' % (prod, prod.clothing_info)
... elif hasattr(prod, 'accessory_info'):
... print '%s : %s' % (prod, prod.accessory_info)
... else:
... print prod
...
2007-11-19 21:35:11,193 INFO sqlalchemy.engine.base.Engine.0x..d0
... SELECT product.sku AS product_sku, clothing.sku AS clothing_sku,
... accessory.sku AS accessory_sku, product.msrp AS product_msrp,
... product.product_type AS product_product_type,
... clothing.clothing_info AS clothing_clothing_info,
... accessory.accessory_info AS accessory_accessory_info
FROM product LEFT OUTER JOIN clothing ON product.sku = clothing.sku
... LEFT OUTER JOIN accessory ON product.sku = accessory.sku ORDER
... BY product.oid
2007-11-19 21:35:11,194 INFO sqlalchemy.engine.base.Engine.0x..d0 []
<Product 123>
<Product 456>
<Clothing 789> : Nice Pants
<Clothing 111> : Nicer Pants
<Accessory 222> : Wallet
<Accessory 333> : Belt
inventory_table = Table(
'inventory', metadata,
Column('store_id', None, ForeignKey('store.id')),
Column('product_id', None, ForeignKey('product.sku')),
Column('quantity', Integer, default=0)
It is also possible to declare relations on a polymorphic class at any level of the inher-
itance hierarchy, and those relations will be inherited by the child classes. In the
previous example, for instance, the Clothing and Accessory classes inherit the back-
ref to their Inventory records.
In concrete table inheritance, mapping relations to a “parent class” is more difficult
because there is no unique table to join to. For instance, it is possible to implement
one-to-many and one-to-one joins where the polymorphic class has a foreign key into
another table. As an example, if we introduced a “vendor” table identifying the man-
ufacturer of all products, we could relate it to the Product hierarchy as follows:
vendor_table = Table(
'vendor', metadata,
Column('id', Integer, primary_key=True),
Column('name', String))
product_table = Table(
'product', metadata,
Column('sku', String(20), primary_key=True),
Column('msrp', Numeric),
Column('vendor_id', None, ForeignKey('vendor.id'))
clothing_table = Table(
'clothing', metadata,
accessory_table = Table(
'accessory', metadata,
Column('sku', String(20), primary_key=True),
Column('msrp', Numeric),
Column('vendor_id', None, ForeignKey('vendor.id'),
Column('accessory_info', String))
punion = polymorphic_union(
dict(P=product_table,
C=clothing_table,
A=accessory_table),
'type_')
mapper(
Product, product_table, select_table=punion,
polymorphic_on=punion.c.type_,
polymorphic_identity='P')
The main limitation with relations and concrete table inheritance mapping is that re-
lations from the polymorphic classes (rather than to them, as shown previously) are not
inherited and must therefore be configured individually for each mapper. This includes
all many-to-many relations, since the secondary join condition (and probably the sec-
ondary table as well) is different depending on which child class is being related to.
Nonpolymorphic Inheritance
All of the inheritance relationships shown so far were implemented using SQLAl-
chemy’s polymorphic loading. If polymorphic loading is not desired, either because of
its overhead or because you always know what types of classes you will be fetching, it
is possible to use nonpolymorphic loading by omitting all of the polymorphic_* param-
eters from the mappers.
Nonpolymorphic loading will always return the type of object being selected in the case
of a query (never the child class, as polymorphic loading does). Relations to nonpoly-
morphic classes also apply only to the actual class being mapped, not to its descendants.
This chapter describes Elixir, a module developed to automate some of the more com-
mon tasks in SQLAlchemy by providing a declarative layer atop “base” or “raw”
SQLAlchemy. This chapter also describes the various extensions to Elixir that provide
features such as encryption and versioning.
Introduction to Elixir
The Elixir module was developed as a declarative layer on top of SQLAlchemy, imple-
menting the “active record” pattern described in Chapter 6. Elixir goes out of its way
to make all of the power of SQLAlchemy available, while providing sensible default
behavior with significantly less code than “raw” SQLAlchemy. This chapter describes
versions 0.4 and 0.5 of Elixir, corresponding to the 0.4 version of SQLAlchemy. Dif-
ferences between versions 0.4 and 0.5 are discussed in ““Differences Between Elixir 0.4
and 0.5.”
So what exactly does Elixir do? Well, consider a simple product database. In SQLAl-
chemy, we might set up the products, stores, and prices with the following code:
product_table = Table(
'product', metadata,
Column('sku', String(20), primary_key=True),
Column('msrp', Numeric))
store_table = Table(
'store', metadata,
Column('id', Integer, primary_key=True),
Column('name', Unicode(255)))
product_price_table = Table(
'product_price', metadata,
Column('sku', None, ForeignKey('product.sku'), primary_key=True),
Column('store_id', None, ForeignKey('store.id'), primary_key=True),
Column('price', Numeric, default=0))
171
class Product(object):
def __init__(self, sku, msrp):
self.sku = sku
self.msrp = msrp
self.prices = []
def __repr__(self):
return '<Product %s>' % self.sku
class Store(object):
def __init__(self, name):
self.name = name
def __repr__(self):
return '<Store %s>' % self.name
class Price(object):
def __init__(self, product, store, price):
self.product = product
self.store = store
self.price = price
def __repr__(self):
return '<Price %s at %s for $%.2f>' % (
self.product.sku, self.store.name, self.price)
class Product(Entity):
sku=Field(String(20), primary_key=True)
msrp=Field(Numeric)
prices=OneToMany('Price')
def __repr__(self):
return '<Product %s>' % self.sku
class Store(Entity):
name=Field(Unicode(255))
prices=OneToMany('Price')
def __repr__(self):
return '<Store %s>' % self.name
class Price(Entity):
price=Field(Numeric, default=0)
product=ManyToOne('Product')
store=ManyToOne('Store')
def __repr__(self):
return '<Price %s at %s for $%.2f>' % (
self.product.sku, self.store.name, self.price)
In version 0.5, autosetup is not only not the default, but also “is not recommended”
according to the official Elixir documentation. So, using setup_all( ) is probably the
most “future-proof” way of defining your model.
There are several interesting things to notice in the Elixir listing. First, note that the
declaration of the tables has been moved inside the class definitions, and that the classes
are derived from Elixir’s Entity class. This is in keeping with Elixir’s “active record”
model, where the mapped classes are responsible for “remembering” the necessary data
for persisting themselves. Second, notice that we didn’t declare any primary keys for
the store or the price tables. If no primary key is declared, then Elixir will autogenerate
an integer primary key with a sequence providing default values. Third, notice that the
relationships were declared according to their behavior in the ORM (OneToMany, Many
ToOne), and that no foreign key information was included in the model. Elixir will, based
on the types of relationships declared, automatically generate foreign key columns as
well as any auxiliary tables required for ManyToMany joins.
Using Elixir
Elixir has two syntaxes for defining your classes: an attribute-based syntax (shown
previously) and a “domain specific language” (DSL) syntax. Both have similar power;
which one you use is mostly a matter of personal style. The DSL-based syntax may be
phased out in the future, as it is no longer the “default” syntax, but it is not currently
deprecated, so it is covered in this chapter. If we were to define the product database
using the DSL syntax, for instance, we would write the following (with the methods
for each class omitted for clarity):
from elixir import *
metadata.bind = 'sqlite://'
class Product(Entity):
has_field('sku', String(20), primary_key=True)
has_field('msrp', Numeric)
has_many('prices', of_kind='Price')
class Store(Entity):
has_field('name', Unicode(255))
has_many('prices', of_kind='Price')
class Price(Entity):
has_field('price', Numeric, default=0)
belongs_to('product', of_kind='Product')
belongs_to('store', of_kind='Store')
In all of the following examples, we will show the attribute-based syntax first, followed
by the DSL syntax.
Using this definition of the Price entity and the definitions of Product and Store used
previously (all saved in a module named model.py), let’s import the model, create the
database, and see what Elixir does in the background:
>>> from elixir import *
>>> from model import *
>>>
>>> create_all()
>>>
>>> stores = [ Store('Main Store'),
... Store('Secondary Store') ]
>>> products = [
... Product('123', 11.22),
... Product('456', 33.44),
... Product('789', 123.45) ]
>>> prices = [ Price(product=product, store=store, price=10.00)
... for product in products
... for store in stores ]
>>>
>>> session.flush()
This will create all the tables used to implement the entities defined up to this point.
Two things are important to note here. The first is that our has_field( ) statement did
indeed create a “proxy” statement to the Store entity’s name field. The second is Elixir’s
naming convention. By default, tables created to implement entities have names gen-
erated by combining the module name with the entity name.
Relations
Relations with Elixir are extremely similar to relations using “bare” SQLAlchemy, ex-
cept that in Elixir, relations are defined by their cardinality (one to many, one to one,
etc.) rather than inferred by foreign key relationships. In fact, Elixir will automatically
create the foreign key columns necessary to implement the relations as defined.
Attribute-based syntax
In the attribute-based syntax, relationships are declared via the ManyToOne, OneToMany,
OneToOne, and ManyToMany classes. Each of these class constructors takes one required
argument, a string specifying the name of the class being related to. Each also supports
some arguments unique to Elixir and pass any unrecognized arguments along to the
underlying SQLAlchemy relation( ) function. Note that the OneToMany and OneToOne
relationships require a corresponding ManyToOne relationship in order to set up the for-
eign key column used to relate the classes.
DSL syntax
In the DSL syntax, relationships are declared via the belongs_to( ), has_many( ),
has_one( ), and has_and_belongs_to_many( ) statements. Each of these functions takes
two required arguments. The first is the name of the relation being defined. (This will
be the name of the attribute in the mapped class.) The second argument, which must
be declared using the of_kind keyword argument, is the name of the Entity being related
to.
Like the has_field( ) statement, all the DSL relation statements take the optional pa-
rameters through and via in order to proxy attributes of the related class(es) to the
mapped class. See the earlier section “Fields and Properties” for more information on
these parameters.
Inheritance
Inheritance in Elixir is handled via either the single table inheritance mapping or the
joined table inheritance mapping supported by SQLAlchemy (and described in detail
in Chapter 8). Elixir also supports specifying whether polymorphic or nonpolymorphic
loading should be used with the mapped classes. Both the inheritance method (joined
table or single table) and whether the loader should be polymorphic are specified via
the DSL statement using_options( ). There is currently no corresponding attribute-
based syntax for specifying options on entities. So, to create the Product, Clothing, and
Accessory hierarchy described in Chapter 8 in Elixir as a joined table (“multiple”) and
polymorphic hierarchy, we would write the following (with methods omitted for clari-
ty):
class Product(Entity):
using_options(inheritance='multi', polymorphic=True)
sku=Field(String(20), primary_key=True)
msrp=Field(Numeric)
class Clothing(Product):
using_options(inheritance='multi', polymorphic=True)
clothing_info=Field(String)
class Accessory(Product):
using_options(inheritance='multi', polymorphic=True)
accessory_info=Field(String)
>>> Product.get('123')
<Product 123>
Elixir also adds the get_by( ) method for retrieving a single instance based on nonpri-
mary key columns. (The corresponding query in SQLAlchemy is a filter_by( ) fol-
lowed by one( ).)
>>> Product.get_by(msrp=33.44)
<Product 456>
Of course, you can always access the underlying Session query via the query attribute:
>>> Product.query.all()
[<Product 123>, <Product 456>, <Product 789>]
The complete set of (nondeprecated) methods on the Entity class is described in the
following list. Each of these methods is a proxy for the corresponding Session methods,
covered in Chapter 7, and any arguments provided to these methods are passed along
unmodified to the underlying Session methods:
flush( self, *args, **kwargs )
Flush the changes to this instance to the database.
delete( self, *args, **kwargs )
Mark this instance for deletion from the database.
expire( self, *args, **kwargs )
Expire this instance from the Session.
refresh( self, *args, **kwargs )
Reload this instance from the database, overwriting any in-memory changes.
expunge( self, *args, **kwargs )
Expunge this instance from the Session.
merge( self, *args, **kwargs )
Merge the instance with the instance in the Session.
Elixir Extensions
In addition to its base functionality, Elixir provides a number of extensions that allow
for more advanced uses.
Associable Extension
In many database schemas, there may be one table that relates to many others via a
many-to-many or a many-to-one join. The elixir.ext.associable extension provides a
convenient way to specify this pattern and to generate the appropriate association ta-
bles. This is accomplished by the associable( ) function, which returns a DSL state-
ment that can be used in the definition of the related entities.
For instance, suppose we have a schema that represents brands and retailers, each of
which may have multiple addresses stored in the database. This can be accomplished
as follows:
class Address(Entity):
has_field('street', Unicode(255))
has_field('city', Unicode(255))
has_field('postal_code', Unicode(10))
class Brand(Entity):
has_field('name', Unicode(255)),
has_field('description', Unicode)
is_addressable()
class Retailer(Entity):
has_field('name', Unicode(255)),
has_field('description', Unicode)
is_addressable()
Encrypted Extension
The elixir.ext.encrypted extension provides encrypted field support for Elixir using the
Blowfish algorithm from the PyCrypto library, which must be installed separately. (Py-
Crypto is available from the Python Package Index via “easy_install pycrypto”.) The
encrypted extension provides the DSL statement acts_as_encrypted( ), which takes the
following parameters:
for_fields=[]
List of field names for which encryption will be enabled
with_secret='abcdef'
A secret key used to perform encryption on the listed fields
The encrypted extension is particularly useful when data must be stored on an untrus-
ted database or as part of a defense-in-depth approach to security. For instance, you
might encrypt passwords that are stored in the database. Keep in mind, however, that
the source code of your application must be kept in a trusted location since it specifies
the encryption key used to store the encrypted columns.
Versioned Extension
The elixir.ext.versioned extension provides a history and versioning for the fields in an
entity. These services are provided by the acts_as_versioned( ) DSL statement. Mark-
ing an entity as versioned will apply the following operations:
• A timestamp column and a version column will be added to the versioned entity
table.
• A history table will be created with the same columns as the primary entity table,
including the added timestamp and version columns.
• A versions attribute will be added to the versioned entity that represents a OneTo
Many join to the history table.
• The instance methods revert( ), revert_to( ), compare_with( ), and get_as_of( )
will be added to the versioned entity.
Whenever changes are made to a versioned entity, the version column is incremented
and the previous values for all the columns are saved to the history table. Note that at
the current time, relationships are not handled automatically by the versioning process
(relationship changes are not tracked in the history table) and must be handled man-
ually. The size of the history table can be managed by specifying fields not to include
via the ignore option to acts_as_versioned( ).
class Product(Entity):
has_field('sku', String(20), primary_key=True)
has_field('msrp', Numeric)
acts_as_versioned()
def __repr__(self):
return '<Product %s, mrsp %s>' % (self.sku, self.msrp)
@after_revert
def price_rollback(self):
print "Rolling back prices to %s" % self.msrp
The behaviors of the new methods added by acts_as_versioned( ) are listed here:
Introduction to SqlSoup
If Elixir is ideally suited for blue sky, legacy-free development, SqlSoup is ideally suited
for connecting to legacy databases. In fact, SqlSoup provides no method of defining a
database schema through tables, classes, and mappers; it uses extensive autoloading
to build the SQLAlchemy constructs (Tables, classes, and mapper( )s) automatically
from an existing database.
To illustrate the uses of SQLAlchemy in this chapter, we will use the following SQLAl-
chemy-created schema. Note that, unlike in previous chapters, we will be saving the
test database in an on-disk SQLite database rather than using an in-memory database,
to illustrate the fact that SqlSoup relies entirely on auto loading.
from sqlalchemy import *
engine = create_engine('sqlite:///chapter10.db')
metadata = MetaData(engine)
product_table = Table(
'product', metadata,
Column('sku', String(20), primary_key=True),
Column('msrp', Numeric))
store_table = Table(
'store', metadata,
Column('id', Integer, primary_key=True),
189
Column('name', Unicode(255)))
product_price_table = Table(
'product_price', metadata,
Column('sku', None, ForeignKey('product.sku'), primary_key=True),
Column('store_id', None, ForeignKey('store.id'), primary_key=True),
Column('price', Numeric, default=0))
metadata.create_all()
stmt = product_table.insert()
stmt.execute([dict(sku="123", msrp=12.34),
dict(sku="456", msrp=22.12),
dict(sku="789", msrp=41.44)])
stmt = store_table.insert()
stmt.execute([dict(name="Main Store"),
dict(name="Secondary Store")])
stmt = product_price_table.insert()
stmt.execute([dict(store_id=1, sku="123"),
dict(store_id=1, sku="456"),
dict(store_id=1, sku="789"),
dict(store_id=2, sku="123"),
dict(store_id=2, sku="456"),
dict(store_id=2, sku="789")])
In order to use SqlSoup, we must first create an instance of the SqlSoup class. This
instance must be created either with an existing MetaData instance as its first argument,
or with the same arguments as SQLAlchemy’s MetaData class. In our case, we will pass
in a database URI to use in autoloading the tables:
>>> from sqlalchemy.ext.sqlsoup import SqlSoup
>>> db = SqlSoup('sqlite:///chapter10.db')
If we wish to restrict the set of tables loaded to a particular schema (in databases that
support this), we can specify it by setting the as db.schema attribute. Since we’re using
SQLite, there is no need to specify a schema.
In order to access the tables we’ve defined in the database, simply use attribute access
from the SqlSoup instance we’ve created:
>>> print db.product.all()
[MappedProduct(sku='123',msrp=Decimal("12.34")),
... MappedProduct(sku='456',msrp=Decimal("22.12")),
... MappedProduct(sku='789',msrp=Decimal("41.44"))]
Note that there was no mapper or table setup required to retrieve the objects (other
than when we first created the database!). The following sections describe in more detail
how you can use SqlSoup.
You may have noticed in the previous example that we accessed the session-like meth-
ods flush( ) and clear( ) on the SqlSoup instance. SqlSoup strives to provide a rich set
of functionality with a limited set of interfaces, namely the SqlSoup instance and auto
mapped classes. As such, the SqlSoup instance provides several session-like functions
as well as providing access to the auto mapped classes:
bind(attribute)
The underlying Engine or Connectable for this SqlSoup instance.
schema(attribute)
Use the specified schema name for auto loading and auto mapping tables.
clear( self )
Call the underlying contextual session’s clear( ) method.
delete( self, *args, **kwargs )
Call the underlying contextual session’s delete( ) method with the specified argu-
ments.
flush( self )
Call the underlying contextual session’s flush( ) method.
join( self, *args, *kwargs )
Call SQLAlchemy’s join( ) function with the specified arguments and return an
auto mapped object corresponding to rows of the generated join.
map( self, selectable, *kwargs )
Auto map an arbitrary selectable, returning the generated mapped class.
In order to chain the join object to other tables, just use the join( ) method again:
>>> join2 = db.join(join1, db.store, isouter=True)
>>> join2.all()
[MappedJoin(sku='123',msrp=Decimal("12.34"),store_id=1,
... price=Decimal("0"),id=1,name='Main Store'),
... MappedJoin(sku='123',msrp=Decimal("12.34"),store_id=2,
... price=Decimal("0"),id=2,name='Secondary Store'),
... MappedJoin(sku='456',msrp=Decimal("22.12"),store_id=1,
... price=Decimal("0"),id=1,name='Main Store'),
... MappedJoin(sku='456',msrp=Decimal("22.12"),store_id=2,
... price=Decimal("0"),id=2,name='Secondary Store'),
... MappedJoin(sku='789',msrp=Decimal("41.44"),store_id=1,
... price=Decimal("0"),id=1,name='Main Store'),
... MappedJoin(sku='789',msrp=Decimal("41.44"),store_id=2,
... price=Decimal("0"),id=2,name='Secondary Store'),
... MappedJoin(sku='111',msrp=Decimal("22.44"),store_id=None,price=None,
... id=None,name=None)]
In some cases, it’s nice to label the columns according to their table of origin. To ac-
complish this, use the with_labels( ) SqlSoup method:
>>> join3 = db.with_labels(join1)
>>> join3.first()
MappedJoin(product_sku='123',product_msrp=Decimal("12.34"),
... product_price_sku='123',product_price_store_id=1,
... product_price_price=Decimal("0"))
>>> db.with_labels(join2).first()
MappedJoin(product_sku='123',product_msrp=Decimal("12.34"),
... product_price_sku='123',product_price_store_id=1,
... product_price_price=Decimal("0"),store_id=1,store_name='Main
... Store')
It is also possible to label a mapped table and then use the labeled table in joins:
Note that the columns from db.product are labeled, whereas the columns from db.prod
uct_price are not.
A common usage pattern is to add such mapped selectables to the SqlSoup instance for
access in other parts of the application:
>>> db.products_with_avg_price = products_with_avg_price
There’s no magic here; this is just Python’s ability to declare new, ad-hoc attributes on
existing objects. Do note that if you happen to add an attribute with the same name as
a table in your database, SqlSoup will not be able to access that table until you remove
the new attribute.
We can similarly use the insert( ) and delete( ) method to perform SQL-level inserts
and deletes.
Association Proxy
The association proxy extension allows our mapped classes to have attributes that are
proxied from related objects. One place where this is useful is when we have two tables
related via an association table that contains extra information in addition to linking
the two tables. For instance, suppose we have a database containing the following
schema:
user_table = Table(
'user', metadata,
Column('id', Integer, primary_key=True),
Column('user_name', String(255), unique=True),
Column('password', String(255)))
brand_table = Table(
'brand', metadata,
Column('id', Integer, primary_key=True),
Column('name', String(255)))
sales_rep_table = Table(
199
'sales_rep', metadata,
Column('brand_id', None, ForeignKey('brand.id'), primary_key=True),
Column('user_id', None, ForeignKey('user.id'), primary_key=True),
Column('commission_pct', Integer, default=0))
In this case, we might want to create User, Brand, and SalesRep classes to represent our
domain objects. The basic mapper setup would then be the following:
class User(object): pass
class Brand(object): pass
class SalesRep(object): pass
In such a case, we have completely mapped the data in our schema to the object model.
But what if we want to have a property on the Brand object that lists all of the Users
who are SalesReps for that Brand? One way we could do this in “base” SQLAlchemy is
by using a property in the Brand class:
class Brand(object):
@property
def users(self):
return [ sr.user for sr in self.sales_reps ]
This is not very convenient, however. It doesn’t allow us to append to or remove from
the list of users, for instance. The association proxy provides a convenient solution to
this problem. Using the association_proxy( ) function, we can add the users property
much more simply:
from sqlalchemy.ext.associationproxy import association_proxy
class Brand(object):
users=association_proxy('sales_reps', 'user')
If we want to keep our domain object definition code ignorant of SQLAlchemy, we can
even move the association_proxy( ) call outside our class into the mapper configura-
tion:
mapper(Brand, brand_table, properties=dict(
sales_reps=relation(SalesRep, backref='brand')))
Brand.users=association_proxy('sales_reps', 'user')
We can even append onto the users attribute to add new SalesReps. To enable this
functionality, however, we need to create some sensible constructors for our mapped
objects:
class User(object):
def __init__(self, user_name=None, password=None):
self.user_name=user_name
self.password=password
class SalesRep(object):
def __init__(self, user=None, brand=None, commission_pct=0):
self.user = user
self.brand = brand
self.commission_pct=commission_pct
Now, we can populate the database and add a user as a sales rep to a brand:
This works because the association proxy extension will automatically create the in-
termediary SalesRep object by calling its constructor with a single positional argument,
the User. To override this creation behavior, you can supply a creation function in the
creator parameter. For instance, if we wanted to give sales reps added in this manner
a commission percentage of 10%, we could define the proxy as follows:
Brand.users=association_proxy(
'sales_reps', 'user',
creator=lambda u:SalesRep(user=u, commission_pct=10))
Although accessing the underlying user attribute of the sales_reps property is useful,
what if we prefer dictionary-style access? associationproxy supports this as well. For
instance, suppose we want a property on Brand that is a dictionary keyed by User con-
taining the commission_pct values. We can implement this as follows. (Note that
dictionary-style association proxy creation functions take two positional parameters:
the key and value being set.)
from sqlalchemy.orm.collections import attribute_mapped_collection
reps_by_user_class=attribute_mapped_collection('user')
>>> session.clear()
>>> session.bind.echo = False
>>>
>>> b = session.get(Brand, 1)
>>> u = session.get(User, 1)
>>> b.commissions[u] = 20
>>> session.flush()
>>> session.clear()
>>>
>>> b = session.get(Brand, 1)
>>> u = session.get(User, 1)
>>> print u.user_name
Note that the proxy and the original relation are automatically kept synchronized by
SQLAlchemy:
Ordering List
A common pattern in many applications is the use of ordered collections. For instance,
consider a simple to-do list application with multiple lists, each containing an (ordered)
set of items. We might start with the following schema:
todo_list_table = Table(
'todo_list', metadata,
Column('name', Unicode(255), primary_key=True))
todo_item_table = Table(
'todo_item', metadata,
Column('id', Integer, primary_key=True),
Column('list_name', None, ForeignKey('todo_list.name')),
Column('list_position', Integer),
Column('value', Unicode))
SQLAlchemy provides nice support for mapping the list items to a property and sorting
them via the order_by parameter:
class TodoList(object):
def __init__(self, name):
self.name = name
def __repr__(self):
return '<TodoList %s>' % self.name
class TodoItem(object):
def __init__(self, value, position=None):
self.value = value
self.list_position = position
def __repr__(self):
return '<%s: %s>' % (self.list_position, self.value)
This approach is certainly workable, but it requires you to manually keep track of the
positions of all the list items. For instance, suppose we wanted to mow the lawn be-
tween buying groceries and doing laundry. To do this using base SQLAlchemy, we
would need to do something like the following:
>>> lst.items.insert(1, TodoItem('Mow lawn'))
>>> for pos, it in enumerate(lst.items):
... it.list_position = pos
Rather than “fixing up” the list after each insert or remove operation, we can instead
use orderinglist to keep track of the list_position attribute automatically:
We can also customize the ordering_list( ) call either by providing a count_from ar-
gument (to use nonzero-based lists) or by providing an ordering_func argument that
maps a position in a list to a value to store in the ordering attribute.
In some cases, you may also want to rearrange the entire list (rather than applying
individual insertions and deletions). For such situations, ordering_list( ) provides the
_reorder( ) method, which will generate new position values for every element in the
list.
We’d like to hear your suggestions for improving our indexes. Send email to [email protected].
207
autoflush feature, flushing sessions, 94 .c objects, 15
autoflush( ) method, 150 cascade parameter, 115
autoincrement argument (Column CheckConstraint, 47
constructor), 44 checkfirst parameter (MetaData), 56
autoload parameter, 13 CheeseShop, 21
Table object, 41 ClauseElement objects, 76
autoload_with argument (Table), 41 clear( ) method, 191
AVG( ) function, 150 clear_mappers( ) function, 98
avg( ) method, 150 close( ) method, 75, 138
ResultProxy object, 37
B collection_class parameter, 115, 117
colname Elixir keyword argument, 175
backref parameter, 115
colname parameter (ManyToOne), 179
backref( ) function, 113, 114
Column class, 40
parameters, 115
definitions, 43
backrefs, 111
column parameter (ForeignKey constructor),
batch parameter (mapper( ) function), 106
45
before_commit( ) (SessionExtension class),
column( ) method, 85
138
columns parameter (ForeignKeyConstraint
before_delete( ) hook (MapperExtension), 121
class), 46
before_flush( ) (SessionExtension class), 138
columns=None parameter (select( )), 73
before_insert( ) hook (MapperExtension), 121
column_kwargs parameter (ManyToOne),
before_update( ) hook (MapperExtension),
179
121
column_mapped_collections( ) method, 120
begin( ) method, 136
column_prefix keyword, 100
belongs_to( ) statement, 180
column_prefix parameter (mapper( ) function),
between( ) method, 140
106
between( ) method (ClauseElement), 78
column_property( ) function, 101, 177
bind argument
commit( ) method, 136
sessionmaker( ) method, 128
compare_with( ) method, 188
bind argument (MetaData.reflect( ) method),
comparison operators, 76
43
__composite_values__( ) method, 102
bind parameters, 40, 76
concat( ) method, 140
custom, using, 78
concrete parameter (mapper( ) function), 106
MetaData methods and, 56
concrete table inheritance, 157
select( ), 73
concrete table inheritance mapping, 161–163
bind( ) method, 191
configure( ) method (Session), 127
bind=None parameter (text( ) function), 80
connect( ) method, 11, 36, 38
bindparam( ) method, 79, 80
Connection object, 36
binds argument (sessionmaker( ) method),
connection pools, 11, 33, 37–39
128
Engine class and, 33
boolean operators, 77
connect_args parameter (create_engine( )
bound MetaData, 55
function), 34
bounded constructors, 39
Constraint class, 44–48
built-in type TypeEngine objects, 59
constraint parameter (ForeignKey constructor),
Byer, Mike, 1
45
constraint_kwargs parameter (ManyToOne),
C 179
c attribute (mapped class), 9 contains( ) method, 141
208 | Index
contains_alias( ) method, 146 ClauseElement object, 78
contains_eager( ) method, 146, 147 distinct=False parameter (select( )), 73
contextual sessions, 153 DML (data manipulation language), 67
convert_bind_param( ) method, 63 domain specific language (DSL) syntax, 174,
convert_result_value( ), 63 180
convert_unicode parameter (create_engine( ) drivers, installing, 23
function), 34 drop_all( ) method, 56
correlate( ) method, 86, 90 DSL (domain specific language) syntax, 174,
correlate=True parameter (select( )), 74 180
COUNT( ) function, 151 dynamic_loader( ) method, 134
count( ) method, 85, 151
CREATE TABLE statement, 45
create( ) method, 41, 59
E
eagerload( ) method, 146
MetaData/schema objects and, 56
eagerload_all( ) method, 144, 146
create_all( ) method, 56, 59
Easyinstall, 21
create_engine( ) function, 10, 33
easy_install tools, 21
configuring logging and, 35
echo argument
create_instance( ) hook (MapperExtension),
configuring logging and, 35
121
echo argument (create_engine( ) function), 34
creator parameter (create_engine( ) function),
echo_pool argument
34
configuring logging and, 35
echo_pool argument (create_engine( )
D function), 34
data manipulation language (DML), 67 echo_uow argument (sessionmaker( ) method),
data mapper pattern, 93 128
database drivers, installing, 23 echo_uow flag (create_engine( ) function), 35
datetime.now method, 49 EGG files, 21
DB-API interface, 1 Elixir, 171–188
DB2 database, 1 extensions, 184–188
decorators, 119 inheritance, 181
default argument (Column constructor), 44 installing/using, 174
insert defaults and, 49 querying using, 183
defaults, 48–52 relations, 178
defer( ) function, 105 elixir.ext.encrypted extension, 186
defer( ) method, 146 elixir.ext.versioned extension, 186
deferred Elixir keyword argument, 175 __emulates__ class attribute, 118
deferred( ) function, 104 encoding parameter (create_engine( )
delete parameter, 114 function), 34
DELETE statements, 71 endswitch( ) method, 140
delete( ) function, 28 endswith( ) method (ClauseElement), 78
SqlSoup, using, 191 Engine class, 10–12, 16, 33–57
delete( ) method, 71, 136, 183, 191 connection pools, 11
sessions, deleting objects from, 135 managing connections, 33–39
delete-orphan, 115 Engine pools
desc( ) method, 140 connection pools, 37
Detached state (Session), 130 entities, 4
dialect management (SQL), 12 entity_name parameter (mapper( ) function),
dialect-specific types, 61 106
distinct( ) method, 81, 85, 140, 151 __eq__( ) method, 102
Index | 209
EXCEPT clause, 88 for_update parameter (Sequence constructor),
except_( ) function, 88 55
except_( ) method, 86 from_obj=[ ] parameter (select( )), 73, 90
except_all( ) function, 88 from_statement( ) method, 147, 151
except_all( ) method, 86
exclude_properties, 99
exclude_properties parameter (mapper( )
G
"generative" interface, 82
function), 107
get( ) (Query object), 142
execute( ) method, 37, 68, 138
get( ) hook (MapperExtension), 121
executemany( ) method, 69
get( ) method, 29, 137, 151, 184
expire( ) method, 137, 183
Elixir, querying using and, 183
explicit execution, 40
__getattr__( ) method, 192
explicit sequences, creating, 54
__get attr__( ) method (ResultProxy), 37
expression language (SQL), 14–16
__getitem__( ) method, 153
expression-builders (Pythonic), 2
get_as_of( ) method, 188
expunge parameter, 115
get_by( ) method, 183, 184
expunge( ) method, 137, 183
get_indent( ) function, 154
extending session, 138
get_session( ) hook (MapperExtension), 121
extension argument (sessionmaker( ) method),
GROUP BY clause, 80
129
group_by( ) method, 85, 151
extension parameter (mapper( ) function), 106,
group_by=Name parameter (select( )), 73
121
extension( ) method, 146
ex_setup.py file, 22 H
hand-generated SQL vs. SQLAlchemy
F generation layers, 15
has( ) method, 141
fetchall( ) method, 74
has_and_belongs_to_many( ) statement, 180
fetchall( ) method (ResultProxy), 37
has_field( ) method, 175
fetchmany( ) method, 74
has_field( ) statement, 180
fetchone( ) method, 74
has_many( ) statement, 180
fetchone( ) method (ResultProxy), 37
has_one( ) statement, 180
Field( ) class, 175
HAVING clause, 80, 151
filter( ) method, 29, 139, 151
having( ) method, 85, 151
querying with joins, 142
having=None parameter (select( )), 73
filter_by( ) method, 18, 29, 139, 151, 183
horizontal partitioning, 124
querying with joins, 142
Firebird database, 24
first( ) (Query object), 142 I
first( ) method, 151 identity_map attribute, 138
flush( ) function, 28 idiomatically Python (Pythonic), 1
saving objects to sessions and, 129 IF EXISTS clause, 56
flush( ) method, 136, 183, 191 IF NOT EXISTS clause, 56
for+update=False parameter (select( )), 74 "impedance mismatch" (object/relational), 4
foreign keys, 4 implicit execution, 40
ForeignKey constructor, 45 IN clause, 91
ForeignKeyConstraint class, 46 include_columns argument (Table), 42
foreign_keys parameter, 115 include_properties, 99
for_fields parameter, 186 include_properties parameter (mapper( )
function), 107
210 | Index
increment parameter (Sequence constructor),
55
K
index argument (Column constructor), 44 key argument (Column constructor), 43
Index object, 53 key method (ResultProxy), 37
indexes, 52 keys( ) method (ResultProxy), 37
Informix database, 24
ingerits parameter (mapper( ) function), 106 L
inheritance mapping, 157–169 label( ) method, 86
concrete table, 161–163 label( ) method (ClauseElement), 78
joined table, 163–168 lazy parameter, 116, 185
inherit_foreign_keys parameter (mapper( ) lazyload( ) method, 146
function), 106 LEFT OUTER JOIN, 147
inhert_condition parameter (mapper( ) library logging, 35
function), 106 like( ) method, 140
init_failed( ) hook (MapperExtension), 122 like( ) method (ClauseElement), 78
init_instance( ) hook (MapperExtension), 122 LIMIT clause, 82
injection attacks (SQL), 2 LIMIT modifier, 152
insert defaults, 48 limit( ) method, 85, 152
INSERT statements, 26, 68 limit=None parameter (select( )), 74
insert( ) function, 68 literal text in queries, 79
SqlSoup, using, 191 load( ) hook (MapperExtension), 121
installation (SQLAlchemy), 21–24 load( ) method, 137, 152
instances( ) method, 151 local_side parameter (ManyToMany), 180
instrument_class( ) hook (MapperExtension),
122
internally_instrumented( ) decorator, 119
M
INTERSECT clause, 88 M:N relationships, 108–110
intersect( ) function, 88 backrefs and, 112
intersect( ) method, 85 many-to-many (M:N) relationships, 109
intersect_all( ) method, 86 backrefs, 112
inverse parameter, 179, 180 ManyToMany object, 180
inverse parameter (OneToMany), 179 ManyToOne object, 178
in_( ) method, 140 map( ) method, 191
in_( ) method (ClauseElement), 78, 90 mapped classes, 9
items( ) method (ResultProxy), 37 MappedProduct class, 191
iterator( ) decorator, 119 mapped_collection( ) method, 120
__iter__( ) method, 74, 153 mapper patterns, 93
__iter__( ) method (ResultProxy), 37 mapper( ) function, 98, 142
extending mappers and, 121
inheritance hierarchy and, 159
J parameters, 105
join( ) function, 19 MapperExtension class, 120, 138
join( ) method, 86, 151, 191 MAX( ) function, 150
SqlSoup and, 192 max( ) method, 152
joined table inheritance, 157 max_overflow parameter (create_engine( )
joined table inheritance mapping, 163–168 function), 35
join_depth parameter, 116 merge parameter, 115
merge( ) method, 137, 183
metadata argument
Table constructor, 41
Index | 211
MetaData class, 10, 12, 33, 39–57 ForeignKey constructor, 46
connecting to databases/tables, 25 ondelete parameter (ManyToOne), 179
object mappers, declaring, 95 one( ) (Query object), 142
tables, defining, 40–43 one( ) method, 152
TypeEngine objects and, 59 OneToMany object, 179
Metadata class OneToOne object, 179
ORM and, 16 only argument (MetaData.reflect( ) method),
metadata.bind.echo property, 26 43
MIN( ) function, 150 onupdate argument
min( ) method, 152 Column constructor, 44
module parameter (create_engine( ) function), onupdate parameter
34 ForeignKey constructor, 46
mustexist argument (Table), 42 onupdate parameter (ManyToOne), 179
MySQL, 1 on_link( ) decorator, 119
drivers, installing, 24 op( ) method (ClauseElement), 78
option( ) method, 121
N optional parameter (Sequence constructor), 55
options( ) method, 144, 145, 152
name argument
OR boolean operator, 77
Column constructor, 43
Oracle, 1
Table constructor, 41
drivers, installing, 24
name parameter
ORDER BY clause, 80
ForeignKey constructor, 46
ordered collections, 203
Sequence constructor, 54
ordering_list( ) method, 204
name parameter (Constraint class), 44
order_by parameter, 116
"named colon" format, 80
mapper( ) function, 107
nested loops, 6
order_by parameter (ManyToMany), 180
__ne__( ) method, 102
order_by parameter (OneToMany), 179
noload( ) method, 146
order_by( ) method, 84, 152
nonpolymorphic inheritance, 169
order_by=None parameter (select( )), 73
non_primary parameter (mapper( ) function),
ORM (object-relation mapper)
107
extending mappers, 120–122
NOT boolean operator, 77
ORM (object-relational mapper), 1, 3, 16–19
nullable argument (Column constructor), 44
(see also mapper)
NullPool pool type (sqlalchemy.pool), 38
declaring, 95–108
design concepts in, 93
O MetaData object and, 55
object-relational mapper (see ORM) partitioning strategies, 122
object/relational "impedance mismatch", 4–7 property mapping, customizing, 99
objects querying, 139–153
tables, mapping to, 28 querying and updating, 127–155
OFFSET clause, 82 relationships, declaring, 108–120
offset( ) method, 85, 152 self-referential mappers, using, 113
offset=None parameter (select( )), 74 ORM object-relational mapper, 93–126
"oid" (object ID), 35 outerjoin( ) method, 86, 152
ON clause, 87 owner argument (Table), 42
ON DELETE CASCADE statement, 114
ON UPDATE clause, 46
ondelete parameter, 114
P
params( ) method, 153
212 | Index
passive defaults, 50 primary_key argument (Column constructor),
PassiveDefault instances, 50 43
passive_deletes parameter, 116 primary_key parameter (mapper( ) function),
Pending objects, 134 107
Pending state (Session), 130 properties parameter (mapper( ) function),
Persistent object, 134 107
Persistent state (Session), 130 property mapping, 99
PIL (Python Imaging Library), 14 PyPI (Python package index), 21
"plain old Python objects" (see POPOs) pysqlite binary module, 23
plural_name argument (associable( ) function), pysqlite driver, 24
185 Python Imaging Library (PIL), 14
polymorphic Pythonic (idiomatically Python), 1
class, 168
polymorphic identity of rows, 159
polymorphic_fetch parameter (mapper( )
Q
queries, 26, 67
function), 107
constructing, 72–86
polymorphic_identity argument, 159
query( ) method, 137, 139
polymorphic_identity parameter (mapper( )
query_from_parent( ) method, 153
function), 107
question mark (?), as a name value, 15
polymorphic_on argument, 159
QueuePool pool type (sqlalchemy.pool), 39
polymorphic_on parameter (mapper( )
quote argument
function), 107
Column constructor, 44
polymorphic_union( ) function, 162
Table constructor, 42
pool parameter (create_engine( ) function), 35
quote parameter
pool.manage( ) function, 38
Sequence constructor, 55
pool.manage( ) method, 38
quote_schema argument (Table), 42
poolclass parameter (create_engine( )
function), 35
pools (connections), 11 R
(see also connection pools) refcolumns parameter (ForeignKeyConstraint
pool_recycle parameter (create_engine( ) class), 46
function), 35 reflect( ) method (MetaData), 43
pool_size parameter (create_engine( ) reflection, defining tables, 42
function), 35 refresh( ) method, 137, 183
pool_timeout parameter (create_engine( ) refresh-expire parameter, 115
function), 35 relation( ) function, 108, 114, 134, 144
POPOs (plain old Python objects), 1, 3 custom collections, using, 117
populate_existing( ) method, 153 Elixir attribute-based syntax and, 178
populate_instance( ) hook (MapperExtension), parameters, 115
122 self-referential mappers and, 113
PostgreSQL, 1 relational model, 4, 168
drivers, installing, 24 relationships, 4
passive defaults, support for, 52 relationships (SQLAlchemy), 108
post_update parameter, 116 remote_side parameter (ManyToMany), 180
prefixes=None parameter (select( )), 74 remote_site parameter, 117
prefix_with( ) method, 85 remove( ) function, 134
PrimaryDeyConstraint object, 45 remover( ) decorator, 119
primaryjoin parameter, 117 removes( ) decorator, 119
removes_return( ) decorator, 120
Index | 213
_reorder( ) method, 204 setup_entities( ) method, 173
replaces( ) decorator, 119 "sharding" (horizontal partitioning), 124
replace_selectable( ) method, 85 single table inheritance, 157
required Elixir keyword argument, 175 SingletonThreadPool pool type
required parameter (ManyToOne), 179 (sqlalchemy.pool), 39
reset_joinpoint( ) method, 153 single_query( ) method, 165
ResultProxy class, 74 SQL dialect management, 12
ResultProxy object, 36 SQL expression language, 14
revert( ) method, 188 SQL Expression Language, 67
revert_to( ) method, 188 SQL injection attacks, 2
rollback( ) method, 136 sqlalchemy package, 24
rowcount ( ) method, 75 sqlalchemy.engine, 36
rowcount method (ResultProxy), 37 sqlalchemy.engine.Connection class, 36
sqlalchemy.engine.ResultProxy, 36
S sqlalchemy.ext.activemapper extension, 205
sqlalchemy.ext.assignmapper extension, 205
save( ) method, 129, 136, 184
sqlalchemy.ext.selectresults extension, 205
Elixir, quering using and, 183
sqlalchemy.ext.sessioncontext extension, 205
save-update parameter, 115
sqlalchemy.orm, 36
save_or_update( ) method, 184
sqlalchemy.orm.attributes, 36
scalar( ) method, 75
sqlalchemy.orm.collections module, 120
scalar( ) method (ResultProxy), 37
sqlalchemy.orm.mapper, 36
schema argument (MetaData.reflect( )
sqlalchemy.orm.strategies, 36
method), 43
sqlalchemy.orm.sync, 36
schema argument (Table), 41
sqlalchemy.orm.unitofwork, 36
schema definitions (database), 2, 12
sqlalchemy.pool, 36
schema( ) method, 191
sqlalchemy.types package, 60
scoped_session( ) function, 154
SQLite, 1
secondary parameter, 117
drivers, installing, 24
secondaryjoin parameter, 117
sqlite3 driver, 24
SELECT statements, 28, 72
SqlSoup, 189
customizing in queries, 147
start parameter (Sequence constructor), 55
WHERE clauses and, 75
startswith( ) method, 140
select( ) function, 72–74
startswith( ) method (ClauseElement), 78
"generative" interface, 84
_state attribute (mapped class), 9
select( ) method, 27, 72–74, 86
statements (SQL), 67
select_from( ) method, 85, 147
StaticPool pool type (sqlalchemy.pool), 39
select_table parameter (mapper( ) function),
strategy parameter (create_engine( ) function),
107
35
self-referential mappers, 113
subqueries, 90
Sequence object, 54
SUM( ) function, 150
Session object, 3, 18, 127–139
sum( ) method, 153
creating, 127
synonym Elixir keyword argument, 176
saving objects to, 129
synonym( ) function, 100
session.commit( ) method, 30
Session.configure( ) method, 127
sessionmaker( ) function, 29, 127 T
set operations, 88 Table object, 13
SetupTools package, 21 table.c object, 15
setup_all( ) method, 173 Table.insert( ) method, 68
214 | Index
Table.select( ) method, 72 updates, 26
tablename parameter (ManyToMany), 180 useexisting argument (Table), 42
Tables object, 40–43 uselist parameter, 117
table_iterator( ) method, 57 use_alter parameter (ForeignKey constructor),
Text construct, 72 45
WHERE clause and, 75 use_alter parameter (ManyToOne), 179
text parameter use_ansi parameter (create_engine( ) function),
text( ) function, 80 35
text( ) function, 79 use_labels=False parameter (select( )), 74
thread-local sessions, 153 use_oids parameter (create_engine( ) function),
threaded parameter (create_engine( ) function), 35
35 using_options( ) method, 173
through Elixir keyword argument, 176
transactional argument (sessionmaker( )
method), 128
V
values( ) method (ResultProxy), 37
Transient state (Session), 130
version_id_col parameter (mapper( ) function),
translate_row( ) hook (MapperExtension),
107
122
vertical partitioning, 122
tutorial.sqlite file, 25
viewonly parameter, 117
twophase argument (sessionmaker( ) method),
128
TypeDecorator object, 63 W
TypeEngine class, 14, 16 weak_identity_map argument (sessionmaker( )
TypeEngine objects, 59–65 method), 129
typemap=None parameter (text( ) function), WHERE clause, 72
80 operators and functions, 75–78
type_ argument (Column constructor), 43 where( ) method, 84
whereclause=None parameter (select( )), 73
U with_labels( ) method, 192
with_lockmode( ) method, 153
unbound metadata, 11
with_options( ) statement, 181
unbounded constructors, 39
with_parent( ) method, 153
undefer( ) function, 105
with_secret parameter, 186
undefer( ) method, 146
undefer_group( ) method, 146
UNION clause, 88
union( ) function, 88
union( ) method, 85
union_all( ) function, 88
union_all( ) method, 85
unique argument (Column constructor), 44
UNIQUE clause, 80
UNIQUE constraint, 52
UNIQUE constraints, 47
unit of work pattern, 93
update defaults, 48
UPDATE statements, 69–71
update( ) function, 28
SqlSoup, using, 191
update( ) method, 69, 137, 184
Index | 215