~ubuntu-branches/debian/sid/sqlalchemy/sid

« back to all changes in this revision

Viewing changes to doc/_sources/changelog/migration_04.txt

  • Committer: Package Import Robot
  • Author(s): Piotr Ożarowski
  • Date: 2014-06-27 20:17:13 UTC
  • mfrom: (1.4.28)
  • Revision ID: package-import@ubuntu.com-20140627201713-g6p1kq8q1qenztrv
Tags: 0.9.6-1
* New upstream release
* Remove Python 3.X build tag files, thanks to Matthias Urlichs for the
  patch (closes: #747852)
* python-fdb isn't in the Debian archive yet so default dialect for firebird://
  URLs is changed to obsolete kinterbasdb, thanks to Russell Stuart for the
  patch (closes: #752145)

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
=============================
2
 
What's new in SQLAlchemy 0.4?
3
 
=============================
4
 
 
5
 
.. admonition:: About this Document
6
 
 
7
 
    This document describes changes between SQLAlchemy version 0.3,
8
 
    last released October 14, 2007, and SQLAlchemy version 0.4,
9
 
    last released October 12, 2008.
10
 
 
11
 
    Document date:  March 21, 2008
12
 
 
13
 
First Things First
14
 
==================
15
 
 
16
 
If you're using any ORM features, make sure you import from
17
 
``sqlalchemy.orm``:
18
 
 
19
 
::
20
 
 
21
 
    from sqlalchemy import *
22
 
    from sqlalchemy.orm import *
23
 
 
24
 
Secondly, anywhere you used to say ``engine=``,
25
 
``connectable=``, ``bind_to=``, ``something.engine``,
26
 
``metadata.connect()``, use ``bind``:
27
 
 
28
 
::
29
 
 
30
 
    myengine = create_engine('sqlite://')
31
 
 
32
 
    meta = MetaData(myengine)
33
 
 
34
 
    meta2 = MetaData()
35
 
    meta2.bind = myengine
36
 
 
37
 
    session = create_session(bind=myengine)
38
 
 
39
 
    statement = select([table], bind=myengine)
40
 
 
41
 
Got those ?  Good!  You're now (95%) 0.4 compatible.  If
42
 
you're using 0.3.10, you can make these changes immediately;
43
 
they'll work there too.
44
 
 
45
 
Module Imports
46
 
==============
47
 
 
48
 
In 0.3, "``from sqlachemy import *``" would import all of
49
 
sqlachemy's sub-modules into your namespace. Version 0.4 no
50
 
longer imports sub-modules into the namespace. This may mean
51
 
you need to add extra imports into your code.
52
 
 
53
 
In 0.3, this code worked:
54
 
 
55
 
::
56
 
 
57
 
    from sqlalchemy import *
58
 
 
59
 
    class UTCDateTime(types.TypeDecorator):
60
 
        pass
61
 
 
62
 
In 0.4, one must do:
63
 
 
64
 
::
65
 
 
66
 
    from sqlalchemy import *
67
 
    from sqlalchemy import types
68
 
 
69
 
    class UTCDateTime(types.TypeDecorator):
70
 
        pass
71
 
 
72
 
Object Relational Mapping
73
 
=========================
74
 
 
75
 
Querying
76
 
--------
77
 
 
78
 
New Query API
79
 
^^^^^^^^^^^^^
80
 
 
81
 
Query is standardized on the generative interface (old
82
 
interface is still there, just deprecated).   While most of
83
 
the generative interface is available in 0.3, the 0.4 Query
84
 
has the inner guts to match the generative outside, and has
85
 
a lot more tricks.  All result narrowing is via ``filter()``
86
 
and ``filter_by()``, limiting/offset is either through array
87
 
slices or ``limit()``/``offset()``, joining is via
88
 
``join()`` and ``outerjoin()`` (or more manually, through
89
 
``select_from()`` as well as manually-formed criteria).
90
 
 
91
 
To avoid deprecation warnings, you must make some changes to
92
 
your 03 code
93
 
 
94
 
User.query.get_by( \**kwargs )
95
 
 
96
 
::
97
 
 
98
 
    User.query.filter_by(**kwargs).first()
99
 
 
100
 
User.query.select_by( \**kwargs )
101
 
 
102
 
::
103
 
 
104
 
    User.query.filter_by(**kwargs).all()
105
 
 
106
 
User.query.select()
107
 
 
108
 
::
109
 
 
110
 
    User.query.filter(xxx).all()
111
 
 
112
 
New Property-Based Expression Constructs
113
 
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
114
 
 
115
 
By far the most palpable difference within the ORM is that
116
 
you can now construct your query criterion using class-based
117
 
attributes directly.  The ".c." prefix is no longer needed
118
 
when working with mapped classes:
119
 
 
120
 
::
121
 
 
122
 
    session.query(User).filter(and_(User.name == 'fred', User.id > 17))
123
 
 
124
 
While simple column-based comparisons are no big deal, the
125
 
class attributes have some new "higher level" constructs
126
 
available, including what was previously only available in
127
 
``filter_by()``:
128
 
 
129
 
::
130
 
 
131
 
    # comparison of scalar relations to an instance
132
 
    filter(Address.user == user)
133
 
 
134
 
    # return all users who contain a particular address
135
 
    filter(User.addresses.contains(address))
136
 
 
137
 
    # return all users who *dont* contain the address
138
 
    filter(~User.address.contains(address))
139
 
 
140
 
    # return all users who contain a particular address with
141
 
    # the email_address like '%foo%'
142
 
    filter(User.addresses.any(Address.email_address.like('%foo%')))
143
 
 
144
 
    # same, email address equals 'foo@bar.com'.  can fall back to keyword
145
 
    # args for simple comparisons
146
 
    filter(User.addresses.any(email_address = 'foo@bar.com'))
147
 
 
148
 
    # return all Addresses whose user attribute has the username 'ed'
149
 
    filter(Address.user.has(name='ed'))
150
 
 
151
 
    # return all Addresses whose user attribute has the username 'ed'
152
 
    # and an id > 5 (mixing clauses with kwargs)
153
 
    filter(Address.user.has(User.id > 5, name='ed'))
154
 
 
155
 
The ``Column`` collection remains available on mapped
156
 
classes in the ``.c`` attribute.  Note that property-based
157
 
expressions are only available with mapped properties of
158
 
mapped classes.  ``.c`` is still used to access columns in
159
 
regular tables and selectable objects produced from SQL
160
 
Expressions.
161
 
 
162
 
Automatic Join Aliasing
163
 
^^^^^^^^^^^^^^^^^^^^^^^
164
 
 
165
 
We've had join() and outerjoin() for a while now:
166
 
 
167
 
::
168
 
 
169
 
    session.query(Order).join('items')...
170
 
 
171
 
Now you can alias them:
172
 
 
173
 
::
174
 
 
175
 
    session.query(Order).join('items', aliased=True).
176
 
       filter(Item.name='item 1').join('items', aliased=True).filter(Item.name=='item 3')
177
 
 
178
 
The above will create two joins from orders->items using
179
 
aliases.  the ``filter()`` call subsequent to each will
180
 
adjust its table criterion to that of the alias.  To get at
181
 
the ``Item`` objects, use ``add_entity()`` and target each
182
 
join with an ``id``:
183
 
 
184
 
::
185
 
 
186
 
    session.query(Order).join('items', id='j1', aliased=True).
187
 
    filter(Item.name == 'item 1').join('items', aliased=True, id='j2').
188
 
    filter(Item.name == 'item 3').add_entity(Item, id='j1').add_entity(Item, id='j2')
189
 
 
190
 
Returns tuples in the form: ``(Order, Item, Item)``.
191
 
 
192
 
Self-referential Queries
193
 
^^^^^^^^^^^^^^^^^^^^^^^^
194
 
 
195
 
So query.join() can make aliases now.  What does that give
196
 
us ?  Self-referential queries !   Joins can be done without
197
 
any ``Alias`` objects:
198
 
 
199
 
::
200
 
 
201
 
    # standard self-referential TreeNode mapper with backref
202
 
    mapper(TreeNode, tree_nodes, properties={
203
 
        'children':relation(TreeNode, backref=backref('parent', remote_side=tree_nodes.id))
204
 
    })
205
 
 
206
 
    # query for node with child containing "bar" two levels deep
207
 
    session.query(TreeNode).join(["children", "children"], aliased=True).filter_by(name='bar')
208
 
 
209
 
To add criterion for each table along the way in an aliased
210
 
join, you can use ``from_joinpoint`` to keep joining against
211
 
the same line of aliases:
212
 
 
213
 
::
214
 
 
215
 
    # search for the treenode along the path "n1/n12/n122"
216
 
 
217
 
    # first find a Node with name="n122"
218
 
    q = sess.query(Node).filter_by(name='n122')
219
 
 
220
 
    # then join to parent with "n12"
221
 
    q = q.join('parent', aliased=True).filter_by(name='n12')
222
 
 
223
 
    # join again to the next parent with 'n1'.  use 'from_joinpoint'
224
 
    # so we join from the previous point, instead of joining off the
225
 
    # root table
226
 
    q = q.join('parent', aliased=True, from_joinpoint=True).filter_by(name='n1')
227
 
 
228
 
    node = q.first()
229
 
 
230
 
``query.populate_existing()``
231
 
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
232
 
 
233
 
The eager version of ``query.load()`` (or
234
 
``session.refresh()``).  Every instance loaded from the
235
 
query, including all eagerly loaded items, get refreshed
236
 
immediately if already present in the session:
237
 
 
238
 
::
239
 
 
240
 
    session.query(Blah).populate_existing().all()
241
 
 
242
 
Relations
243
 
---------
244
 
 
245
 
SQL Clauses Embedded in Updates/Inserts
246
 
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
247
 
 
248
 
For inline execution of SQL clauses, embedded right in the
249
 
UPDATE or INSERT, during a ``flush()``:
250
 
 
251
 
::
252
 
 
253
 
 
254
 
    myobject.foo = mytable.c.value + 1
255
 
 
256
 
    user.pwhash = func.md5(password)
257
 
 
258
 
    order.hash = text("select hash from hashing_table")
259
 
 
260
 
The column-attribute is set up with a deferred loader after
261
 
the operation, so that it issues the SQL to load the new
262
 
value when you next access.
263
 
 
264
 
Self-referential and Cyclical Eager Loading
265
 
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
266
 
 
267
 
Since our alias-fu has improved, ``relation()`` can join
268
 
along the same table \*any number of times*; you tell it how
269
 
deep you want to go.  Lets show the self-referential
270
 
``TreeNode`` more clearly:
271
 
 
272
 
::
273
 
 
274
 
    nodes = Table('nodes', metadata,
275
 
         Column('id', Integer, primary_key=True),
276
 
         Column('parent_id', Integer, ForeignKey('nodes.id')),
277
 
         Column('name', String(30)))
278
 
 
279
 
    class TreeNode(object):
280
 
        pass
281
 
 
282
 
    mapper(TreeNode, nodes, properties={
283
 
        'children':relation(TreeNode, lazy=False, join_depth=3)
284
 
    })
285
 
 
286
 
So what happens when we say:
287
 
 
288
 
::
289
 
 
290
 
    create_session().query(TreeNode).all()
291
 
 
292
 
?  A join along aliases, three levels deep off the parent:
293
 
 
294
 
::
295
 
 
296
 
    SELECT
297
 
    nodes_3.id AS nodes_3_id, nodes_3.parent_id AS nodes_3_parent_id, nodes_3.name AS nodes_3_name,
298
 
    nodes_2.id AS nodes_2_id, nodes_2.parent_id AS nodes_2_parent_id, nodes_2.name AS nodes_2_name,
299
 
    nodes_1.id AS nodes_1_id, nodes_1.parent_id AS nodes_1_parent_id, nodes_1.name AS nodes_1_name,
300
 
    nodes.id AS nodes_id, nodes.parent_id AS nodes_parent_id, nodes.name AS nodes_name
301
 
    FROM nodes LEFT OUTER JOIN nodes AS nodes_1 ON nodes.id = nodes_1.parent_id
302
 
    LEFT OUTER JOIN nodes AS nodes_2 ON nodes_1.id = nodes_2.parent_id
303
 
    LEFT OUTER JOIN nodes AS nodes_3 ON nodes_2.id = nodes_3.parent_id
304
 
    ORDER BY nodes.oid, nodes_1.oid, nodes_2.oid, nodes_3.oid
305
 
 
306
 
Notice the nice clean alias names too.  The joining doesn't
307
 
care if it's against the same immediate table or some other
308
 
object which then cycles back to the beginining.  Any kind
309
 
of chain of eager loads can cycle back onto itself when
310
 
``join_depth`` is specified.  When not present, eager
311
 
loading automatically stops when it hits a cycle.
312
 
 
313
 
Composite Types
314
 
^^^^^^^^^^^^^^^
315
 
 
316
 
This is one from the Hibernate camp.  Composite Types let
317
 
you define a custom datatype that is composed of more than
318
 
one column (or one column, if you wanted).   Lets define a
319
 
new type, ``Point``.  Stores an x/y coordinate:
320
 
 
321
 
::
322
 
 
323
 
    class Point(object):
324
 
        def __init__(self, x, y):
325
 
            self.x = x
326
 
            self.y = y
327
 
        def __composite_values__(self):
328
 
            return self.x, self.y
329
 
        def __eq__(self, other):
330
 
            return other.x == self.x and other.y == self.y
331
 
        def __ne__(self, other):
332
 
            return not self.__eq__(other)
333
 
 
334
 
The way the ``Point`` object is defined is specific to a
335
 
custom type; constructor takes a list of arguments, and the
336
 
``__composite_values__()`` method produces a sequence of
337
 
those arguments.  The order will match up to our mapper, as
338
 
we'll see in a moment.
339
 
 
340
 
Let's create a table of vertices storing two points per row:
341
 
 
342
 
::
343
 
 
344
 
    vertices = Table('vertices', metadata,
345
 
        Column('id', Integer, primary_key=True),
346
 
        Column('x1', Integer),
347
 
        Column('y1', Integer),
348
 
        Column('x2', Integer),
349
 
        Column('y2', Integer),
350
 
        )
351
 
 
352
 
Then, map it !  We'll create a ``Vertex`` object which
353
 
stores two ``Point`` objects:
354
 
 
355
 
::
356
 
 
357
 
    class Vertex(object):
358
 
        def __init__(self, start, end):
359
 
            self.start = start
360
 
            self.end = end
361
 
 
362
 
    mapper(Vertex, vertices, properties={
363
 
        'start':composite(Point, vertices.c.x1, vertices.c.y1),
364
 
        'end':composite(Point, vertices.c.x2, vertices.c.y2)
365
 
    })
366
 
 
367
 
Once you've set up your composite type, it's usable just
368
 
like any other type:
369
 
 
370
 
::
371
 
 
372
 
 
373
 
    v = Vertex(Point(3, 4), Point(26,15))
374
 
    session.save(v)
375
 
    session.flush()
376
 
 
377
 
    # works in queries too
378
 
    q = session.query(Vertex).filter(Vertex.start == Point(3, 4))
379
 
 
380
 
If you'd like to define the way the mapped attributes
381
 
generate SQL clauses when used in expressions, create your
382
 
own ``sqlalchemy.orm.PropComparator`` subclass, defining any
383
 
of the common operators (like ``__eq__()``, ``__le__()``,
384
 
etc.), and send it in to ``composite()``.  Composite types
385
 
work as primary keys too, and are usable in ``query.get()``:
386
 
 
387
 
::
388
 
 
389
 
    # a Document class which uses a composite Version
390
 
    # object as primary key
391
 
    document = query.get(Version(1, 'a'))
392
 
 
393
 
``dynamic_loader()`` relations
394
 
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
395
 
 
396
 
A ``relation()`` that returns a live ``Query`` object for
397
 
all read operations.  Write operations are limited to just
398
 
``append()`` and ``remove()``, changes to the collection are
399
 
not visible until the session is flushed.  This feature is
400
 
particularly handy with an "autoflushing" session which will
401
 
flush before each query.
402
 
 
403
 
::
404
 
 
405
 
    mapper(Foo, foo_table, properties={
406
 
        'bars':dynamic_loader(Bar, backref='foo', <other relation() opts>)
407
 
    })
408
 
 
409
 
    session = create_session(autoflush=True)
410
 
    foo = session.query(Foo).first()
411
 
 
412
 
    foo.bars.append(Bar(name='lala'))
413
 
 
414
 
    for bar in foo.bars.filter(Bar.name=='lala'):
415
 
        print bar
416
 
 
417
 
    session.commit()
418
 
 
419
 
New Options: ``undefer_group()``, ``eagerload_all()``
420
 
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
421
 
 
422
 
A couple of query options which are handy.
423
 
``undefer_group()`` marks a whole group of "deferred"
424
 
columns as undeferred:
425
 
 
426
 
::
427
 
 
428
 
    mapper(Class, table, properties={
429
 
        'foo' : deferred(table.c.foo, group='group1'),
430
 
        'bar' : deferred(table.c.bar, group='group1'),
431
 
        'bat' : deferred(table.c.bat, group='group1'),
432
 
    )
433
 
 
434
 
    session.query(Class).options(undefer_group('group1')).filter(...).all()
435
 
 
436
 
and ``eagerload_all()`` sets a chain of attributes to be
437
 
eager in one pass:
438
 
 
439
 
::
440
 
 
441
 
    mapper(Foo, foo_table, properties={
442
 
       'bar':relation(Bar)
443
 
    })
444
 
    mapper(Bar, bar_table, properties={
445
 
       'bat':relation(Bat)
446
 
    })
447
 
    mapper(Bat, bat_table)
448
 
 
449
 
    # eager load bar and bat
450
 
    session.query(Foo).options(eagerload_all('bar.bat')).filter(...).all()
451
 
 
452
 
New Collection API
453
 
^^^^^^^^^^^^^^^^^^
454
 
 
455
 
Collections are no longer proxied by an
456
 
{{{InstrumentedList}}} proxy, and access to members, methods
457
 
and attributes is direct.   Decorators now intercept objects
458
 
entering and leaving the collection, and it is now possible
459
 
to easily write a custom collection class that manages its
460
 
own membership.  Flexible decorators also replace the named
461
 
method interface of custom collections in 0.3, allowing any
462
 
class to be easily adapted to use as a collection container.
463
 
 
464
 
Dictionary-based collections are now much easier to use and
465
 
fully ``dict``-like.  Changing ``__iter__`` is no longer
466
 
needed for ``dict``s, and new built-in ``dict`` types cover
467
 
many needs:
468
 
 
469
 
::
470
 
 
471
 
    # use a dictionary relation keyed by a column
472
 
    relation(Item, collection_class=column_mapped_collection(items.c.keyword))
473
 
    # or named attribute
474
 
    relation(Item, collection_class=attribute_mapped_collection('keyword'))
475
 
    # or any function you like
476
 
    relation(Item, collection_class=mapped_collection(lambda entity: entity.a + entity.b))
477
 
 
478
 
Existing 0.3 ``dict``-like and freeform object derived
479
 
collection classes will need to be updated for the new API.
480
 
In most cases this is simply a matter of adding a couple
481
 
decorators to the class definition.
482
 
 
483
 
Mapped Relations from External Tables/Subqueries
484
 
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
485
 
 
486
 
This feature quietly appeared in 0.3 but has been improved
487
 
in 0.4 thanks to better ability to convert subqueries
488
 
against a table into subqueries against an alias of that
489
 
table; this is key for eager loading, aliased joins in
490
 
queries, etc.  It reduces the need to create mappers against
491
 
select statements when you just need to add some extra
492
 
columns or subqueries:
493
 
 
494
 
::
495
 
 
496
 
    mapper(User, users, properties={
497
 
           'fullname': column_property((users.c.firstname + users.c.lastname).label('fullname')),
498
 
           'numposts': column_property(
499
 
                select([func.count(1)], users.c.id==posts.c.user_id).correlate(users).label('posts')
500
 
           )
501
 
        })
502
 
 
503
 
a typical query looks like:
504
 
 
505
 
::
506
 
 
507
 
    SELECT (SELECT count(1) FROM posts WHERE users.id = posts.user_id) AS count,
508
 
    users.firstname || users.lastname AS fullname,
509
 
    users.id AS users_id, users.firstname AS users_firstname, users.lastname AS users_lastname
510
 
    FROM users ORDER BY users.oid
511
 
 
512
 
Horizontal Scaling (Sharding) API
513
 
---------------------------------
514
 
 
515
 
[browser:/sqlalchemy/trunk/examples/sharding/attribute_shard
516
 
.py]
517
 
 
518
 
Sessions
519
 
--------
520
 
 
521
 
New Session Create Paradigm; SessionContext, assignmapper Deprecated
522
 
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
523
 
 
524
 
That's right, the whole shebang is being replaced with two
525
 
configurational functions.  Using both will produce the most
526
 
0.1-ish feel we've had since 0.1 (i.e., the least amount of
527
 
typing).
528
 
 
529
 
Configure your own ``Session`` class right where you define
530
 
your ``engine`` (or anywhere):
531
 
 
532
 
::
533
 
 
534
 
    from sqlalchemy import create_engine
535
 
    from sqlalchemy.orm import sessionmaker
536
 
 
537
 
    engine = create_engine('myengine://')
538
 
    Session = sessionmaker(bind=engine, autoflush=True, transactional=True)
539
 
 
540
 
    # use the new Session() freely
541
 
    sess = Session()
542
 
    sess.save(someobject)
543
 
    sess.flush()
544
 
 
545
 
 
546
 
If you need to post-configure your Session, say with an
547
 
engine, add it later with ``configure()``:
548
 
 
549
 
::
550
 
 
551
 
    Session.configure(bind=create_engine(...))
552
 
 
553
 
All the behaviors of ``SessionContext`` and the ``query``
554
 
and ``__init__`` methods of ``assignmapper`` are moved into
555
 
the new ``scoped_session()`` function, which is compatible
556
 
with both ``sessionmaker`` as well as ``create_session()``:
557
 
 
558
 
::
559
 
 
560
 
    from sqlalchemy.orm import scoped_session, sessionmaker
561
 
 
562
 
    Session = scoped_session(sessionmaker(autoflush=True, transactional=True))
563
 
    Session.configure(bind=engine)
564
 
 
565
 
    u = User(name='wendy')
566
 
 
567
 
    sess = Session()
568
 
    sess.save(u)
569
 
    sess.commit()
570
 
 
571
 
    # Session constructor is thread-locally scoped.  Everyone gets the same
572
 
    # Session in the thread when scope="thread".
573
 
    sess2 = Session()
574
 
    assert sess is sess2
575
 
 
576
 
 
577
 
When using a thread-local ``Session``, the returned class
578
 
has all of ``Session's`` interface implemented as
579
 
classmethods, and "assignmapper"'s functionality is
580
 
available using the ``mapper`` classmethod.  Just like the
581
 
old ``objectstore`` days....
582
 
 
583
 
::
584
 
 
585
 
 
586
 
    # "assignmapper"-like functionality available via ScopedSession.mapper
587
 
    Session.mapper(User, users_table)
588
 
 
589
 
    u = User(name='wendy')
590
 
 
591
 
    Session.commit()
592
 
 
593
 
 
594
 
Sessions are again Weak Referencing By Default
595
 
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
596
 
 
597
 
The weak_identity_map flag is now set to ``True`` by default
598
 
on Session.  Instances which are externally deferenced and
599
 
fall out of scope are removed from the session
600
 
automatically.   However, items which have "dirty" changes
601
 
present will remain strongly referenced until those changes
602
 
are flushed at which case the object reverts to being weakly
603
 
referenced (this works for 'mutable' types, like picklable
604
 
attributes, as well).  Setting weak_identity_map to
605
 
``False`` restores the old strong-referencing behavior for
606
 
those of you using the session like a cache.
607
 
 
608
 
Auto-Transactional Sessions
609
 
^^^^^^^^^^^^^^^^^^^^^^^^^^^
610
 
 
611
 
As you might have noticed above, we are calling ``commit()``
612
 
on ``Session``.  The flag ``transactional=True`` means the
613
 
``Session`` is always in a transaction, ``commit()``
614
 
persists permanently.
615
 
 
616
 
Auto-Flushing Sessions
617
 
^^^^^^^^^^^^^^^^^^^^^^
618
 
 
619
 
Also, ``autoflush=True`` means the ``Session`` will
620
 
``flush()`` before each ``query`` as well as when you call
621
 
``flush()`` or ``commit()``.  So now this will work:
622
 
 
623
 
::
624
 
 
625
 
    Session = sessionmaker(bind=engine, autoflush=True, transactional=True)
626
 
 
627
 
    u = User(name='wendy')
628
 
 
629
 
    sess = Session()
630
 
    sess.save(u)
631
 
 
632
 
    # wendy is flushed, comes right back from a query
633
 
    wendy = sess.query(User).filter_by(name='wendy').one()
634
 
 
635
 
Transactional methods moved onto sessions
636
 
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
637
 
 
638
 
``commit()`` and ``rollback()``, as well as ``begin()`` are
639
 
now directly on ``Session``.  No more need to use
640
 
``SessionTransaction`` for anything (it remains in the
641
 
background).
642
 
 
643
 
::
644
 
 
645
 
    Session = sessionmaker(autoflush=True, transactional=False)
646
 
 
647
 
    sess = Session()
648
 
    sess.begin()
649
 
 
650
 
    # use the session
651
 
 
652
 
    sess.commit() # commit transaction
653
 
 
654
 
Sharing a ``Session`` with an enclosing engine-level (i.e.
655
 
non-ORM) transaction is easy:
656
 
 
657
 
::
658
 
 
659
 
    Session = sessionmaker(autoflush=True, transactional=False)
660
 
 
661
 
    conn = engine.connect()
662
 
    trans = conn.begin()
663
 
    sess = Session(bind=conn)
664
 
 
665
 
    # ... session is transactional
666
 
 
667
 
    # commit the outermost transaction
668
 
    trans.commit()
669
 
 
670
 
Nested Session Transactions with SAVEPOINT
671
 
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
672
 
 
673
 
Available at the Engine and ORM level.  ORM docs so far:
674
 
 
675
 
http://www.sqlalchemy.org/docs/04/session.html#unitofwork_ma
676
 
naging
677
 
 
678
 
Two-Phase Commit Sessions
679
 
^^^^^^^^^^^^^^^^^^^^^^^^^
680
 
 
681
 
Available at the Engine and ORM level.  ORM docs so far:
682
 
 
683
 
http://www.sqlalchemy.org/docs/04/session.html#unitofwork_ma
684
 
naging
685
 
 
686
 
Inheritance
687
 
-----------
688
 
 
689
 
Polymorphic Inheritance with No Joins or Unions
690
 
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
691
 
 
692
 
New docs for inheritance:  http://www.sqlalchemy.org/docs/04
693
 
/mappers.html#advdatamapping_mapper_inheritance_joined
694
 
 
695
 
Better Polymorphic Behavior with ``get()``
696
 
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
697
 
 
698
 
All classes within a joined-table inheritance hierarchy get
699
 
an ``_instance_key`` using the base class, i.e.
700
 
``(BaseClass, (1, ), None)``.  That way when you call
701
 
``get()`` a ``Query`` against the base class, it can locate
702
 
subclass instances in the current identity map without
703
 
querying the database.
704
 
 
705
 
Types
706
 
-----
707
 
 
708
 
Custom Subclasses of ``sqlalchemy.types.TypeDecorator``
709
 
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
710
 
 
711
 
There is a `New API <http://www.sqlalchemy.org/docs/04/types
712
 
.html#types_custom>`_ for subclassing a TypeDecorator.
713
 
Using the 0.3 API causes compilation errors in some cases.
714
 
 
715
 
SQL Expressions
716
 
===============
717
 
 
718
 
All New, Deterministic Label/Alias Generation
719
 
---------------------------------------------
720
 
 
721
 
All the "anonymous" labels and aliases use a simple
722
 
<name>_<number> format now.  SQL is much easier to read and
723
 
is compatible with plan optimizer caches.  Just check out
724
 
some of the examples in the tutorials:
725
 
http://www.sqlalchemy.org/docs/04/ormtutorial.html
726
 
http://www.sqlalchemy.org/docs/04/sqlexpression.html
727
 
 
728
 
Generative select() Constructs
729
 
------------------------------
730
 
 
731
 
This is definitely the way to go with ``select()``.  See htt
732
 
p://www.sqlalchemy.org/docs/04/sqlexpression.html#sql_transf
733
 
orm .
734
 
 
735
 
New Operator System
736
 
-------------------
737
 
 
738
 
SQL operators and more or less every SQL keyword there is
739
 
are now abstracted into the compiler layer.  They now act
740
 
intelligently and are type/backend aware, see: http://www.sq
741
 
lalchemy.org/docs/04/sqlexpression.html#sql_operators
742
 
 
743
 
All ``type`` Keyword Arguments Renamed to ``type_``
744
 
---------------------------------------------------
745
 
 
746
 
Just like it says:
747
 
 
748
 
::
749
 
 
750
 
       b = bindparam('foo', type_=String)
751
 
 
752
 
in\_ Function Changed to Accept Sequence or Selectable
753
 
------------------------------------------------------
754
 
 
755
 
The in\_ function now takes a sequence of values or a
756
 
selectable as its sole argument. The previous API of passing
757
 
in values as positional arguments still works, but is now
758
 
deprecated. This means that
759
 
 
760
 
::
761
 
 
762
 
    my_table.select(my_table.c.id.in_(1,2,3)
763
 
    my_table.select(my_table.c.id.in_(*listOfIds)
764
 
 
765
 
should be changed to
766
 
 
767
 
::
768
 
 
769
 
    my_table.select(my_table.c.id.in_([1,2,3])
770
 
    my_table.select(my_table.c.id.in_(listOfIds)
771
 
 
772
 
Schema and Reflection
773
 
=====================
774
 
 
775
 
``MetaData``, ``BoundMetaData``, ``DynamicMetaData``...
776
 
-------------------------------------------------------
777
 
 
778
 
In the 0.3.x series, ``BoundMetaData`` and
779
 
``DynamicMetaData`` were deprecated in favor of ``MetaData``
780
 
and ``ThreadLocalMetaData``.  The older names have been
781
 
removed in 0.4.  Updating is simple:
782
 
 
783
 
::
784
 
 
785
 
    +-------------------------------------+-------------------------+
786
 
    |If You Had                           | Now Use                 |
787
 
    +=====================================+=========================+
788
 
    | ``MetaData``                        | ``MetaData``            |
789
 
    +-------------------------------------+-------------------------+
790
 
    | ``BoundMetaData``                   | ``MetaData``            |
791
 
    +-------------------------------------+-------------------------+
792
 
    | ``DynamicMetaData`` (with one       | ``MetaData``            |
793
 
    | engine or threadlocal=False)        |                         |
794
 
    +-------------------------------------+-------------------------+
795
 
    | ``DynamicMetaData``                 | ``ThreadLocalMetaData`` |
796
 
    | (with different engines per thread) |                         |
797
 
    +-------------------------------------+-------------------------+
798
 
 
799
 
The seldom-used ``name`` parameter to ``MetaData`` types has
800
 
been removed.  The ``ThreadLocalMetaData`` constructor now
801
 
takes no arguments.  Both types can now be bound to an
802
 
``Engine`` or a single ``Connection``.
803
 
 
804
 
One Step Multi-Table Reflection
805
 
-------------------------------
806
 
 
807
 
You can now load table definitions and automatically create
808
 
``Table`` objects from an entire database or schema in one
809
 
pass:
810
 
 
811
 
::
812
 
 
813
 
    >>> metadata = MetaData(myengine, reflect=True)
814
 
    >>> metadata.tables.keys()
815
 
    ['table_a', 'table_b', 'table_c', '...']
816
 
 
817
 
``MetaData`` also gains a ``.reflect()`` method enabling
818
 
finer control over the loading process, including
819
 
specification of a subset of available tables to load.
820
 
 
821
 
SQL Execution
822
 
=============
823
 
 
824
 
``engine``, ``connectable``, and ``bind_to`` are all now ``bind``
825
 
-----------------------------------------------------------------
826
 
 
827
 
``Transactions``, ``NestedTransactions`` and ``TwoPhaseTransactions``
828
 
---------------------------------------------------------------------
829
 
 
830
 
Connection Pool Events
831
 
----------------------
832
 
 
833
 
The connection pool now fires events when new DB-API
834
 
connections are created, checked out and checked back into
835
 
the pool.   You can use these to execute session-scoped SQL
836
 
setup statements on fresh connections, for example.
837
 
 
838
 
Oracle Engine Fixed
839
 
-------------------
840
 
 
841
 
In 0.3.11, there were bugs in the Oracle Engine on how
842
 
Primary Keys are handled.  These bugs could cause programs
843
 
that worked fine with other engines, such as sqlite, to fail
844
 
when using the Oracle Engine.  In 0.4, the Oracle Engine has
845
 
been reworked, fixing these Primary Key problems.
846
 
 
847
 
Out Parameters for Oracle
848
 
-------------------------
849
 
 
850
 
::
851
 
 
852
 
    result = engine.execute(text("begin foo(:x, :y, :z); end;", bindparams=[bindparam('x', Numeric), outparam('y', Numeric), outparam('z', Numeric)]), x=5)
853
 
    assert result.out_parameters == {'y':10, 'z':75}
854
 
 
855
 
Connection-bound ``MetaData``, ``Sessions``
856
 
-------------------------------------------
857
 
 
858
 
``MetaData`` and ``Session`` can be explicitly bound to a
859
 
connection:
860
 
 
861
 
::
862
 
 
863
 
    conn = engine.connect()
864
 
    sess = create_session(bind=conn)
865
 
 
866
 
Faster, More Foolproof ``ResultProxy`` Objects
867
 
----------------------------------------------
868