~ubuntu-branches/debian/jessie/sqlalchemy/jessie

« 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, Jakub Wilk, Piotr Ożarowski
  • Date: 2013-07-06 20:53:52 UTC
  • mfrom: (1.4.23) (16.1.17 experimental)
  • Revision ID: package-import@ubuntu.com-20130706205352-ryppl1eto3illd79
Tags: 0.8.2-1
[ Jakub Wilk ]
* Use canonical URIs for Vcs-* fields.

[ Piotr Ożarowski ]
* New upstream release
* Upload to unstable
* Build depend on python3-all instead of -dev, extensions are not built for
  Python 3.X 

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