]> git.ipfire.org Git - thirdparty/sqlalchemy/sqlalchemy.git/commitdiff
Corrected failing doctests in the docs directory due to Changeset r6860. Fixes #1722.
authorMichael Trier <mtrier@gmail.com>
Sun, 28 Feb 2010 21:55:16 +0000 (21:55 +0000)
committerMichael Trier <mtrier@gmail.com>
Sun, 28 Feb 2010 21:55:16 +0000 (21:55 +0000)
doc/build/ormtutorial.rst
doc/build/sqlexpression.rst

index 725aece9d612b43bf3fb882a2fb0905818eb33ab..d835fd5f31dbe46c419e304b1f3d269914b129e6 100644 (file)
@@ -219,12 +219,12 @@ For example, below we create a new :class:`~sqlalchemy.orm.query.Query` object w
     {sql}>>> our_user = session.query(User).filter_by(name='ed').first() # doctest:+ELLIPSIS,+NORMALIZE_WHITESPACE
     BEGIN
     INSERT INTO users (name, fullname, password) VALUES (?, ?, ?)
-    ['ed', 'Ed Jones', 'edspassword']
+    ('ed', 'Ed Jones', 'edspassword')
     SELECT users.id AS users_id, users.name AS users_name, users.fullname AS users_fullname, users.password AS users_password
     FROM users
     WHERE users.name = ?
      LIMIT 1 OFFSET 0
-    ['ed']
+    ('ed',)
     {stop}>>> our_user
     <User('ed','Ed Jones', 'edspassword')>
 
@@ -272,13 +272,13 @@ We tell the :class:`~sqlalchemy.orm.session.Session` that we'd like to issue all
 
     {sql}>>> session.commit()
     UPDATE users SET password=? WHERE users.id = ?
-    ['f8s7ccs', 1]
+    ('f8s7ccs', 1)
     INSERT INTO users (name, fullname, password) VALUES (?, ?, ?)
-    ['wendy', 'Wendy Williams', 'foobar']
+    ('wendy', 'Wendy Williams', 'foobar')
     INSERT INTO users (name, fullname, password) VALUES (?, ?, ?)
-    ['mary', 'Mary Contrary', 'xxg527']
+    ('mary', 'Mary Contrary', 'xxg527')
     INSERT INTO users (name, fullname, password) VALUES (?, ?, ?)
-    ['fred', 'Fred Flinstone', 'blah']
+    ('fred', 'Fred Flinstone', 'blah')
     COMMIT
 
 ``commit()`` flushes whatever remaining changes remain to the database, and commits the transaction.  The connection resources referenced by the session are now returned to the connection pool.  Subsequent operations with this session will occur in a **new** transaction, which will again re-acquire connection resources when first needed.
@@ -292,7 +292,7 @@ If we look at Ed's ``id`` attribute, which earlier was ``None``, it now has a va
     SELECT users.id AS users_id, users.name AS users_name, users.fullname AS users_fullname, users.password AS users_password
     FROM users
     WHERE users.id = ?
-    [1]
+    (1,)
     {stop}1
 
 After the :class:`~sqlalchemy.orm.session.Session` inserts new rows in the database, all newly generated identifiers and database-generated defaults become available on the instance, either immediately or via load-on-first-access.  In this case, the entire row was re-loaded on access because a new transaction was begun after we issued ``commit()``.  SQLAlchemy by default refreshes data from a previous transaction the first time it's accessed within a new transaction, so that the most recent state is available.  The level of reloading is configurable as is described in the chapter on Sessions.
@@ -318,13 +318,13 @@ Querying the session, we can see that they're flushed into the current transacti
 
     {sql}>>> session.query(User).filter(User.name.in_(['Edwardo', 'fakeuser'])).all() #doctest: +NORMALIZE_WHITESPACE
     UPDATE users SET name=? WHERE users.id = ?
-    ['Edwardo', 1]
+    ('Edwardo', 1)
     INSERT INTO users (name, fullname, password) VALUES (?, ?, ?)
-    ['fakeuser', 'Invalid', '12345']
+    ('fakeuser', 'Invalid', '12345')
     SELECT users.id AS users_id, users.name AS users_name, users.fullname AS users_fullname, users.password AS users_password
     FROM users
     WHERE users.name IN (?, ?)
-    ['Edwardo', 'fakeuser']
+    ('Edwardo', 'fakeuser')
     {stop}[<User('Edwardo','Ed Jones', 'f8s7ccs')>, <User('fakeuser','Invalid', '12345')>]
 
 Rolling back, we can see that ``ed_user``'s name is back to ``ed``, and ``fake_user`` has been kicked out of the session:
@@ -340,7 +340,7 @@ Rolling back, we can see that ``ed_user``'s name is back to ``ed``, and ``fake_u
     SELECT users.id AS users_id, users.name AS users_name, users.fullname AS users_fullname, users.password AS users_password
     FROM users
     WHERE users.id = ?
-    [1]
+    (1,)
     {stop}u'ed'
     >>> fake_user in session
     False
@@ -353,7 +353,7 @@ issuing a SELECT illustrates the changes made to the database:
     SELECT users.id AS users_id, users.name AS users_name, users.fullname AS users_fullname, users.password AS users_password
     FROM users
     WHERE users.name IN (?, ?)
-    ['ed', 'fakeuser']
+    ('ed', 'fakeuser')
     {stop}[<User('ed','Ed Jones', 'f8s7ccs')>]
 
 .. _ormtutorial_querying:
@@ -370,7 +370,7 @@ A :class:`~sqlalchemy.orm.query.Query` is created using the :class:`~sqlalchemy.
     SELECT users.id AS users_id, users.name AS users_name,
     users.fullname AS users_fullname, users.password AS users_password
     FROM users ORDER BY users.id
-    []
+    ()
     {stop}ed Ed Jones
     wendy Wendy Williams
     mary Mary Contrary
@@ -384,7 +384,7 @@ The :class:`~sqlalchemy.orm.query.Query` also accepts ORM-instrumented descripto
     ...     print name, fullname
     SELECT users.name AS users_name, users.fullname AS users_fullname
     FROM users
-    []
+    ()
     {stop}ed Ed Jones
     wendy Wendy Williams
     mary Mary Contrary
@@ -398,7 +398,7 @@ The tuples returned by :class:`~sqlalchemy.orm.query.Query` are *named* tuples,
     ...    print row.User, row.name
     SELECT users.id AS users_id, users.name AS users_name, users.fullname AS users_fullname, users.password AS users_password
     FROM users
-    []
+    ()
     {stop}<User('ed','Ed Jones', 'f8s7ccs')> ed
     <User('wendy','Wendy Williams', 'foobar')> wendy
     <User('mary','Mary Contrary', 'xxg527')> mary
@@ -414,7 +414,7 @@ You can control the names using the ``label()`` construct for scalar attributes
     ...    print row.user_alias, row.name_label
     SELECT users_1.id AS users_1_id, users_1.name AS users_1_name, users_1.fullname AS users_1_fullname, users_1.password AS users_1_password, users_1.name AS name_label
     FROM users AS users_1
-    []{stop}
+    (){stop}
     <User('ed','Ed Jones', 'f8s7ccs')> ed
     <User('wendy','Wendy Williams', 'foobar')> wendy
     <User('mary','Mary Contrary', 'xxg527')> mary
@@ -429,7 +429,7 @@ Basic operations with :class:`~sqlalchemy.orm.query.Query` include issuing LIMIT
     SELECT users.id AS users_id, users.name AS users_name, users.fullname AS users_fullname, users.password AS users_password
     FROM users ORDER BY users.id
     LIMIT 2 OFFSET 1
-    []{stop}
+    (){stop}
     <User('wendy','Wendy Williams', 'foobar')>
     <User('mary','Mary Contrary', 'xxg527')>
 
@@ -441,7 +441,7 @@ and filtering results, which is accomplished either with :func:`~sqlalchemy.orm.
     ...    print name
     SELECT users.name AS users_name FROM users
     WHERE users.fullname = ?
-    ['Ed Jones']
+    ('Ed Jones',)
     {stop}ed
 
 ...or ``filter()``, which uses more flexible SQL expression language constructs.  These allow you to use regular Python operators with the class-level attributes on your mapped class:
@@ -452,7 +452,7 @@ and filtering results, which is accomplished either with :func:`~sqlalchemy.orm.
     ...    print name
     SELECT users.name AS users_name FROM users
     WHERE users.fullname = ?
-    ['Ed Jones']
+    ('Ed Jones',)
     {stop}ed
 
 The :class:`~sqlalchemy.orm.query.Query` object is fully *generative*, meaning that most method calls return a new :class:`~sqlalchemy.orm.query.Query` object upon which further criteria may be added.  For example, to query for users named "ed" with a full name of "Ed Jones", you can call ``filter()`` twice, which joins criteria using ``AND``:
@@ -464,7 +464,7 @@ The :class:`~sqlalchemy.orm.query.Query` object is fully *generative*, meaning t
     SELECT users.id AS users_id, users.name AS users_name, users.fullname AS users_fullname, users.password AS users_password
     FROM users
     WHERE users.name = ? AND users.fullname = ?
-    ['ed', 'Ed Jones']
+    ('ed', 'Ed Jones')
     {stop}<User('ed','Ed Jones', 'f8s7ccs')>
 
 
@@ -536,7 +536,7 @@ The :meth:`~sqlalchemy.orm.query.Query.all()`, :meth:`~sqlalchemy.orm.query.Quer
     SELECT users.id AS users_id, users.name AS users_name, users.fullname AS users_fullname, users.password AS users_password
     FROM users
     WHERE users.name LIKE ? ORDER BY users.id
-    ['%ed']
+    ('%ed',)
     {stop}[<User('ed','Ed Jones', 'f8s7ccs')>, <User('fred','Fred Flinstone', 'blah')>]
 
 :meth:`~sqlalchemy.orm.query.Query.first()` applies a limit of one and returns the first result as a scalar:
@@ -548,7 +548,7 @@ The :meth:`~sqlalchemy.orm.query.Query.all()`, :meth:`~sqlalchemy.orm.query.Quer
     FROM users
     WHERE users.name LIKE ? ORDER BY users.id
      LIMIT 1 OFFSET 0
-    ['%ed']
+    ('%ed',)
     {stop}<User('ed','Ed Jones', 'f8s7ccs')>
 
 :meth:`~sqlalchemy.orm.query.Query.one()`, fully fetches all rows, and if not exactly one object identity or composite row is present in the result, raises an error:
@@ -563,7 +563,7 @@ The :meth:`~sqlalchemy.orm.query.Query.all()`, :meth:`~sqlalchemy.orm.query.Quer
     SELECT users.id AS users_id, users.name AS users_name, users.fullname AS users_fullname, users.password AS users_password
     FROM users
     WHERE users.name LIKE ? ORDER BY users.id
-    ['%ed']
+    ('%ed',)
     {stop}Multiple rows were found for one()
 
 .. sourcecode:: python+sql
@@ -576,7 +576,7 @@ The :meth:`~sqlalchemy.orm.query.Query.all()`, :meth:`~sqlalchemy.orm.query.Quer
     SELECT users.id AS users_id, users.name AS users_name, users.fullname AS users_fullname, users.password AS users_password
     FROM users
     WHERE users.name LIKE ? AND users.id = ? ORDER BY users.id
-    ['%ed', 99]
+    ('%ed', 99)
     {stop}No row was found for one()
 
 Using Literal SQL
@@ -591,7 +591,7 @@ Literal strings can be used flexibly with :class:`~sqlalchemy.orm.query.Query`.
     SELECT users.id AS users_id, users.name AS users_name, users.fullname AS users_fullname, users.password AS users_password
     FROM users
     WHERE id<224 ORDER BY id
-    []
+    ()
     {stop}ed
     wendy
     mary
@@ -606,7 +606,7 @@ Bind parameters can be specified with string-based SQL, using a colon.  To speci
     SELECT users.id AS users_id, users.name AS users_name, users.fullname AS users_fullname, users.password AS users_password
     FROM users
     WHERE id<? and name=? ORDER BY users.id
-    [224, 'fred']
+    (224, 'fred')
     {stop}<User('fred','Fred Flinstone', 'blah')>
 
 To use an entirely string-based statement, using :meth:`~sqlalchemy.orm.query.Query.from_statement()`; just ensure that the columns clause of the statement contains the column names normally used by the mapper (below illustrated using an asterisk):
@@ -615,7 +615,7 @@ To use an entirely string-based statement, using :meth:`~sqlalchemy.orm.query.Qu
 
     {sql}>>> session.query(User).from_statement("SELECT * FROM users where name=:name").params(name='ed').all()
     SELECT * FROM users where name=?
-    ['ed']
+    ('ed',)
     {stop}[<User('ed','Ed Jones', 'f8s7ccs')>]
 
 You can use :meth:`~sqlalchemy.orm.query.Query.from_statement()` to go completely "raw", using string names to identify desired columns:
@@ -624,7 +624,7 @@ You can use :meth:`~sqlalchemy.orm.query.Query.from_statement()` to go completel
 
     {sql}>>> session.query("id", "name", "thenumber12").from_statement("SELECT id, name, 12 as thenumber12 FROM users where name=:name").params(name='ed').all()
     SELECT id, name, 12 as thenumber12 FROM users where name=?
-    ['ed']
+    ('ed',)
     {stop}[(1, u'ed', 12)]
 
 Counting
@@ -634,34 +634,34 @@ Counting
 
 .. sourcecode:: python+sql
 
-    {sql}>>> session.query(User).filter(User.name.like('%ed')).count()
+    {sql}>>> session.query(User).filter(User.name.like('%ed')).count() #doctest: +NORMALIZE_WHITESPACE
     SELECT count(1) AS count_1
     FROM users
     WHERE users.name LIKE ?
-    ['%ed']
+    ('%ed',)
     {stop}2
 
 The :meth:`~sqlalchemy.orm.query.Query.count()` method is used to determine how many rows the SQL statement would return, and is mainly intended to return a simple count of a single type of entity, in this case ``User``.   For more complicated sets of columns or entities where the "thing to be counted" needs to be indicated more specifically, :meth:`~sqlalchemy.orm.query.Query.count()` is probably not what you want.  Below, a query for individual columns does return the expected result:
 
 .. sourcecode:: python+sql
 
-    {sql}>>> session.query(User.id, User.name).filter(User.name.like('%ed')).count()
+    {sql}>>> session.query(User.id, User.name).filter(User.name.like('%ed')).count() #doctest: +NORMALIZE_WHITESPACE
     SELECT count(1) AS count_1
     FROM (SELECT users.id AS users_id, users.name AS users_name
     FROM users
     WHERE users.name LIKE ?) AS anon_1
-    ['%ed']
+    ('%ed',)
     {stop}2
 
 ...but if you look at the generated SQL, SQLAlchemy saw that we were placing individual column expressions and decided to wrap whatever it was we were doing in a subquery, so as to be assured that it returns the "number of rows".   This defensive behavior is not really needed here and in other cases is not what we want at all, such as if we wanted a grouping of counts per name:
 
 .. sourcecode:: python+sql
 
-    {sql}>>> session.query(User.name).group_by(User.name).count()
+    {sql}>>> session.query(User.name).group_by(User.name).count()  #doctest: +NORMALIZE_WHITESPACE
     SELECT count(1) AS count_1
     FROM (SELECT users.name AS users_name
     FROM users GROUP BY users.name) AS anon_1
-    []
+    ()
     {stop}4
 
 We don't want the number ``4``, we wanted some rows back.   So for detailed queries where you need to count something specific, use the ``func.count()`` function as a column expression:
@@ -669,10 +669,10 @@ We don't want the number ``4``, we wanted some rows back.   So for detailed quer
 .. sourcecode:: python+sql
 
     >>> from sqlalchemy import func
-    {sql}>>> session.query(func.count(User.name), User.name).group_by(User.name).all()
+    {sql}>>> session.query(func.count(User.name), User.name).group_by(User.name).all()  #doctest: +NORMALIZE_WHITESPACE
     SELECT count(users.name) AS count_1, users.name AS users_name
     FROM users GROUP BY users.name
-    {stop}[]
+    {stop}()
     [(1, u'ed'), (1, u'fred'), (1, u'mary'), (1, u'wendy')]
 
 Building a Relation
@@ -773,11 +773,11 @@ Let's add and commit ``Jack Bean`` to the database.  ``jack`` as well as the two
     >>> session.add(jack)
     {sql}>>> session.commit()
     INSERT INTO users (name, fullname, password) VALUES (?, ?, ?)
-    ['jack', 'Jack Bean', 'gjffdd']
+    ('jack', 'Jack Bean', 'gjffdd')
     INSERT INTO addresses (email_address, user_id) VALUES (?, ?)
-    ['jack@google.com', 5]
+    ('jack@google.com', 5)
     INSERT INTO addresses (email_address, user_id) VALUES (?, ?)
-    ['j25@yahoo.com', 5]
+    ('j25@yahoo.com', 5)
     COMMIT
 
 Querying for Jack, we get just Jack back.  No SQL is yet issued for Jack's addresses:
@@ -789,7 +789,7 @@ Querying for Jack, we get just Jack back.  No SQL is yet issued for Jack's addre
     SELECT users.id AS users_id, users.name AS users_name, users.fullname AS users_fullname, users.password AS users_password
     FROM users
     WHERE users.name = ?
-    ['jack']
+    ('jack',)
 
     {stop}>>> jack
     <User('jack','Jack Bean', 'gjffdd')>
@@ -802,7 +802,7 @@ Let's look at the ``addresses`` collection.  Watch the SQL:
     SELECT addresses.id AS addresses_id, addresses.email_address AS addresses_email_address, addresses.user_id AS addresses_user_id
     FROM addresses
     WHERE ? = addresses.user_id ORDER BY addresses.id
-    [5]
+    (5,)
     {stop}[<Address('jack@google.com')>, <Address('j25@yahoo.com')>]
 
 When we accessed the ``addresses`` collection, SQL was suddenly issued.  This is an example of a **lazy loading relation**.  The ``addresses`` collection is now loaded and behaves just like an ordinary list.
@@ -819,7 +819,7 @@ If you want to reduce the number of queries (dramatically, in many cases), we ca
     AS addresses_1_email_address, addresses_1.user_id AS addresses_1_user_id
     FROM users LEFT OUTER JOIN addresses AS addresses_1 ON users.id = addresses_1.user_id
     WHERE users.name = ? ORDER BY addresses_1.id
-    ['jack']
+    ('jack',)
 
     {stop}>>> jack
     <User('jack','Jack Bean', 'gjffdd')>
@@ -844,7 +844,7 @@ While the eager load created a JOIN specifically to populate a collection, we ca
     addresses.email_address AS addresses_email_address, addresses.user_id AS addresses_user_id
     FROM users, addresses
     WHERE users.id = addresses.user_id AND addresses.email_address = ?
-    ['jack@google.com']
+    ('jack@google.com',)
     {stop}<User('jack','Jack Bean', 'gjffdd')> <Address('jack@google.com')>
 
 Or we can make a real JOIN construct; one way to do so is to use the ORM :func:`~sqlalchemy.orm.join()` function, and tell :class:`~sqlalchemy.orm.query.Query` to "select from" this join:
@@ -857,7 +857,7 @@ Or we can make a real JOIN construct; one way to do so is to use the ORM :func:`
     SELECT users.id AS users_id, users.name AS users_name, users.fullname AS users_fullname, users.password AS users_password
     FROM users JOIN addresses ON users.id = addresses.user_id
     WHERE addresses.email_address = ?
-    ['jack@google.com']
+    ('jack@google.com',)
     {stop}[<User('jack','Jack Bean', 'gjffdd')>]
 
 :func:`~sqlalchemy.orm.join()` knows how to join between ``User`` and ``Address`` because there's only one foreign key between them.  If there were no foreign keys, or several, :func:`~sqlalchemy.orm.join()` would require a third argument indicating the ON clause of the join, in one of the following forms:
@@ -877,7 +877,7 @@ The functionality of :func:`~sqlalchemy.orm.join()` is also available generative
     SELECT users.id AS users_id, users.name AS users_name, users.fullname AS users_fullname, users.password AS users_password
     FROM users JOIN addresses ON users.id = addresses.user_id
     WHERE addresses.email_address = ?
-    ['jack@google.com']
+    ('jack@google.com',)
     {stop}[<User('jack','Jack Bean', 'gjffdd')>]
 
 To explicitly specify the target of the join, use tuples to form an argument list similar to the standalone join.  This becomes more important when using aliases and similar constructs:
@@ -915,7 +915,7 @@ When querying across multiple tables, if the same table needs to be referenced m
     FROM users JOIN addresses AS addresses_1 ON users.id = addresses_1.user_id
     JOIN addresses AS addresses_2 ON users.id = addresses_2.user_id
     WHERE addresses_1.email_address = ? AND addresses_2.email_address = ?
-    ['jack@google.com', 'j25@yahoo.com']
+    ('jack@google.com', 'j25@yahoo.com')
     {stop}jack jack@google.com j25@yahoo.com
 
 Using Subqueries
@@ -947,7 +947,7 @@ Once we have our statement, it behaves like a :class:`~sqlalchemy.schema.Table`
     FROM users LEFT OUTER JOIN (SELECT addresses.user_id AS user_id, count(?) AS address_count
     FROM addresses GROUP BY addresses.user_id) AS anon_1 ON users.id = anon_1.user_id
     ORDER BY users.id
-    ['*']
+    ('*',)
     {stop}<User('ed','Ed Jones', 'f8s7ccs')> None
     <User('wendy','Wendy Williams', 'foobar')> None
     <User('mary','Mary Contrary', 'xxg527')> None
@@ -971,7 +971,7 @@ Above, we just selected a result that included a column from a subquery.  What i
     FROM users JOIN (SELECT addresses.id AS id, addresses.email_address AS email_address, addresses.user_id AS user_id
     FROM addresses
     WHERE addresses.email_address != ?) AS anon_1 ON users.id = anon_1.user_id
-    ['j25@yahoo.com']
+    ('j25@yahoo.com',)
     {stop}<User('jack','Jack Bean', 'gjffdd')> <Address('jack@google.com')>
 
 Using EXISTS
@@ -992,7 +992,7 @@ There is an explicit EXISTS construct, which looks like this:
     WHERE EXISTS (SELECT *
     FROM addresses
     WHERE addresses.user_id = users.id)
-    []
+    ()
     {stop}jack
 
 The :class:`~sqlalchemy.orm.query.Query` features several operators which make usage of EXISTS automatically.  Above, the statement can be expressed along the ``User.addresses`` relation using ``any()``:
@@ -1006,7 +1006,7 @@ The :class:`~sqlalchemy.orm.query.Query` features several operators which make u
     WHERE EXISTS (SELECT 1
     FROM addresses
     WHERE users.id = addresses.user_id)
-    []
+    ()
     {stop}jack
 
 ``any()`` takes criterion as well, to limit the rows matched:
@@ -1021,7 +1021,7 @@ The :class:`~sqlalchemy.orm.query.Query` features several operators which make u
     WHERE EXISTS (SELECT 1
     FROM addresses
     WHERE users.id = addresses.user_id AND addresses.email_address LIKE ?)
-    ['%google%']
+    ('%google%',)
     {stop}jack
 
 ``has()`` is the same operator as ``any()`` for many-to-one relations (note the ``~`` operator here too, which means "NOT"):
@@ -1035,7 +1035,7 @@ The :class:`~sqlalchemy.orm.query.Query` features several operators which make u
     WHERE NOT (EXISTS (SELECT 1
     FROM users
     WHERE users.id = addresses.user_id AND users.name = ?))
-    ['jack']
+    ('jack',)
     {stop}[]
 
 Common Relation Operators
@@ -1084,15 +1084,15 @@ Let's try to delete ``jack`` and see how that goes.  We'll mark as deleted in th
     >>> session.delete(jack)
     {sql}>>> session.query(User).filter_by(name='jack').count() # doctest: +NORMALIZE_WHITESPACE
     UPDATE addresses SET user_id=? WHERE addresses.id = ?
-    [None, 1]
+    (None, 1)
     UPDATE addresses SET user_id=? WHERE addresses.id = ?
-    [None, 2]
+    (None, 2)
     DELETE FROM users WHERE users.id = ?
-    [5]
+    (5,)
     SELECT count(1) AS count_1
     FROM users
     WHERE users.name = ?
-    ['jack']
+    ('jack',)
     {stop}0
 
 So far, so good.  How about Jack's ``Address`` objects ?
@@ -1105,7 +1105,7 @@ So far, so good.  How about Jack's ``Address`` objects ?
     SELECT count(1) AS count_1
     FROM addresses
     WHERE addresses.email_address IN (?, ?)
-    ['jack@google.com', 'j25@yahoo.com']
+    ('jack@google.com', 'j25@yahoo.com')
     {stop}2
 
 Uh oh, they're still there !  Analyzing the flush SQL, we can see that the ``user_id`` column of each address was set to NULL, but the rows weren't deleted.  SQLAlchemy doesn't assume that deletes cascade, you have to tell it to do so.
@@ -1146,7 +1146,7 @@ Now when we load Jack (below using ``get()``, which loads by primary key), remov
     SELECT users.id AS users_id, users.name AS users_name, users.fullname AS users_fullname, users.password AS users_password
     FROM users
     WHERE users.id = ?
-    [5]
+    (5,)
     {stop}
 
     # remove one Address (lazy load fires off)
@@ -1154,7 +1154,7 @@ Now when we load Jack (below using ``get()``, which loads by primary key), remov
     SELECT addresses.id AS addresses_id, addresses.email_address AS addresses_email_address, addresses.user_id AS addresses_user_id
     FROM addresses
     WHERE ? = addresses.user_id
-    [5]
+    (5,)
     {stop}
 
     # only one address remains
@@ -1162,11 +1162,11 @@ Now when we load Jack (below using ``get()``, which loads by primary key), remov
     ...     Address.email_address.in_(['jack@google.com', 'j25@yahoo.com'])
     ... ).count() # doctest: +NORMALIZE_WHITESPACE
     DELETE FROM addresses WHERE addresses.id = ?
-    [2]
+    (2,)
     SELECT count(1) AS count_1
     FROM addresses
     WHERE addresses.email_address IN (?, ?)
-    ['jack@google.com', 'j25@yahoo.com']
+    ('jack@google.com', 'j25@yahoo.com')
     {stop}1
 
 Deleting Jack will delete both Jack and his remaining ``Address``:
@@ -1177,13 +1177,13 @@ Deleting Jack will delete both Jack and his remaining ``Address``:
 
     {sql}>>> session.query(User).filter_by(name='jack').count() # doctest: +NORMALIZE_WHITESPACE
     DELETE FROM addresses WHERE addresses.id = ?
-    [1]
+    (1,)
     DELETE FROM users WHERE users.id = ?
-    [5]
+    (5,)
     SELECT count(1) AS count_1
     FROM users
     WHERE users.name = ?
-    ['jack']
+    ('jack',)
     {stop}0
 
     {sql}>>> session.query(Address).filter(
@@ -1192,7 +1192,7 @@ Deleting Jack will delete both Jack and his remaining ``Address``:
     SELECT count(1) AS count_1
     FROM addresses
     WHERE addresses.email_address IN (?, ?)
-    ['jack@google.com', 'j25@yahoo.com']
+    ('jack@google.com', 'j25@yahoo.com')
     {stop}0
 
 Building a Many To Many Relation
@@ -1302,7 +1302,7 @@ Usage is not too different from what we've been doing.  Let's give Wendy some bl
     SELECT users.id AS users_id, users.name AS users_name, users.fullname AS users_fullname, users.password AS users_password
     FROM users
     WHERE users.name = ?
-    ['wendy']
+    ('wendy',)
     {stop}
     >>> post = BlogPost("Wendy's Blog Post", "This is a test", wendy)
     >>> session.add(post)
@@ -1320,19 +1320,19 @@ We can now look up all blog posts with the keyword 'firstpost'.   We'll use the
 
     {sql}>>> session.query(BlogPost).filter(BlogPost.keywords.any(keyword='firstpost')).all() #doctest: +NORMALIZE_WHITESPACE
     INSERT INTO keywords (keyword) VALUES (?)
-    ['wendy']
+    ('wendy',)
     INSERT INTO keywords (keyword) VALUES (?)
-    ['firstpost']
+    ('firstpost',)
     INSERT INTO posts (user_id, headline, body) VALUES (?, ?, ?)
-    [2, "Wendy's Blog Post", 'This is a test']
+    (2, "Wendy's Blog Post", 'This is a test')
     INSERT INTO post_keywords (post_id, keyword_id) VALUES (?, ?)
-    [[1, 1], [1, 2]]
+    ((1, 1), (1, 2))
     SELECT posts.id AS posts_id, posts.user_id AS posts_user_id, posts.headline AS posts_headline, posts.body AS posts_body
     FROM posts
     WHERE EXISTS (SELECT 1
     FROM post_keywords, keywords
     WHERE posts.id = post_keywords.post_id AND keywords.id = post_keywords.keyword_id AND keywords.keyword = ?)
-    ['firstpost']
+    ('firstpost',)
     {stop}[BlogPost("Wendy's Blog Post", 'This is a test', <User('wendy','Wendy Williams', 'foobar')>)]
 
 If we want to look up just Wendy's posts, we can tell the query to narrow down to her as a parent:
@@ -1346,7 +1346,7 @@ If we want to look up just Wendy's posts, we can tell the query to narrow down t
     WHERE ? = posts.user_id AND (EXISTS (SELECT 1
     FROM post_keywords, keywords
     WHERE posts.id = post_keywords.post_id AND keywords.id = post_keywords.keyword_id AND keywords.keyword = ?))
-    [2, 'firstpost']
+    (2, 'firstpost')
     {stop}[BlogPost("Wendy's Blog Post", 'This is a test', <User('wendy','Wendy Williams', 'foobar')>)]
 
 Or we can use Wendy's own ``posts`` relation, which is a "dynamic" relation, to query straight from there:
@@ -1359,7 +1359,7 @@ Or we can use Wendy's own ``posts`` relation, which is a "dynamic" relation, to
     WHERE ? = posts.user_id AND (EXISTS (SELECT 1
     FROM post_keywords, keywords
     WHERE posts.id = post_keywords.post_id AND keywords.id = post_keywords.keyword_id AND keywords.keyword = ?))
-    [2, 'firstpost']
+    (2, 'firstpost')
     {stop}[BlogPost("Wendy's Blog Post", 'This is a test', <User('wendy','Wendy Williams', 'foobar')>)]
 
 Further Reference
index d2d522ba7b8279165e2ac6629bc7985dc8c60e43..12f716a7ba26ccd7ff9150d0a47faf0887a2b403 100644 (file)
@@ -151,7 +151,7 @@ The :class:`~sqlalchemy.engine.base.Connection` object represents an actively ch
 
     >>> result = conn.execute(ins)
     {opensql}INSERT INTO users (name, fullname) VALUES (?, ?)
-    ['jack', 'Jack Jones']
+    ('jack', 'Jack Jones')
     COMMIT
 
 So the INSERT statement was now issued to the database.  Although we got positional "qmark" bind parameters instead of "named" bind parameters in the output.  How come ?  Because when executed, the :class:`~sqlalchemy.engine.base.Connection` used the SQLite **dialect** to help generate the statement; when we use the ``str()`` function, the statement isn't aware of this dialect, and falls back onto a default which uses named parameters. We can view this manually as follows:
@@ -182,7 +182,7 @@ Our insert example above was intentionally a little drawn out to show some vario
     >>> ins = users.insert()
     >>> conn.execute(ins, id=2, name='wendy', fullname='Wendy Williams') # doctest: +ELLIPSIS
     {opensql}INSERT INTO users (id, name, fullname) VALUES (?, ?, ?)
-    [2, 'wendy', 'Wendy Williams']
+    (2, 'wendy', 'Wendy Williams')
     COMMIT
     {stop}<sqlalchemy.engine.base.ResultProxy object at 0x...>
 
@@ -199,7 +199,7 @@ To issue many inserts using DBAPI's ``executemany()`` method, we can send in a l
     ...    {'user_id': 2, 'email_address' : 'wendy@aol.com'},
     ... ])
     {opensql}INSERT INTO addresses (user_id, email_address) VALUES (?, ?)
-    [[1, 'jack@yahoo.com'], [1, 'jack@msn.com'], [2, 'www@www.org'], [2, 'wendy@aol.com']]
+    ((1, 'jack@yahoo.com'), (1, 'jack@msn.com'), (2, 'www@www.org'), (2, 'wendy@aol.com'))
     COMMIT
     {stop}<sqlalchemy.engine.base.ResultProxy object at 0x...>
 
@@ -217,7 +217,7 @@ We're executing our :class:`~sqlalchemy.sql.expression.Insert` using a :class:`~
 
     {sql}>>> result = engine.execute(users.insert(), name='fred', fullname="Fred Flintstone")
     INSERT INTO users (name, fullname) VALUES (?, ?)
-    ['fred', 'Fred Flintstone']
+    ('fred', 'Fred Flintstone')
     COMMIT
 
 and you can save even more steps than that, if you connect the :class:`~sqlalchemy.engine.base.Engine` to the :class:`~sqlalchemy.schema.MetaData` object we created earlier.  When this is done, all SQL expressions which involve tables within the :class:`~sqlalchemy.schema.MetaData` object will be automatically **bound** to the :class:`~sqlalchemy.engine.base.Engine`.  In this case, we call it  **implicit execution**:
@@ -227,7 +227,7 @@ and you can save even more steps than that, if you connect the :class:`~sqlalche
     >>> metadata.bind = engine
     {sql}>>> result = users.insert().execute(name="mary", fullname="Mary Contrary")
     INSERT INTO users (name, fullname) VALUES (?, ?)
-    ['mary', 'Mary Contrary']
+    ('mary', 'Mary Contrary')
     COMMIT
 
 When the :class:`~sqlalchemy.schema.MetaData` is bound, statements will also compile against the engine's dialect.  Since a lot of the examples here assume the default dialect, we'll detach the engine from the metadata which we just attached:
@@ -248,10 +248,10 @@ We began with inserts just so that our test database had some data in it.  The m
 
     >>> from sqlalchemy.sql import select
     >>> s = select([users])
-    >>> result = conn.execute(s)
+    >>> result = conn.execute(s)  # doctest: +NORMALIZE_WHITESPACE
     {opensql}SELECT users.id, users.name, users.fullname
     FROM users
-    []
+    ()
 
 Above, we issued a basic ``select()`` call, placing the ``users`` table within the COLUMNS clause of the select, and then executing.  SQLAlchemy expanded the ``users`` table into the set of each of its columns, and also generated a FROM clause for us.  The result returned is again a :class:`~sqlalchemy.engine.base.ResultProxy` object, which acts much like a DBAPI cursor, including methods such as :func:`~sqlalchemy.engine.base.ResultProxy.fetchone` and :func:`~sqlalchemy.engine.base.ResultProxy.fetchall`.  The easiest way to get rows from it is to just iterate:
 
@@ -268,10 +268,10 @@ Above, we see that printing each row produces a simple tuple-like result.  We ha
 
 .. sourcecode:: pycon+sql
 
-    {sql}>>> result = conn.execute(s)
+    {sql}>>> result = conn.execute(s)  # doctest: +NORMALIZE_WHITESPACE
     SELECT users.id, users.name, users.fullname
     FROM users
-    []
+    ()
 
     {stop}>>> row = result.fetchone()
     >>> print "name:", row['name'], "; fullname:", row['fullname']
@@ -289,11 +289,11 @@ But another way, whose usefulness will become apparent later on, is to use the :
 
 .. sourcecode:: pycon+sql
 
-    {sql}>>> for row in conn.execute(s):
+    {sql}>>> for row in conn.execute(s):  # doctest: +NORMALIZE_WHITESPACE
     ...     print "name:", row[users.c.name], "; fullname:", row[users.c.fullname]
     SELECT users.id, users.name, users.fullname
     FROM users
-    []
+    ()
     {stop}name: jack ; fullname: Jack Jones
     name: wendy ; fullname: Wendy Williams
     name: fred ; fullname: Fred Flintstone
@@ -310,10 +310,10 @@ If we'd like to more carefully control the columns which are placed in the COLUM
 .. sourcecode:: pycon+sql
 
     >>> s = select([users.c.name, users.c.fullname])
-    {sql}>>> result = conn.execute(s)
+    {sql}>>> result = conn.execute(s)  # doctest: +NORMALIZE_WHITESPACE
     SELECT users.name, users.fullname
     FROM users
-    []
+    ()
     {stop}>>> for row in result:  #doctest: +NORMALIZE_WHITESPACE
     ...     print row
     (u'jack', u'Jack Jones')
@@ -326,10 +326,10 @@ Lets observe something interesting about the FROM clause.  Whereas the generated
 .. sourcecode:: pycon+sql
 
     {sql}>>> for row in conn.execute(select([users, addresses])):
-    ...     print row
+    ...     print row  # doctest: +NORMALIZE_WHITESPACE
     SELECT users.id, users.name, users.fullname, addresses.id, addresses.user_id, addresses.email_address
     FROM users, addresses
-    []
+    ()
     {stop}(1, u'jack', u'Jack Jones', 1, 1, u'jack@yahoo.com')
     (1, u'jack', u'Jack Jones', 2, 1, u'jack@msn.com')
     (1, u'jack', u'Jack Jones', 3, 2, u'www@www.org')
@@ -353,11 +353,11 @@ It placed **both** tables into the FROM clause.  But also, it made a real mess.
 
     >>> s = select([users, addresses], users.c.id==addresses.c.user_id)
     {sql}>>> for row in conn.execute(s):
-    ...     print row
+    ...     print row  # doctest: +NORMALIZE_WHITESPACE
     SELECT users.id, users.name, users.fullname, addresses.id, addresses.user_id, addresses.email_address
     FROM users, addresses
     WHERE users.id = addresses.user_id
-    []
+    ()
     {stop}(1, u'jack', u'Jack Jones', 1, 1, u'jack@yahoo.com')
     (1, u'jack', u'Jack Jones', 2, 1, u'jack@msn.com')
     (2, u'wendy', u'Wendy Williams', 3, 2, u'www@www.org')
@@ -501,7 +501,7 @@ So with all of this vocabulary, let's select all users who have an email address
     FROM users, addresses
     WHERE users.id = addresses.user_id AND users.name BETWEEN ? AND ? AND
     (addresses.email_address LIKE ? OR addresses.email_address LIKE ?)
-    [', ', 'm', 'z', '%@aol.com', '%@msn.com']
+    (', ', 'm', 'z', '%@aol.com', '%@msn.com')
     [(u'Wendy Williams, wendy@aol.com',)]
 
 Once again, SQLAlchemy figured out the FROM clause for our statement.  In fact it will determine the FROM clause based on all of its other bits; the columns clause, the where clause, and also some other elements which we haven't covered yet, which include ORDER BY, GROUP BY, and HAVING.
@@ -526,7 +526,7 @@ Our last example really became a handful to type.  Going from what one understan
     FROM users, addresses
     WHERE users.id = addresses.user_id AND users.name BETWEEN ? AND ? AND
     (addresses.email_address LIKE ? OR addresses.email_address LIKE ?)
-    ['m', 'z', '%@aol.com', '%@msn.com']
+    ('m', 'z', '%@aol.com', '%@msn.com')
     {stop}[(u'Wendy Williams, wendy@aol.com',)]
 
 To gain a "hybrid" approach, the `select()` construct accepts strings for most of its arguments.  Below we combine the usage of strings with our constructed ``select()`` object, by using the ``select()`` object to structure the statement, and strings to provide all the content within the structure.  For this example, SQLAlchemy is not given any :class:`~sqlalchemy.schema.Column` or :class:`~sqlalchemy.schema.Table` objects in any of its expressions, so it cannot generate a FROM clause.  So we also give it the ``from_obj`` keyword argument, which is a list of ``ClauseElements`` (or strings) to be placed within the FROM clause:
@@ -545,7 +545,7 @@ To gain a "hybrid" approach, the `select()` construct accepts strings for most o
     SELECT users.fullname || ', ' || addresses.email_address AS title
     FROM users, addresses
     WHERE users.id = addresses.user_id AND users.name BETWEEN 'm' AND 'z' AND (addresses.email_address LIKE ? OR addresses.email_address LIKE ?)
-    ['%@aol.com', '%@msn.com']
+    ('%@aol.com', '%@msn.com')
     {stop}[(u'Wendy Williams, wendy@aol.com',)]
 
 Going from constructed SQL to text, we lose some capabilities.  We lose the capability for SQLAlchemy to compile our expression to a specific target database; above, our expression won't work with MySQL since it has no ``||`` construct.  It also becomes more tedious for SQLAlchemy to be made aware of the datatypes in use; for example, if our bind parameters required UTF-8 encoding before going in, or conversion from a Python ``datetime`` into a string (as is required with SQLite), we would have to add extra information to our ``text()`` construct.  Similar issues arise on the result set side, where SQLAlchemy also performs type-specific data conversion in some cases; still more information can be added to ``text()`` to work around this.  But what we really lose from our statement is the ability to manipulate it, transform it, and analyze it.  These features are critical when using the ORM, which makes heavy usage of relational transformations.  To show off what we mean, we'll first introduce the ALIAS construct and the JOIN construct, just so we have some juicier bits to play with.
@@ -565,11 +565,11 @@ The alias corresponds to a "renamed" version of a table or arbitrary relation, w
     ...        a1.c.email_address=='jack@msn.com',
     ...        a2.c.email_address=='jack@yahoo.com'
     ...   ))
-    {sql}>>> print conn.execute(s).fetchall()
+    {sql}>>> print conn.execute(s).fetchall()  # doctest: +NORMALIZE_WHITESPACE
     SELECT users.id, users.name, users.fullname
     FROM users, addresses AS a1, addresses AS a2
     WHERE users.id = a1.user_id AND users.id = a2.user_id AND a1.email_address = ? AND a2.email_address = ?
-    ['jack@msn.com', 'jack@yahoo.com']
+    ('jack@msn.com', 'jack@yahoo.com')
     {stop}[(1, u'jack', u'Jack Jones')]
 
 Easy enough.  One thing that we're going for with the SQL Expression Language is the melding of programmatic behavior with SQL generation.  Coming up with names like ``a1`` and ``a2`` is messy; we really didn't need to use those names anywhere, it's just the database that needed them.  Plus, we might write some code that uses alias objects that came from several different places, and it's difficult to ensure that they all have unique names.  So instead, we just let SQLAlchemy make the names for us, using "anonymous" aliases:
@@ -584,11 +584,11 @@ Easy enough.  One thing that we're going for with the SQL Expression Language is
     ...        a1.c.email_address=='jack@msn.com',
     ...        a2.c.email_address=='jack@yahoo.com'
     ...   ))
-    {sql}>>> print conn.execute(s).fetchall()
+    {sql}>>> print conn.execute(s).fetchall()  # doctest: +NORMALIZE_WHITESPACE
     SELECT users.id, users.name, users.fullname
     FROM users, addresses AS addresses_1, addresses AS addresses_2
     WHERE users.id = addresses_1.user_id AND users.id = addresses_2.user_id AND addresses_1.email_address = ? AND addresses_2.email_address = ?
-    ['jack@msn.com', 'jack@yahoo.com']
+    ('jack@msn.com', 'jack@yahoo.com')
     {stop}[(1, u'jack', u'Jack Jones')]
 
 One super-huge advantage of anonymous aliases is that not only did we not have to guess up a random name, but we can also be guaranteed that the above SQL string is **deterministically** generated to be the same every time.  This is important for databases such as Oracle which cache compiled "query plans" for their statements, and need to see the same SQL string in order to make use of it.
@@ -599,13 +599,13 @@ Aliases can of course be used for anything which you can SELECT from, including
 
     >>> a1 = s.correlate(None).alias()
     >>> s = select([users.c.name], users.c.id==a1.c.id)
-    {sql}>>> print conn.execute(s).fetchall()
+    {sql}>>> print conn.execute(s).fetchall()  # doctest: +NORMALIZE_WHITESPACE
     SELECT users.name
     FROM users, (SELECT users.id AS id, users.name AS name, users.fullname AS fullname
     FROM users, addresses AS addresses_1, addresses AS addresses_2
     WHERE users.id = addresses_1.user_id AND users.id = addresses_2.user_id AND addresses_1.email_address = ? AND addresses_2.email_address = ?) AS anon_1
     WHERE users.id = anon_1.id
-    ['jack@msn.com', 'jack@yahoo.com']
+    ('jack@msn.com', 'jack@yahoo.com')
     {stop}[(u'jack',)]
 
 Using Joins
@@ -635,10 +635,10 @@ When we create a ``select()`` construct, SQLAlchemy looks around at the tables w
     >>> s = select([users.c.fullname], from_obj=[
     ...    users.join(addresses, addresses.c.email_address.like(users.c.name + '%'))
     ...    ])
-    {sql}>>> print conn.execute(s).fetchall()
+    {sql}>>> print conn.execute(s).fetchall()  # doctest: +NORMALIZE_WHITESPACE
     SELECT users.fullname
     FROM users JOIN addresses ON addresses.email_address LIKE users.name || ?
-    ['%']
+    ('%',)
     {stop}[(u'Jack Jones',), (u'Jack Jones',), (u'Wendy Williams',)]
 
 The ``outerjoin()`` function just creates ``LEFT OUTER JOIN`` constructs.  It's used just like ``join()``:
@@ -646,7 +646,7 @@ The ``outerjoin()`` function just creates ``LEFT OUTER JOIN`` constructs.  It's
 .. sourcecode:: pycon+sql
 
     >>> s = select([users.c.fullname], from_obj=[users.outerjoin(addresses)])
-    >>> print s
+    >>> print s  # doctest: +NORMALIZE_WHITESPACE
     SELECT users.fullname
     FROM users LEFT OUTER JOIN addresses ON users.id = addresses.user_id
 
@@ -655,7 +655,7 @@ That's the output ``outerjoin()`` produces, unless, of course, you're stuck in a
 .. sourcecode:: pycon+sql
 
     >>> from sqlalchemy.dialects.oracle import dialect as OracleDialect
-    >>> print s.compile(dialect=OracleDialect(use_ansi=False))
+    >>> print s.compile(dialect=OracleDialect(use_ansi=False))  # doctest: +NORMALIZE_WHITESPACE
     SELECT users.fullname
     FROM users, addresses
     WHERE users.id = addresses.user_id(+)
@@ -673,7 +673,7 @@ To support this, the ``select()`` construct we've been working with supports pie
 .. sourcecode:: pycon+sql
 
     >>> query = users.select()
-    >>> print query
+    >>> print query  # doctest: +NORMALIZE_WHITESPACE
     SELECT users.id, users.name, users.fullname
     FROM users
 
@@ -709,13 +709,13 @@ Let's bake for .0001 seconds and see what rises:
 
 .. sourcecode:: pycon+sql
 
-    >>> conn.execute(query).fetchall()
+    >>> conn.execute(query).fetchall()  # doctest: +NORMALIZE_WHITESPACE
     {opensql}SELECT users.id AS users_id, users.name AS users_name, users.fullname AS users_fullname, addresses.id AS addresses_id, addresses.user_id AS addresses_user_id, addresses.email_address AS addresses_email_address
     FROM users LEFT OUTER JOIN addresses ON users.id = addresses.user_id
     WHERE users.name = ? AND (EXISTS (SELECT addresses.id
     FROM addresses
     WHERE addresses.user_id = users.id AND addresses.email_address LIKE ?)) ORDER BY users.fullname DESC
-    ['jack', '%@msn.com']
+    ('jack', '%@msn.com')
     {stop}[(1, u'jack', u'Jack Jones', 1, 1, u'jack@yahoo.com'), (1, u'jack', u'Jack Jones', 2, 1, u'jack@msn.com')]
 
 So we started small, added one little thing at a time, and at the end we have a huge statement..which actually works.  Now let's do one more thing; the searching function wants to add another ``email_address`` criterion on, however it doesn't want to construct an alias of the ``addresses`` table; suppose many parts of the application are written to deal specifically with the ``addresses`` table, and to change all those functions to support receiving an arbitrary alias of the address would be cumbersome.  We can actually *convert* the ``addresses`` table within the *existing* statement to be an alias of itself, using :func:`~sqlalchemy.sql.expression.FromClause.replace_selectable`:
@@ -724,7 +724,7 @@ So we started small, added one little thing at a time, and at the end we have a
 
     >>> a1 = addresses.alias()
     >>> query = query.replace_selectable(addresses, a1)
-    >>> print query
+    >>> print query  # doctest: +NORMALIZE_WHITESPACE
     {opensql}SELECT users.id AS users_id, users.name AS users_name, users.fullname AS users_fullname, addresses_1.id AS addresses_1_id, addresses_1.user_id AS addresses_1_user_id, addresses_1.email_address AS addresses_1_email_address
     FROM users LEFT OUTER JOIN addresses AS addresses_1 ON users.id = addresses_1.user_id
     WHERE users.name = :name_1 AND (EXISTS (SELECT addresses_1.id
@@ -736,13 +736,13 @@ One more thing though, with automatic labeling applied as well as anonymous alia
 .. sourcecode:: pycon+sql
 
     {sql}>>> for row in conn.execute(query):
-    ...     print "Name:", row[users.c.name], "; Email Address", row[a1.c.email_address]
+    ...     print "Name:", row[users.c.name], "; Email Address", row[a1.c.email_address]  # doctest: +NORMALIZE_WHITESPACE
     SELECT users.id AS users_id, users.name AS users_name, users.fullname AS users_fullname, addresses_1.id AS addresses_1_id, addresses_1.user_id AS addresses_1_user_id, addresses_1.email_address AS addresses_1_email_address
     FROM users LEFT OUTER JOIN addresses AS addresses_1 ON users.id = addresses_1.user_id
     WHERE users.name = ? AND (EXISTS (SELECT addresses_1.id
     FROM addresses AS addresses_1
     WHERE addresses_1.user_id = users.id AND addresses_1.email_address LIKE ?)) ORDER BY users.fullname DESC
-    ['jack', '%@msn.com']
+    ('jack', '%@msn.com')
     {stop}Name: jack ; Email Address jack@yahoo.com
     Name: jack ; Email Address jack@msn.com
 
@@ -763,11 +763,11 @@ Throughout all these examples, SQLAlchemy is busy creating bind parameters where
 
     >>> from sqlalchemy.sql import bindparam
     >>> s = users.select(users.c.name==bindparam('username'))
-    {sql}>>> conn.execute(s, username='wendy').fetchall()
+    {sql}>>> conn.execute(s, username='wendy').fetchall() # doctest: +NORMALIZE_WHITESPACE
     SELECT users.id, users.name, users.fullname
     FROM users
     WHERE users.name = ?
-    ['wendy']
+    ('wendy',)
     {stop}[(2, u'wendy', u'Wendy Williams')]
 
 Another important aspect of bind parameters is that they may be assigned a type.  The type of the bind parameter will determine its behavior within expressions and also how the data bound to it is processed before being sent off to the database:
@@ -775,11 +775,11 @@ Another important aspect of bind parameters is that they may be assigned a type.
 .. sourcecode:: pycon+sql
 
     >>> s = users.select(users.c.name.like(bindparam('username', type_=String) + text("'%'")))
-    {sql}>>> conn.execute(s, username='wendy').fetchall()
+    {sql}>>> conn.execute(s, username='wendy').fetchall() # doctest: +NORMALIZE_WHITESPACE
     SELECT users.id, users.name, users.fullname
     FROM users
     WHERE users.name LIKE ? || '%'
-    ['wendy']
+    ('wendy',)
     {stop}[(2, u'wendy', u'Wendy Williams')]
 
 
@@ -791,11 +791,11 @@ Bind parameters of the same name can also be used multiple times, where only a s
     ...    users.c.name.like(bindparam('name', type_=String) + text("'%'")) |
     ...    addresses.c.email_address.like(bindparam('name', type_=String) + text("'@%'")),
     ...    from_obj=[users.outerjoin(addresses)])
-    {sql}>>> conn.execute(s, name='jack').fetchall()
+    {sql}>>> conn.execute(s, name='jack').fetchall() # doctest: +NORMALIZE_WHITESPACE
     SELECT users.id, users.name, users.fullname, addresses.id, addresses.user_id, addresses.email_address
     FROM users LEFT OUTER JOIN addresses ON users.id = addresses.user_id
     WHERE users.name LIKE ? || '%' OR addresses.email_address LIKE ? || '@%'
-    ['jack', 'jack']
+    ('jack', 'jack')
     {stop}[(1, u'jack', u'Jack Jones', 1, 1, u'jack@yahoo.com'), (1, u'jack', u'Jack Jones', 2, 1, u'jack@msn.com')]
 
 Functions
@@ -831,10 +831,10 @@ Functions are most typically used in the columns clause of a select statement, a
 
     >>> print conn.execute(
     ...     select([func.max(addresses.c.email_address, type_=String).label('maxemail')])
-    ... ).scalar()
+    ... ).scalar() # doctest: +NORMALIZE_WHITESPACE
     {opensql}SELECT max(addresses.email_address) AS maxemail
     FROM addresses
-    []
+    ()
     {stop}www@www.org
 
 Databases such as PostgreSQL and Oracle which support functions that return whole result sets can be assembled into selectable units, which can be used in statements.   Such as, a database function ``calculate()`` which takes the parameters ``x`` and ``y``, and returns three columns which we'd like to name ``q``, ``z`` and ``r``, we can construct using "lexical" column objects as well as bind parameters:
@@ -845,7 +845,7 @@ Databases such as PostgreSQL and Oracle which support functions that return whol
     >>> calculate = select([column('q'), column('z'), column('r')],
     ...     from_obj=[func.calculate(bindparam('x'), bindparam('y'))])
 
-    >>> print select([users], users.c.id > calculate.c.z)
+    >>> print select([users], users.c.id > calculate.c.z) # doctest: +NORMALIZE_WHITESPACE
     SELECT users.id, users.name, users.fullname
     FROM users, (SELECT q, z, r
     FROM calculate(:x, :y))
@@ -859,7 +859,7 @@ If we wanted to use our ``calculate`` statement twice with different bind parame
     ...    calculate.alias('c1').unique_params(x=17, y=45).c.z,
     ...    calculate.alias('c2').unique_params(x=5, y=12).c.z))
 
-    >>> print s
+    >>> print s # doctest: +NORMALIZE_WHITESPACE
     SELECT users.id, users.name, users.fullname
     FROM users, (SELECT q, z, r
     FROM calculate(:x_1, :y_1)) AS c1, (SELECT q, z, r
@@ -884,13 +884,13 @@ Unions come in two flavors, UNION and UNION ALL, which are available via module
     ...    addresses.select(addresses.c.email_address.like('%@yahoo.com')),
     ... ).order_by(addresses.c.email_address)
 
-    {sql}>>> print conn.execute(u).fetchall()
+    {sql}>>> print conn.execute(u).fetchall() # doctest: +NORMALIZE_WHITESPACE
     SELECT addresses.id, addresses.user_id, addresses.email_address
     FROM addresses
     WHERE addresses.email_address = ? UNION SELECT addresses.id, addresses.user_id, addresses.email_address
     FROM addresses
     WHERE addresses.email_address LIKE ? ORDER BY addresses.email_address
-    ['foo@bar.com', '%@yahoo.com']
+    ('foo@bar.com', '%@yahoo.com')
     {stop}[(1, 1, u'jack@yahoo.com')]
 
 Also available, though not supported on all databases, are ``intersect()``, ``intersect_all()``, ``except_()``, and ``except_all()``:
@@ -903,13 +903,13 @@ Also available, though not supported on all databases, are ``intersect()``, ``in
     ...    addresses.select(addresses.c.email_address.like('%@msn.com'))
     ... )
 
-    {sql}>>> print conn.execute(u).fetchall()
+    {sql}>>> print conn.execute(u).fetchall() # doctest: +NORMALIZE_WHITESPACE
     SELECT addresses.id, addresses.user_id, addresses.email_address
     FROM addresses
     WHERE addresses.email_address LIKE ? EXCEPT SELECT addresses.id, addresses.user_id, addresses.email_address
     FROM addresses
     WHERE addresses.email_address LIKE ?
-    ['%@%.com', '%@msn.com']
+    ('%@%.com', '%@msn.com')
     {stop}[(1, 1, u'jack@yahoo.com'), (4, 2, u'wendy@aol.com')]
 
 A common issue with so-called "compound" selectables arises due to the fact that they nest with parenthesis.  SQLite in particular doesn't like a statement that starts with parenthesis.  So when nesting a "compound" inside a "compound", it's often necessary to apply
@@ -935,7 +935,7 @@ the "union" to be stated as a subquery:
     SELECT addresses.id, addresses.user_id, addresses.email_address
     FROM addresses
     WHERE addresses.email_address LIKE ?
-    ['%@yahoo.com', '%@msn.com', '%@msn.com']
+    ('%@yahoo.com', '%@msn.com', '%@msn.com')
     {stop}[(1, 1, u'jack@yahoo.com')]
 
 
@@ -955,7 +955,7 @@ To embed a SELECT in a column expression, use :func:`~sqlalchemy.sql.expression.
     FROM addresses
     WHERE users.id = addresses.user_id) AS anon_1
     FROM users
-    []
+    ()
     {stop}[(u'jack', 2), (u'wendy', 2), (u'fred', 0), (u'mary', 0)]
 
 Alternatively, applying a ``label()`` to a select evaluates it as a scalar as well:
@@ -970,7 +970,7 @@ Alternatively, applying a ``label()`` to a select evaluates it as a scalar as we
     FROM addresses
     WHERE users.id = addresses.user_id) AS address_count
     FROM users
-    []
+    ()
     {stop}[(u'jack', 2), (u'wendy', 2), (u'fred', 0), (u'mary', 0)]
 
 Correlated Subqueries
@@ -979,7 +979,7 @@ Correlated Subqueries
 Notice in the examples on "scalar selects", the FROM clause of each embedded select did not contain the ``users`` table in its FROM clause.  This is because SQLAlchemy automatically attempts to correlate embedded FROM objects to that of an enclosing query.  To disable this, or to specify explicit FROM clauses to be correlated, use ``correlate()``::
 
     >>> s = select([users.c.name], users.c.id==select([users.c.id]).correlate(None))
-    >>> print s
+    >>> print s # doctest: +NORMALIZE_WHITESPACE
     SELECT users.name
     FROM users
     WHERE users.id = (SELECT users.id
@@ -988,7 +988,7 @@ Notice in the examples on "scalar selects", the FROM clause of each embedded sel
     >>> s = select([users.c.name, addresses.c.email_address], users.c.id==
     ...        select([users.c.id], users.c.id==addresses.c.user_id).correlate(addresses)
     ...    )
-    >>> print s
+    >>> print s # doctest: +NORMALIZE_WHITESPACE
     SELECT users.name, addresses.email_address
     FROM users, addresses
     WHERE users.id = (SELECT users.id
@@ -1005,19 +1005,19 @@ The ``select()`` function can take keyword arguments ``order_by``, ``group_by``
 
     >>> s = select([addresses.c.user_id, func.count(addresses.c.id)]).\
     ...     group_by(addresses.c.user_id).having(func.count(addresses.c.id)>1)
-    {sql}>>> print conn.execute(s).fetchall()
+    {sql}>>> print conn.execute(s).fetchall() # doctest: +NORMALIZE_WHITESPACE
     SELECT addresses.user_id, count(addresses.id) AS count_1
     FROM addresses GROUP BY addresses.user_id
     HAVING count(addresses.id) > ?
-    [1]
+    (1,)
     {stop}[(1, 2), (2, 2)]
 
     >>> s = select([addresses.c.email_address, addresses.c.id]).distinct().\
     ...     order_by(addresses.c.email_address.desc(), addresses.c.id)
-    {sql}>>> conn.execute(s).fetchall()
+    {sql}>>> conn.execute(s).fetchall() # doctest: +NORMALIZE_WHITESPACE
     SELECT DISTINCT addresses.email_address, addresses.id
     FROM addresses ORDER BY addresses.email_address DESC, addresses.id
-    []
+    ()
     {stop}[(u'www@www.org', 3), (u'wendy@aol.com', 4), (u'jack@yahoo.com', 1), (u'jack@msn.com', 2)]
 
     >>> s = select([addresses]).offset(1).limit(1)
@@ -1025,7 +1025,7 @@ The ``select()`` function can take keyword arguments ``order_by``, ``group_by``
     SELECT addresses.id, addresses.user_id, addresses.email_address
     FROM addresses
     LIMIT 1 OFFSET 1
-    []
+    ()
     {stop}[(2, 1, u'jack@msn.com')]
 
 Updates
@@ -1039,7 +1039,7 @@ Finally, we're back to UPDATE.  Updates work a lot like INSERTS, except there is
     >>> # change 'jack' to 'ed'
     {sql}>>> conn.execute(users.update().where(users.c.name=='jack').values(name='ed')) #doctest: +ELLIPSIS
     UPDATE users SET name=? WHERE users.name = ?
-    ['ed', 'jack']
+    ('ed', 'jack')
     COMMIT
     {stop}<sqlalchemy.engine.base.ResultProxy object at 0x...>
 
@@ -1047,14 +1047,14 @@ Finally, we're back to UPDATE.  Updates work a lot like INSERTS, except there is
     >>> u = users.update().where(users.c.name==bindparam('oldname')).values(name=bindparam('newname'))
     {sql}>>> conn.execute(u, oldname='jack', newname='ed') #doctest: +ELLIPSIS
     UPDATE users SET name=? WHERE users.name = ?
-    ['ed', 'jack']
+    ('ed', 'jack')
     COMMIT
     {stop}<sqlalchemy.engine.base.ResultProxy object at 0x...>
 
     >>> # update a column to an expression.:
     {sql}>>> conn.execute(users.update().values(fullname="Fullname: " + users.c.name)) #doctest: +ELLIPSIS
     UPDATE users SET fullname=(? || users.name)
-    ['Fullname: ']
+    ('Fullname: ',)
     COMMIT
     {stop}<sqlalchemy.engine.base.ResultProxy object at 0x...>
 
@@ -1071,7 +1071,7 @@ A correlated update lets you update a table using selection from another table,
     FROM addresses
     WHERE addresses.user_id = users.id
     LIMIT 1 OFFSET 0)
-    []
+    ()
     COMMIT
     {stop}<sqlalchemy.engine.base.ResultProxy object at 0x...>
 
@@ -1085,13 +1085,13 @@ Finally, a delete.  Easy enough:
 
     {sql}>>> conn.execute(addresses.delete()) #doctest: +ELLIPSIS
     DELETE FROM addresses
-    []
+    ()
     COMMIT
     {stop}<sqlalchemy.engine.base.ResultProxy object at 0x...>
 
     {sql}>>> conn.execute(users.delete().where(users.c.name > 'm')) #doctest: +ELLIPSIS
     DELETE FROM users WHERE users.name > ?
-    ['m']
+    ('m',)
     COMMIT
     {stop}<sqlalchemy.engine.base.ResultProxy object at 0x...>