SQLAlchemy 0.3 Documentation
Table of Contents
(view full table)- Tutorial
- Database Engines
- Database Meta Data
- Constructing SQL Queries via Python Expressions
- Data Mapping
- Session / Unit of Work
- Advanced Data Mapping
- The Types System
- Connection Pooling
- Plugins
- Generated Documentation
Table of Contents: Full
(view brief table)- Tutorial
- Installation
- Getting Started
- SQLAlchemy is Two Libraries in One
- Working with Database Objects
- Defining Metadata, Binding to Engines
- Creating a Table
- Inserting Rows
- Selecting
- Working with Rows
- Table Relationships
- Working with Object Mappers
- Creating a Mapper
- Obtaining a Session
- The Query Object
- Making Changes
- Saving
- Relationships
- Transactions
- Next Steps
- Database Engines
- Supported Databases
- Establishing a Database Engine
- Database Engine Options
- Configuring Logging
- Using Connections
- Transactions
- Implicit Execution
- Database Meta Data
- Describing Databases with MetaData
- Binding MetaData to an Engine
- Using the global Metadata object
- Reflecting Tables
- Specifying the Schema Name
- ON UPDATE and ON DELETE
- Enabling Table / Column Quoting
- Other Options
- Creating and Dropping Database Tables
- Column Defaults and OnUpdates
- Pre-Executed Insert Defaults
- Pre-Executed OnUpdate Defaults
- Inline Default Execution: PassiveDefault
- Defining Sequences
- Defining Constraints and Indexes
- Adapting Tables to Alternate Metadata
- Constructing SQL Queries via Python Expressions
- Simple Select
- Explicit Execution
- Binding ClauseElements to Engines
- Getting Results
- Using Column Labels
- Table/Column Specification
- WHERE Clause
- Inner and Outer Joins
- Table Aliases
- Subqueries
- Correlated Subqueries
- Unions
- Custom Bind Parameters
- Literal Text Blocks
- Building Select Objects
- Inserts
- Updates
- Deletes
- Data Mapping
- Basic Data Mapping
- Synopsis
- The Query Object
- Saving Objects
- Defining and Using Relationships
- Selecting from Relationships
- Selecting With Joins
- Creating Joins Using select_by()
- Generating Join Criterion Using join_to, join_via
- Eager Loading
- Using Options to Change the Loading Strategy
- One to One/Many to One
- Many to Many
- Association Object
- Session / Unit of Work
- Overview
- Object States
- Acquiring a Session
- Introduction to the Identity Map
- Whats Changed ?
- The Session API
- query()
- get()
- load()
- save()
- flush()
- close()
- delete()
- clear()
- refresh() / expire()
- expunge()
- bind_mapper() / bind_table()
- update()
- save_or_update()
- merge()
- Cascade rules
- SessionTransaction
- Analyzing Object Flushes
- Advanced Data Mapping
- More On Mapper Properties
- Overriding Column Names
- Overriding Properties
- Custom List Classes
- Custom Join Conditions
- Lazy/Eager Joins Multiple Times to One Table
- Deferred Column Loading
- Working with Large Collections
- Relation Options
- Controlling Ordering
- Limiting Rows
- Mapping a Class with Table Inheritance
- Mapping a Class against Multiple Tables
- Mapping a Class against Arbitrary Selects
- Multiple Mappers for One Class
- Self Referential Mappers
- Result-Set Mapping
- Mapper Options
- Extending Mapper
- The Types System
- Connection Pooling
- Plugins
- SessionContext
- SelectResults
- assignmapper
- associationproxy
- threadlocal
- ActiveMapper
- SqlSoup
- ProxyEngine
- Generated Documentation
- module sqlalchemy.sql
- Module Functions
- alias()
- and_()
- asc()
- between()
- bindparam()
- case()
- cast()
- column()
- delete()
- desc()
- except_()
- except_all()
- exists()
- extract()
- insert()
- intersect()
- intersect_all()
- join()
- literal()
- literal_column()
- not_()
- null()
- or_()
- outerjoin()
- select()
- subquery()
- table()
- text()
- union()
- union_all()
- update()
- class AbstractDialect(object)
- class Alias(FromClause)
- class ClauseElement(object)
- class ClauseParameters(dict)
- class ClauseVisitor(object)
- class ColumnCollection(OrderedProperties)
- class ColumnElement(Selectable,_CompareMixin)
- class Compiled(ClauseVisitor)
- class CompoundSelect(_SelectBaseMixin,FromClause)
- class Executor(object)
- class FromClause(Selectable)
- class Join(FromClause)
- class Select(_SelectBaseMixin,FromClause)
- class Selectable(ClauseElement)
- class TableClause(FromClause)
- module sqlalchemy.schema
- class BoundMetaData(MetaData)
- class CheckConstraint(Constraint)
- class Column(SchemaItem,_ColumnClause)
- class ColumnDefault(DefaultGenerator)
- class Constraint(SchemaItem)
- class DefaultGenerator(SchemaItem)
- class DynamicMetaData(MetaData)
- class ForeignKey(SchemaItem)
- class ForeignKeyConstraint(Constraint)
- class Index(SchemaItem)
- class MetaData(SchemaItem)
- class PassiveDefault(DefaultGenerator)
- class PrimaryKeyConstraint(Constraint)
- class SchemaItem(object)
- class SchemaVisitor(ClauseVisitor)
- class Sequence(DefaultGenerator)
- class Table(SchemaItem,TableClause)
- class UniqueConstraint(Constraint)
- module sqlalchemy.types
- class BLOB(Binary)
- class BOOLEAN(Boolean)
- class Binary(TypeEngine)
- class Boolean(TypeEngine)
- class CHAR(String)
- class CLOB(String)
- class DATE(Date)
- class DATETIME(DateTime)
- class DECIMAL(Numeric)
- class Date(TypeEngine)
- class DateTime(TypeEngine)
- class FLOAT(Float)
- class Float(Numeric)
- class INT(Integer)
- class Integer(TypeEngine)
- class NCHAR(Unicode)
- class NullTypeEngine(TypeEngine)
- class Numeric(TypeEngine)
- class PickleType(MutableType,TypeDecorator)
- class SMALLINT(SmallInteger)
- class SmallInteger(Integer)
- class SmallInteger(Integer)
- class String(TypeEngine)
- class TEXT(String)
- class TIME(Time)
- class TIMESTAMP(DateTime)
- class Time(TypeEngine)
- class TypeDecorator(AbstractType)
- class TypeEngine(AbstractType)
- class Unicode(TypeDecorator)
- class VARCHAR(String)
- module sqlalchemy.engine
- Module Functions
- class Connectable(object)
- class Connection(Connectable)
- class ConnectionProvider(object)
- class DefaultRunner(SchemaVisitor)
- class Dialect(AbstractDialect)
- class Engine(Executor,Connectable)
- class ExecutionContext(object)
- class PrefetchingResultProxy(ResultProxy)
- class ResultProxy(object)
- class RowProxy(object)
- class SchemaIterator(SchemaVisitor)
- class Transaction(object)
- module sqlalchemy.engine.url
- module sqlalchemy.orm
- Module Functions
- backref()
- cascade_mappers()
- class_mapper()
- clear_mapper()
- clear_mappers()
- compile_mappers()
- contains_alias()
- contains_eager()
- defer()
- deferred()
- eagerload()
- extension()
- lazyload()
- mapper()
- noload()
- object_mapper()
- object_session()
- polymorphic_union()
- relation()
- synonym()
- undefer()
- class MapperExtension(object)
- class Query(object)
- class Session(object)
- module sqlalchemy.orm.mapper
- module sqlalchemy.orm.query
- module sqlalchemy.orm.session
- module sqlalchemy.pool
- Module Functions
- class AssertionPool(Pool)
- class NullPool(Pool)
- class Pool(object)
- class QueuePool(Pool)
- class SingletonThreadPool(Pool)
- module sqlalchemy.ext.sessioncontext
- module sqlalchemy.mods.threadlocal
- module sqlalchemy.ext.selectresults
- module sqlalchemy.exceptions
- class ArgumentError()
- class AssertionError()
- class CircularDependencyError()
- class CompileError()
- class ConcurrentModificationError()
- class DBAPIError()
- class FlushError()
- class InvalidRequestError()
- class NoSuchColumnError()
- class NoSuchTableError()
- class SQLAlchemyError()
- class SQLError()
- class TimeoutError()
- module sqlalchemy.ext.proxy
- class AutoConnectEngine(BaseProxyEngine)
- class BaseProxyEngine(Executor)
- class ProxyEngine(BaseProxyEngine)
- module sqlalchemy.databases.sqlite
- Module Functions
- class DateTimeMixin(object)
- class PassiveDefault(DefaultGenerator)
- class SLBinary(Binary)
- class SLBoolean(Boolean)
- class SLChar(CHAR)
- class SLDate(DateTimeMixin,Date)
- class SLDateTime(DateTimeMixin,DateTime)
- class SLInteger(Integer)
- class SLNumeric(Numeric)
- class SLSmallInteger(SmallInteger)
- class SLString(String)
- class SLText(TEXT)
- class SLTime(DateTimeMixin,Time)
- class SQLiteCompiler(ANSICompiler)
- class SQLiteDialect(ANSIDialect)
- class SQLiteDialect(ANSIDialect)
- class SQLiteExecutionContext(DefaultExecutionContext)
- class SQLiteIdentifierPreparer(ANSIIdentifierPreparer)
- class SQLiteSchemaDropper(ANSISchemaDropper)
- class SQLiteSchemaGenerator(ANSISchemaGenerator)
- class SingletonThreadPool(Pool)
- module sqlalchemy.databases.postgres
- Module Functions
- class PG1Date(Date)
- class PG1DateTime(DateTime)
- class PG1Time(Time)
- class PG2Date(Date)
- class PG2DateTime(DateTime)
- class PG2Time(Time)
- class PGBigInteger(PGInteger)
- class PGBinary(Binary)
- class PGBoolean(Boolean)
- class PGChar(CHAR)
- class PGCompiler(ANSICompiler)
- class PGDefaultRunner(ANSIDefaultRunner)
- class PGDialect(ANSIDialect)
- class PGDialect(ANSIDialect)
- class PGExecutionContext(DefaultExecutionContext)
- class PGFloat(Float)
- class PGIdentifierPreparer(ANSIIdentifierPreparer)
- class PGInet(TypeEngine)
- class PGInteger(Integer)
- class PGInterval(TypeEngine)
- class PGNumeric(Numeric)
- class PGSchemaDropper(ANSISchemaDropper)
- class PGSchemaGenerator(ANSISchemaGenerator)
- class PGSmallInteger(SmallInteger)
- class PGString(String)
- class PGText(TEXT)
- module sqlalchemy.databases.mysql
- Module Functions
- class MSBigInteger(MSInteger)
- class MSBinary(Binary)
- class MSBoolean(Boolean)
- class MSChar(CHAR)
- class MSDate(Date)
- class MSDateTime(DateTime)
- class MSDecimal(MSNumeric)
- class MSDouble(MSNumeric)
- class MSEnum(MSString)
- class MSFloat(Float)
- class MSInteger(Integer)
- class MSLongText(MSText)
- class MSMediumBlob(MSBinary)
- class MSMediumText(MSText)
- class MSNumeric(Numeric)
- class MSSmallInteger(SmallInteger)
- class MSString(String)
- class MSText(TEXT)
- class MSTime(Time)
- class MSTinyText(MSText)
- class MySQLCompiler(ANSICompiler)
- class MySQLDialect(ANSIDialect)
- class MySQLDialect(ANSIDialect)
- class MySQLExecutionContext(DefaultExecutionContext)
- class MySQLIdentifierPreparer(ANSIIdentifierPreparer)
- class MySQLSchemaDropper(ANSISchemaDropper)
- class MySQLSchemaGenerator(ANSISchemaGenerator)
- class array(object)
- module sqlalchemy.databases.oracle
- Module Functions
- class OracleBinary(Binary)
- class OracleBoolean(Boolean)
- class OracleChar(CHAR)
- class OracleCompiler(ANSICompiler)
- class OracleDateTime(DateTime)
- class OracleDefaultRunner(ANSIDefaultRunner)
- class OracleDialect(ANSIDialect)
- class OracleDialect(ANSIDialect)
- class OracleExecutionContext(DefaultExecutionContext)
- class OracleInteger(Integer)
- class OracleNumeric(Numeric)
- class OracleRaw(Binary)
- class OracleSchemaDropper(ANSISchemaDropper)
- class OracleSchemaGenerator(ANSISchemaGenerator)
- class OracleSmallInteger(SmallInteger)
- class OracleString(String)
- class OracleText(TEXT)
- class OracleTimestamp(DateTime)
- module sqlalchemy.databases.mssql
- Module Functions
- class AdoMSNVarchar(MSNVarchar)
- class AdoMSUnicode(MSUnicode)
- class MSBinary(Binary)
- class MSBoolean(Boolean)
- class MSChar(CHAR)
- class MSDate(Date)
- class MSDateTime(DateTime)
- class MSFloat(Float)
- class MSInteger(Integer)
- class MSNChar(NCHAR)
- class MSNVarchar(MSString)
- class MSNumeric(Numeric)
- class MSSQLCompiler(ANSICompiler)
- class MSSQLDefaultRunner(ANSIDefaultRunner)
- class MSSQLDialect(ANSIDialect)
- class MSSQLDialect(ANSIDialect)
- class MSSQLDialect_adodbapi(MSSQLDialect)
- class MSSQLDialect_pymssql(MSSQLDialect)
- class MSSQLDialect_pyodbc(MSSQLDialect)
- class MSSQLExecutionContext(DefaultExecutionContext)
- class MSSQLIdentifierPreparer(ANSIIdentifierPreparer)
- class MSSQLSchemaDropper(ANSISchemaDropper)
- class MSSQLSchemaGenerator(ANSISchemaGenerator)
- class MSSmallInteger(SmallInteger)
- class MSString(String)
- class MSText(TEXT)
- class MSTinyInteger(Integer)
- class MSUnicode(Unicode)
- module sqlalchemy.databases.firebird
- Module Functions
- class FBBinary(Binary)
- class FBBoolean(Boolean)
- class FBChar(CHAR)
- class FBCompiler(ANSICompiler)
- class FBDate(DateTime)
- class FBDateTime(DateTime)
- class FBDefaultRunner(ANSIDefaultRunner)
- class FBDialect(ANSIDialect)
- class FBDialect(ANSIDialect)
- class FBExecutionContext(DefaultExecutionContext)
- class FBIdentifierPreparer(ANSIIdentifierPreparer)
- class FBInteger(Integer)
- class FBNumeric(Numeric)
- class FBSchemaDropper(ANSISchemaDropper)
- class FBSchemaGenerator(ANSISchemaGenerator)
- class FBSmallInteger(SmallInteger)
- class FBString(String)
- class FBText(TEXT)
This tutorial provides a relatively simple walking tour through the basic concepts of SQLAlchemy. You may wish to skip it and dive into the main manual which is more reference-oriented. The examples in this tutorial comprise a fully working interactive Python session, and are guaranteed to be functioning courtesy of doctest.
Installation
Installing SQLAlchemy
Installing SQLAlchemy from scratch is most easily achieved with setuptools. (setuptools installation). Just run this from the command-line:
# easy_install SQLAlchemy
This command will download the latest version of SQLAlchemy from the Python Cheese Shop and install it to your system.
Otherwise, you can install from the distribution using the setup.py
script:
# python setup.py install
back to section top
Installing a Database API
SQLAlchemy is designed to operate with a DBAPI implementation built for a particular database, and includes support for the most popular databases. If you have one of the supported DBAPI implementations, you can proceed to the following section. Otherwise SQLite is an easy-to-use database to get started with, which works with plain files or in-memory databases.
SQLite is included with Python 2.5 and greater.
If you are working with Python 2.3 or 2.4, SQLite and the Python API for SQLite can be installed from the following packages:
- pysqlite - Python interface for SQLite
- SQLite library
Note that the SQLite library download is not required with Windows, as the Windows Pysqlite library already includes it linked in. Pysqlite and SQLite can also be installed on Linux or FreeBSD via pre-made packages or from sources.
Getting Started
Imports
To start connecting to databases and begin issuing queries, we want to import the base of SQLAlchemy's functionality, which is provided under the module name of sqlalchemy
. For the purposes of this tutorial, we will import its full list of symbols into our own local namespace.
>>> from sqlalchemy import *
Note that importing using the *
operator pulls all the names from sqlalchemy
into the local module namespace, which in a real application can produce name conflicts. Therefore its recommended in practice to either import the individual symbols desired (i.e. from sqlalchemy import Table, Column
) or to import under a distinct namespace (i.e. import sqlalchemy as sa
).
Connecting to the Database
After our imports, the next thing we need is a handle to the desired database, represented by an Engine
object. This object handles the business of managing connections and dealing with the specifics of a particular database. Below, we will make a SQLite connection to a file-based database called "tutorial.db".
>>> db = create_engine('sqlite:///tutorial.db')
Technically, the above statement did not make an actual connection to the sqlite database just yet. As soon as we begine working with the engine, it will start creating connections. In the case of SQLite, the tutorial.db
file will actually be created at the moment it is first used, if the file does not exist already.
For full information on creating database engines, including those for SQLite and others, see Database Engines.
SQLAlchemy is Two Libraries in One
Now that the basics of installing SQLAlchemy and connecting to our database are established, we can start getting in to actually doing something. But first, a little bit of explanation is required.
A central concept of SQLAlchemy is that it actually contains two distinct areas of functionality, one of which builds upon the other. One is a SQL Construction Language and the other is an Object Relational Mapper ("ORM" for short). The SQL construction language allows you to construct objects called ClauseElements
which represent SQL expressions. These ClauseElements can then be executed against any database, where they are compiled into strings that are appropriate for the target database, and return an object called a ResultProxy
, which is essentially a result set object that acts very much like a deluxe version of the dbapi cursor
object.
The Object Relational Mapper (ORM) is a set of tools completely distinct from the SQL Construction Language which serve the purpose of mapping Python object instances into database rows, providing a rich selection interface with which to retrieve instances from tables as well as a comprehensive solution to persisting changes on those instances back into the database. When working with the ORM, its underlying workings as well as its public API make extensive use of the SQL Construction Language, however the general theory of operation is slightly different. Instead of working with database rows directly, you work with your own user-defined classes and object instances. Additionally, the method of issuing queries to the database is different, as the ORM handles the job of generating most of the SQL required, and instead requires more information about what kind of class instances you'd like to load and where you'd like to put them.
Where SA is somewhat unique, more powerful, and slightly more complicated is that the two areas of functionality can be mixed together in many ways. A key strategy to working with SA effectively is to have a solid awareness of these two distinct toolsets, and which concepts of SA belong to each - even some publications have confused the SQL Construction Language with the ORM. The key difference between the two is that when you're working with cursor-like result sets its the SQL Construction Language, and when working with collections of your own class instances its the Object Relational Mapper.
This tutorial will first focus on the basic configuration that is common to using both the SQL Construction Language as well as the ORM, which is to declare information about your database called table metadata. This will be followed by some constructed SQL examples, and then into usage of the ORM utilizing the same data we established in the SQL construction examples.
back to section topWorking with Database Objects
Defining Metadata, Binding to Engines
Configuring SQLAlchemy for your database consists of creating objects called Tables
, each of which represent an actual table in the database. A collection of Table
objects resides in a MetaData
object which is essentially a table collection. We will create a handy form of MetaData
that automatically connects to our Engine
(connecting a schema object to an Engine is called binding):
>>> metadata = BoundMetaData(db)
An equivalent operation is to create the BoundMetaData
object directly with an Engine URL, which calls the create_engine
call for us:
>>> metadata = BoundMetaData('sqlite:///tutorial.db')
Now, when we tell "metadata" about the tables in our database, we can issue CREATE statements for those tables, as well as execute SQL statements derived from them, without needing to open or close any connections; that will be all done automatically.
Note that SQLALchemy fully supports the usage of explicit Connection objects for all SQL operations, which may be in conjunction with plain MetaData
objects that are entirely unbound to any Engine, providing a more decoupled pattern that allows finer-grained control of connections than the "bound" approach this tutorial will present. For the purposes of this tutorial, we will stick with "bound" objects, as it allows us to focus more on SA's general concepts, leaving explicit connection management as a more advanced topic.
Creating a Table
With metadata
as our established home for tables, lets make a Table for it:
>>> users_table = Table('users', metadata, ... Column('user_id', Integer, primary_key=True), ... Column('user_name', String(40)), ... Column('password', String(10)) ... )
As you might have guessed, we have just defined a table named users
which has three columns: user_id
(which is a primary key column), user_name
and password
. Currently it is just an object that doesn't necessarily correspond to an existing table in our database. To actually create the table, we use the create()
method. To make it interesting, we will have SQLAlchemy echo the SQL statements it sends to the database, by setting the echo
flag on the Engine
associated with our BoundMetaData
:
>>> metadata.engine.echo = True >>> users_table.create() CREATE TABLE users ( user_id INTEGER NOT NULL, user_name VARCHAR(40), password VARCHAR(10), PRIMARY KEY (user_id) ) ...
Alternatively, the users
table might already exist (such as, if you're running examples from this tutorial for the second time), in which case you can just skip the create()
method call. You can even skip defining the individual columns in the users
table and ask SQLAlchemy to load its definition from the database:
>>> users_table = Table('users', metadata, autoload=True) >>> list(users_table.columns)[0].name 'user_id'
Loading a table's columns from the database is called reflection. Documentation on table metadata, including reflection, is available in Database Meta Data.
back to section topInserting Rows
Inserting is achieved via the insert()
method, which defines a clause object (known as a ClauseElement
) representing an INSERT statement:
>>> i = users_table.insert() >>> i <sqlalchemy.sql._Insert object at 0x...> >>> # the string form of the Insert object is a generic SQL representation >>> print i INSERT INTO users (user_id, user_name, password) VALUES (?, ?, ?)
Since we created this insert statement object from the users
table which is bound to our Engine
, the statement itself is also bound to the Engine
, and supports executing itself. The execute()
method of the clause object will compile the object into a string according to the underlying dialect of the Engine to which the statement is bound, and will then execute the resulting statement.
>>> # insert a single row >>> i.execute(user_name='Mary', password='secure') INSERT INTO users (user_name, password) VALUES (?, ?) ['Mary', 'secure'] COMMIT <sqlalchemy.engine.base.ResultProxy object at 0x...> >>> # insert multiple rows simultaneously >>> i.execute({'user_name':'Tom'}, {'user_name':'Fred'}, {'user_name':'Harry'}) INSERT INTO users (user_name) VALUES (?) [['Tom'], ['Fred'], ['Harry']] COMMIT <sqlalchemy.engine.base.ResultProxy object at 0x...>
Note that the VALUES
clause of each INSERT
statement was automatically adjusted to correspond to the parameters sent to the execute()
method. This is because the compilation step of a ClauseElement
takes into account not just the constructed SQL object and the specifics of the type of database being used, but the execution parameters sent along as well.
When constructing clause objects, SQLAlchemy will bind all literal values into bind parameters. On the construction side, bind parameters are always treated as named parameters. At compilation time, SQLAlchemy will convert them into their proper format, based on the paramstyle of the underlying DBAPI. This works equally well for all named and positional bind parameter formats described in the DBAPI specification.
Documentation on inserting: Inserts.
back to section topSelecting
Let's check that the data we have put into users
table is actually there. The procedure is analogous to the insert example above, except you now call the select()
method off the users
table:
>>> s = users_table.select() >>> print s SELECT users.user_id, users.user_name, users.password FROM users >>> r = s.execute() SELECT users.user_id, users.user_name, users.password FROM users []
This time, we won't ignore the return value of execute()
. Its an instance of ResultProxy
, which is a result-holding object that behaves very similarly to the cursor
object one deals with directly with a database API:
>>> r <sqlalchemy.engine.base.ResultProxy object at 0x...> >>> r.fetchone() (1, u'Mary', u'secure') >>> r.fetchall() [(2, u'Tom', None), (3, u'Fred', None), (4, u'Harry', None)]
Query criterion for the select is specified using Python expressions, using the Column
objects in the Table
as a base. All expressions constructed from Column
objects are themselves instances of ClauseElements
, just like the Select
, Insert
, and Table
objects themselves.
>>> r = users_table.select(users_table.c.user_name=='Harry').execute() SELECT users.user_id, users.user_name, users.password FROM users WHERE users.user_name = ? ['Harry'] >>> row = r.fetchone() >>> print row (4, u'Harry', None)
Pretty much the full range of standard SQL operations are supported as constructed Python expressions, including joins, ordering, grouping, functions, correlated subqueries, unions, etc. Documentation on selecting: Simple Select.
back to section topWorking with Rows
You can see that when we print out the rows returned by an execution result, it prints the rows as tuples. These rows in fact support both the list and dictionary interfaces. The dictionary interface allows the addressing of columns by string column name, or even the original Column
object:
>>> row.keys() ['user_id', 'user_name', 'password'] >>> row['user_id'], row[1], row[users_table.c.password] (4, u'Harry', None)
Addressing the columns in a row based on the original Column
object is especially handy, as it eliminates the need to work with literal column names altogether.
Result sets also support iteration. We'll show this with a slightly different form of select
that allows you to specify the specific columns to be selected:
>>> for row in select([users_table.c.user_id, users_table.c.user_name]).execute(): ... print row SELECT users.user_id, users.user_name FROM users [] (1, u'Mary') (2, u'Tom') (3, u'Fred') (4, u'Harry')
Table Relationships
Lets create a second table, email_addresses
, which references the users
table. To define the relationship between the two tables, we will use the ForeignKey
construct. We will also issue the CREATE
statement for the table:
>>> email_addresses_table = Table('email_addresses', metadata, ... Column('address_id', Integer, primary_key=True), ... Column('email_address', String(100), nullable=False), ... Column('user_id', Integer, ForeignKey('users.user_id'))) >>> email_addresses_table.create() CREATE TABLE email_addresses ( address_id INTEGER NOT NULL, email_address VARCHAR(100) NOT NULL, user_id INTEGER, PRIMARY KEY (address_id), FOREIGN KEY(user_id) REFERENCES users (user_id) ) ...
Above, the email_addresses
table is related to the users
table via the ForeignKey('users.user_id')
. The ForeignKey
constructor can take a Column
object or a string representing the table and column name. When using the string argument, the referenced table must exist within the same MetaData
object; thats where it looks for the other table!
Next, lets put a few rows in:
>>> email_addresses_table.insert().execute( ... {'email_address':'[email protected]', 'user_id':2}, ... {'email_address':'[email protected]', 'user_id':1}) INSERT INTO email_addresses (email_address, user_id) VALUES (?, ?) [['[email protected]', 2], ['[email protected]', 1]] COMMIT <sqlalchemy.engine.base.ResultProxy object at 0x...>
With two related tables, we can now construct a join amongst them using the join
method:
>>> r = users_table.join(email_addresses_table).select().execute() SELECT users.user_id, users.user_name, users.password, email_addresses.address_id, email_addresses.email_address, email_addresses.user_id FROM users JOIN email_addresses ON users.user_id = email_addresses.user_id [] >>> print [row for row in r] [(1, u'Mary', u'secure', 2, u'[email protected]', 1), (2, u'Tom', None, 1, u'[email protected]', 2)]
The join
method is also a standalone function in the sqlalchemy
namespace. The join condition is figured out from the foreign keys of the Table objects given. The condition (also called the "ON clause") can be specified explicitly, such as in this example where we locate all users that used their email address as their password:
>>> print join(users_table, email_addresses_table, ... and_(users_table.c.user_id==email_addresses_table.c.user_id, ... users_table.c.password==email_addresses_table.c.email_address) ... ) users JOIN email_addresses ON users.user_id = email_addresses.user_id AND users.password = email_addresses.email_address
Working with Object Mappers
Now that we have a little bit of Table and SQL operations covered, lets look into SQLAlchemy's ORM (object relational mapper). With the ORM, you associate Tables (and other Selectable units, like queries and table aliases) with Python classes, into units called Mappers. Then you can execute queries that return lists of object instances, instead of result sets. The object instances themselves are associated with an object called a Session, which automatically tracks changes on each object and supports a "save all at once" operation called a flush.
To start, we will import the names necessary to use SQLAlchemy's ORM, again using import *
for simplicities sake, even though we all know that in real life we should be importing individual names via "from sqlalchemy.orm import symbol1, symbol2, ...
" or "import sqlalchemy.orm as orm
":
>>> from sqlalchemy.orm import *
It should be noted that the above step is technically not needed when working with the 0.3 series of SQLAlchemy; all symbols from the orm
package are also included in the sqlalchemy
package. However, a future release (most likely the 0.4 series) will make the separate orm
import required in order to use the object relational mapper, so its a good practice for now.
Creating a Mapper
A Mapper is usually created once per Python class, and at its core primarily means to say, "objects of this class are to be stored as rows in this table". Lets create a class called User
, which will represent a user object that is stored in our users
table:
>>> class User(object): ... def __repr__(self): ... return "%s(%r,%r)" % ( ... self.__class__.__name__, self.user_name, self.password)
The class is a new style class (i.e. it extends object
) and does not require a constructor (although one may be provided if desired). We just have one __repr__
method on it which will display basic information about the User. Note that the __repr__
method references the instance variables user_name
and password
which otherwise aren't defined. While we are free to explicitly define these attributes and treat them normally, this is optional; as SQLAlchemy's Mapper
construct will manage them for us, since their names correspond to the names of columns in the users
table. Lets create a mapper, and observe that these attributes are now defined:
>>> mapper(User, users_table) <sqlalchemy.orm.mapper.Mapper object at 0x...> >>> u1 = User() >>> print u1.user_name None >>> print u1.password None
The mapper
function returns a new instance of Mapper
. As it is the first Mapper we have created for the User
class, it is known as the classes' primary mapper. We generally don't need to hold onto the return value of the mapper
function; SA can automatically locate this Mapper as needed when it deals with the User
class.
Obtaining a Session
After you create a Mapper, all operations with that Mapper require the usage of an important object called a Session
. All objects loaded or saved by the Mapper must be attached to a Session
object, which represents a kind of "workspace" of objects that are loaded into memory. A particular object instance can only be attached to one Session
at a time (but of course can be moved around or detached altogether).
By default, you have to create a Session
object explicitly before you can load or save objects. Theres several ways to manage sessions, but the most straightforward is to just create one, which we will do by saying, create_session()
:
>>> session = create_session() >>> session <sqlalchemy.orm.session.Session object at 0x...>
The Query Object
The Session has all kinds of methods on it to manage and inspect its collection of objects. The Session also provides an easy interface which can be used to query the database, by giving you an instance to a Query
object corresponding to a particular Python class:
>>> query = session.query(User) >>> print query.select_by(user_name='Harry') SELECT users.user_name AS users_user_name, users.password AS users_password, users.user_id AS users_user_id FROM users WHERE users.user_name = ? ORDER BY users.oid ['Harry'] [User(u'Harry',None)]
All querying for objects is performed via an instance of Query
. The various select
methods on an instance of Mapper
also use an underlying Query
object to perform the operation. A Query
is always bound to a specific Session
.
Lets turn off the database echoing for a moment, and try out a few methods on Query
. Methods that end with the suffix _by
primarily take keyword arguments which correspond to properties on the object. Other methods take ClauseElement
objects, which are constructed by using Column
objects inside of Python expressions, in the same way as we did with our SQL select example in the previous section of this tutorial. Using ClauseElement
structures to query objects is more verbose but more flexible:
>>> metadata.engine.echo = False >>> print query.select(User.c.user_id==3) [User(u'Fred',None)] >>> print query.get(2) User(u'Tom',None) >>> print query.get_by(user_name='Mary') User(u'Mary',u'secure') >>> print query.selectfirst(User.c.password==None) User(u'Tom',None) >>> print query.count() 4
Notice that our User
class has a special attribute c
attached to it. This 'c' represents the columns on the User's mapper's Table object. Saying User.c.user_name
is synonymous with saying users_table.c.user_name
, recalling that User
is the Python class and users
is our Table
object.
Making Changes
With a little experience in loading objects, lets see what its like to make changes. First, lets create a new user "Ed". We do this by just constructing the new object. Then, we just add it to the session:
>>> ed = User() >>> ed.user_name = 'Ed' >>> ed.password = 'edspassword' >>> session.save(ed) >>> ed in session True
Lets also make a few changes on some of the objects in the database. We will load them with our Query
object, and then change some things.
>>> mary = query.get_by(user_name='Mary') >>> harry = query.get_by(user_name='Harry') >>> mary.password = 'marysnewpassword' >>> harry.password = 'harrysnewpassword'
At the moment, nothing has been saved to the database; all of our changes are in memory only. What happens if some other part of the application also tries to load 'Mary' from the database and make some changes before we had a chance to save it ? Assuming that the same Session
is used, loading 'Mary' from the database a second time will issue a second query in order locate the primary key of 'Mary', but will return the same object instance as the one already loaded. This behavior is due to an important property of the Session
known as the identity map:
>>> mary2 = query.get_by(user_name='Mary') >>> mary is mary2 True
With the identity map, a single Session
can be relied upon to keep all loaded instances straight.
As far as the issue of the same object being modified in two different Sessions, that's an issue of concurrency detection; SQLAlchemy does some basic concurrency checks when saving objects, with the option for a stronger check using version ids. See advdatamapping_arguments for more details.
back to section topSaving
With a new user "ed" and some changes made on "Mary" and "Harry", lets also mark "Fred" as deleted:
>>> fred = query.get_by(user_name='Fred') >>> session.delete(fred)
Then to send all of our changes to the database, we flush()
the Session. Lets turn echo back on to see this happen!:
>>> metadata.engine.echo = True >>> session.flush() BEGIN UPDATE users SET password=? WHERE users.user_id = ? ['marysnewpassword', 1] UPDATE users SET password=? WHERE users.user_id = ? ['harrysnewpassword', 4] INSERT INTO users (user_name, password) VALUES (?, ?) ['Ed', 'edspassword'] DELETE FROM users WHERE users.user_id = ? [3] COMMIT
Relationships
When our User object contains relationships to other kinds of information, such as a list of email addresses, we can indicate this by using a function when creating the Mapper
called relation()
. While there is a lot you can do with relations, we'll cover a simple one here. First, recall that our users
table has a foreign key relationship to another table called email_addresses
. A single row in email_addresses
has a column user_id
that references a row in the users
table; since many rows in the email_addresses
table can reference a single row in users
, this is called a one to many relationship.
To illustrate this relationship, we will start with a new mapper configuration. Since our User
class has a mapper assigned to it, we want to discard it and start over again. So we issue the clear_mappers()
function first, which removes all mapping associations from classes:
>>> clear_mappers()
When removing mappers, it is usually best to remove all mappings at the same time, since mappers usually have relationships to each other which will become invalid if only part of the mapper collection is removed. In practice, a particular mapping setup will usually remain throughout the lifetime of an application. Clearing out the mappers and making new ones is a practice that is generally limited to writing mapper unit tests and experimenting from the console.
Next, we want to create a class/mapping that corresponds to the email_addresses
table. We will create a new class Address
which represents a single row in the email_addresses
table, and a corresponding Mapper
which will associate the Address
class with the email_addresses
table:
>>> class Address(object): ... def __init__(self, email_address): ... self.email_address = email_address ... def __repr__(self): ... return "%s(%r)" % ( ... self.__class__.__name__, self.email_address) >>> mapper(Address, email_addresses_table) <sqlalchemy.orm.mapper.Mapper object at 0x...>
We then create a mapper for the User
class which contains a relationship to the Address
class using the relation()
function:
>>> mapper(User, users_table, properties={ ... 'addresses':relation(Address) ... }) <sqlalchemy.orm.mapper.Mapper object at 0x...>
The relation()
function takes either a class or a Mapper as its first argument, and has many options to further control its behavior. When this mapping relationship is used, each new User
instance will contain an attribute called addresses
. SQLAlchemy will automatically determine that this relationship is a one-to-many relationship, and will subsequently create addresses
as a list. When a new User
is created, this list will begin as empty.
The order in which the mapping definitions for User
and Address
is created is not significant. When the mapper()
function is called, it creates an uncompiled mapping record corresponding to the given class/table combination. When the mappers are first used, the entire collection of mappers created up until that point will be compiled, which involves the establishment of class instrumentation as well as the resolution of all mapping relationships.
Lets try out this new mapping configuration, and see what we get for the email addresses already in the database. Since we have made a new mapping configuration, its best that we clear out our Session
, which is currently holding onto every User
object we have already loaded:
>>> session.clear()
We can then treat the addresses
attribute on each User
object like a regular list:
>>> mary = query.get_by(user_name='Mary') SELECT users.user_name AS users_user_name, users.password AS users_password, users.user_id AS users_user_id FROM users WHERE users.user_name = ? ORDER BY users.oid LIMIT 1 OFFSET 0 ['Mary'] >>> print [a for a in mary.addresses] SELECT email_addresses.user_id AS email_addresses_user_id, email_addresses.address_id AS email_addresses_address_id, email_addresses.email_address AS email_addresses_email_address FROM email_addresses WHERE ? = email_addresses.user_id ORDER BY email_addresses.oid [1] [Address(u'[email protected]')]
Adding to the list is just as easy. New Address
objects will be detected and saved when we flush
the Session:
>>> mary.addresses.append(Address('[email protected]')) >>> session.flush() BEGIN INSERT INTO email_addresses (email_address, user_id) VALUES (?, ?) ['[email protected]', 1] COMMIT
Main documentation for using mappers: Data Mapping
back to section topTransactions
You may have noticed from the example above that when we say session.flush()
, SQLAlchemy indicates the names BEGIN
and COMMIT
to indicate a transaction with the database. The flush()
method, since it may execute many statements in a row, will automatically use a transaction in order to execute these instructions. But what if we want to use flush()
inside of a larger transaction? This is performed via the SessionTransaction
object, which we can establish using session.create_transaction()
. Below, we will perform a more complicated SELECT
statement, make several changes to our collection of users and email addresess, and then create a new user with two email addresses, within the context of a transaction. We will perform a flush()
in the middle of it to write the changes we have so far, and then allow the remaining changes to be written when we finally commit()
the transaction. We enclose our operations within a try/except
block to ensure that resources are properly freed:
>>> transaction = session.create_transaction() >>> try: ... (ed, harry, mary) = session.query(User).select( ... User.c.user_name.in_('Ed', 'Harry', 'Mary'), order_by=User.c.user_name ... ) ... del mary.addresses[1] ... harry.addresses.append(Address('[email protected]')) ... session.flush() ... print "***flushed the session***" ... fred = User() ... fred.user_name = 'fred_again' ... fred.addresses.append(Address('[email protected]')) ... fred.addresses.append(Address('[email protected]')) ... session.save(fred) ... transaction.commit() ... except: ... transaction.rollback() ... raise BEGIN SELECT users.user_name AS users_user_name, users.password AS users_password, users.user_id AS users_user_id FROM users WHERE users.user_name IN (?, ?, ?) ORDER BY users.user_name ['Ed', 'Harry', 'Mary'] SELECT email_addresses.user_id AS email_addresses_user_id, email_addresses.address_id AS email_addresses_address_id, email_addresses.email_address AS email_addresses_email_address FROM email_addresses WHERE ? = email_addresses.user_id ORDER BY email_addresses.oid [4] UPDATE email_addresses SET user_id=? WHERE email_addresses.address_id = ? [None, 3] INSERT INTO email_addresses (email_address, user_id) VALUES (?, ?) ['[email protected]', 4] ***flushed the session*** INSERT INTO users (user_name, password) VALUES (?, ?) ['fred_again', None] INSERT INTO email_addresses (email_address, user_id) VALUES (?, ?) ['[email protected]', 6] INSERT INTO email_addresses (email_address, user_id) VALUES (?, ?) ['[email protected]', 6] COMMIT
Main documentation: Session / Unit of Work
Next Steps
That covers a quick tour through the basic idea of SQLAlchemy, in its simplest form. Beyond that, one should familiarize oneself with the basics of Sessions, the various patterns that can be used to define different kinds of Mappers and relations among them, the rudimentary SQL types that are available when constructing Tables, and the basics of Engines, SQL statements, and database Connections.