1============================= 2What's new in SQLAlchemy 0.5? 3============================= 4 5.. admonition:: About this Document 6 7 This document describes changes between SQLAlchemy version 0.4, 8 last released October 12, 2008, and SQLAlchemy version 0.5, 9 last released January 16, 2010. 10 11 Document date: August 4, 2009 12 13 14This guide documents API changes which affect users 15migrating their applications from the 0.4 series of 16SQLAlchemy to 0.5. It's also recommended for those working 17from `Essential SQLAlchemy 18<https://oreilly.com/catalog/9780596516147/>`_, which only 19covers 0.4 and seems to even have some old 0.3isms in it. 20Note that SQLAlchemy 0.5 removes many behaviors which were 21deprecated throughout the span of the 0.4 series, and also 22deprecates more behaviors specific to 0.4. 23 24Major Documentation Changes 25=========================== 26 27Some sections of the documentation have been completely 28rewritten and can serve as an introduction to new ORM 29features. The ``Query`` and ``Session`` objects in 30particular have some distinct differences in API and 31behavior which fundamentally change many of the basic ways 32things are done, particularly with regards to constructing 33highly customized ORM queries and dealing with stale session 34state, commits and rollbacks. 35 36* `ORM Tutorial 37 <https://www.sqlalchemy.org/docs/05/ormtutorial.html>`_ 38 39* `Session Documentation 40 <https://www.sqlalchemy.org/docs/05/session.html>`_ 41 42Deprecations Source 43=================== 44 45Another source of information is documented within a series 46of unit tests illustrating up to date usages of some common 47``Query`` patterns; this file can be viewed at 48[source:sqlalchemy/trunk/test/orm/test_deprecations.py]. 49 50Requirements Changes 51==================== 52 53* Python 2.4 or higher is required. The SQLAlchemy 0.4 line 54 is the last version with Python 2.3 support. 55 56Object Relational Mapping 57========================= 58 59* **Column level expressions within Query.** - as detailed 60 in the `tutorial 61 <https://www.sqlalchemy.org/docs/05/ormtutorial.html>`_, 62 ``Query`` has the capability to create specific SELECT 63 statements, not just those against full rows: 64 65 :: 66 67 session.query(User.name, func.count(Address.id).label("numaddresses")).join(Address).group_by(User.name) 68 69 The tuples returned by any multi-column/entity query are 70 *named*' tuples: 71 72 :: 73 74 for row in session.query(User.name, func.count(Address.id).label('numaddresses')).join(Address).group_by(User.name): 75 print("name", row.name, "number", row.numaddresses) 76 77 ``Query`` has a ``statement`` accessor, as well as a 78 ``subquery()`` method which allow ``Query`` to be used to 79 create more complex combinations: 80 81 :: 82 83 subq = session.query(Keyword.id.label('keyword_id')).filter(Keyword.name.in_(['beans', 'carrots'])).subquery() 84 recipes = session.query(Recipe).filter(exists(). 85 where(Recipe.id==recipe_keywords.c.recipe_id). 86 where(recipe_keywords.c.keyword_id==subq.c.keyword_id) 87 ) 88 89* **Explicit ORM aliases are recommended for aliased joins** 90 - The ``aliased()`` function produces an "alias" of a 91 class, which allows fine-grained control of aliases in 92 conjunction with ORM queries. While a table-level alias 93 (i.e. ``table.alias()``) is still usable, an ORM level 94 alias retains the semantics of the ORM mapped object which 95 is significant for inheritance mappings, options, and 96 other scenarios. E.g.: 97 98 :: 99 100 Friend = aliased(Person) 101 session.query(Person, Friend).join((Friend, Person.friends)).all() 102 103* **query.join() greatly enhanced.** - You can now specify 104 the target and ON clause for a join in multiple ways. A 105 target class alone can be provided where SQLA will attempt 106 to form a join to it via foreign key in the same way as 107 ``table.join(someothertable)``. A target and an explicit 108 ON condition can be provided, where the ON condition can 109 be a ``relation()`` name, an actual class descriptor, or a 110 SQL expression. Or the old way of just a ``relation()`` 111 name or class descriptor works too. See the ORM tutorial 112 which has several examples. 113 114* **Declarative is recommended for applications which don't 115 require (and don't prefer) abstraction between tables and 116 mappers** - The [/docs/05/reference/ext/declarative.html 117 Declarative] module, which is used to combine the 118 expression of ``Table``, ``mapper()``, and user defined 119 class objects together, is highly recommended as it 120 simplifies application configuration, ensures the "one 121 mapper per class" pattern, and allows the full range of 122 configuration available to distinct ``mapper()`` calls. 123 Separate ``mapper()`` and ``Table`` usage is now referred 124 to as "classical SQLAlchemy usage" and of course is freely 125 mixable with declarative. 126 127* **The .c. attribute has been removed** from classes (i.e. 128 ``MyClass.c.somecolumn``). As is the case in 0.4, class- 129 level properties are usable as query elements, i.e. 130 ``Class.c.propname`` is now superseded by 131 ``Class.propname``, and the ``c`` attribute continues to 132 remain on ``Table`` objects where they indicate the 133 namespace of ``Column`` objects present on the table. 134 135 To get at the Table for a mapped class (if you didn't keep 136 it around already): 137 138 :: 139 140 table = class_mapper(someclass).mapped_table 141 142 Iterate through columns: 143 144 :: 145 146 for col in table.c: 147 print(col) 148 149 Work with a specific column: 150 151 :: 152 153 table.c.somecolumn 154 155 The class-bound descriptors support the full set of Column 156 operators as well as the documented relation-oriented 157 operators like ``has()``, ``any()``, ``contains()``, etc. 158 159 The reason for the hard removal of ``.c.`` is that in 0.5, 160 class-bound descriptors carry potentially different 161 meaning, as well as information regarding class mappings, 162 versus plain ``Column`` objects - and there are use cases 163 where you'd specifically want to use one or the other. 164 Generally, using class-bound descriptors invokes a set of 165 mapping/polymorphic aware translations, and using table- 166 bound columns does not. In 0.4, these translations were 167 applied across the board to all expressions, but 0.5 168 differentiates completely between columns and mapped 169 descriptors, only applying translations to the latter. So 170 in many cases, particularly when dealing with joined table 171 inheritance configurations as well as when using 172 ``query(<columns>)``, ``Class.propname`` and 173 ``table.c.colname`` are not interchangeable. 174 175 For example, ``session.query(users.c.id, users.c.name)`` 176 is different versus ``session.query(User.id, User.name)``; 177 in the latter case, the ``Query`` is aware of the mapper 178 in use and further mapper-specific operations like 179 ``query.join(<propname>)``, ``query.with_parent()`` etc. 180 may be used, but in the former case cannot. Additionally, 181 in polymorphic inheritance scenarios, the class-bound 182 descriptors refer to the columns present in the 183 polymorphic selectable in use, not necessarily the table 184 column which directly corresponds to the descriptor. For 185 example, a set of classes related by joined-table 186 inheritance to the ``person`` table along the 187 ``person_id`` column of each table will all have their 188 ``Class.person_id`` attribute mapped to the ``person_id`` 189 column in ``person``, and not their subclass table. 190 Version 0.4 would map this behavior onto table-bound 191 ``Column`` objects automatically. In 0.5, this automatic 192 conversion has been removed, so that you in fact *can* use 193 table-bound columns as a means to override the 194 translations which occur with polymorphic querying; this 195 allows ``Query`` to be able to create optimized selects 196 among joined-table or concrete-table inheritance setups, 197 as well as portable subqueries, etc. 198 199* **Session Now Synchronizes Automatically with 200 Transactions.** Session now synchronizes against the 201 transaction automatically by default, including autoflush 202 and autoexpire. A transaction is present at all times 203 unless disabled using the ``autocommit`` option. When all 204 three flags are set to their default, the Session recovers 205 gracefully after rollbacks and it's very difficult to get 206 stale data into the session. See the new Session 207 documentation for details. 208 209* **Implicit Order By Is Removed**. This will impact ORM 210 users who rely upon SA's "implicit ordering" behavior, 211 which states that all Query objects which don't have an 212 ``order_by()`` will ORDER BY the "id" or "oid" column of 213 the primary mapped table, and all lazy/eagerly loaded 214 collections apply a similar ordering. In 0.5, automatic 215 ordering must be explicitly configured on ``mapper()`` and 216 ``relation()`` objects (if desired), or otherwise when 217 using ``Query``. 218 219 To convert an 0.4 mapping to 0.5, such that its ordering 220 behavior will be extremely similar to 0.4 or previous, use 221 the ``order_by`` setting on ``mapper()`` and 222 ``relation()``: 223 224 :: 225 226 mapper(User, users, properties={ 227 'addresses':relation(Address, order_by=addresses.c.id) 228 }, order_by=users.c.id) 229 230 To set ordering on a backref, use the ``backref()`` 231 function: 232 233 :: 234 235 'keywords':relation(Keyword, secondary=item_keywords, 236 order_by=keywords.c.name, backref=backref('items', order_by=items.c.id)) 237 238 Using declarative ? To help with the new ``order_by`` 239 requirement, ``order_by`` and friends can now be set using 240 strings which are evaluated in Python later on (this works 241 **only** with declarative, not plain mappers): 242 243 :: 244 245 class MyClass(MyDeclarativeBase): 246 ... 247 'addresses':relation("Address", order_by="Address.id") 248 249 It's generally a good idea to set ``order_by`` on 250 ``relation()s`` which load list-based collections of 251 items, since that ordering cannot otherwise be affected. 252 Other than that, the best practice is to use 253 ``Query.order_by()`` to control ordering of the primary 254 entities being loaded. 255 256* **Session is now 257 autoflush=True/autoexpire=True/autocommit=False.** - To 258 set it up, just call ``sessionmaker()`` with no arguments. 259 The name ``transactional=True`` is now 260 ``autocommit=False``. Flushes occur upon each query 261 issued (disable with ``autoflush=False``), within each 262 ``commit()`` (as always), and before each 263 ``begin_nested()`` (so rolling back to the SAVEPOINT is 264 meaningful). All objects are expired after each 265 ``commit()`` and after each ``rollback()``. After 266 rollback, pending objects are expunged, deleted objects 267 move back to persistent. These defaults work together 268 very nicely and there's really no more need for old 269 techniques like ``clear()`` (which is renamed to 270 ``expunge_all()`` as well). 271 272 P.S.: sessions are now reusable after a ``rollback()``. 273 Scalar and collection attribute changes, adds and deletes 274 are all rolled back. 275 276* **session.add() replaces session.save(), session.update(), 277 session.save_or_update().** - the 278 ``session.add(someitem)`` and ``session.add_all([list of 279 items])`` methods replace ``save()``, ``update()``, and 280 ``save_or_update()``. Those methods will remain 281 deprecated throughout 0.5. 282 283* **backref configuration made less verbose.** - The 284 ``backref()`` function now uses the ``primaryjoin`` and 285 ``secondaryjoin`` arguments of the forwards-facing 286 ``relation()`` when they are not explicitly stated. It's 287 no longer necessary to specify 288 ``primaryjoin``/``secondaryjoin`` in both directions 289 separately. 290 291* **Simplified polymorphic options.** - The ORM's 292 "polymorphic load" behavior has been simplified. In 0.4, 293 mapper() had an argument called ``polymorphic_fetch`` 294 which could be configured as ``select`` or ``deferred``. 295 This option is removed; the mapper will now just defer any 296 columns which were not present in the SELECT statement. 297 The actual SELECT statement used is controlled by the 298 ``with_polymorphic`` mapper argument (which is also in 0.4 299 and replaces ``select_table``), as well as the 300 ``with_polymorphic()`` method on ``Query`` (also in 0.4). 301 302 An improvement to the deferred loading of inheriting 303 classes is that the mapper now produces the "optimized" 304 version of the SELECT statement in all cases; that is, if 305 class B inherits from A, and several attributes only 306 present on class B have been expired, the refresh 307 operation will only include B's table in the SELECT 308 statement and will not JOIN to A. 309 310* The ``execute()`` method on ``Session`` converts plain 311 strings into ``text()`` constructs, so that bind 312 parameters may all be specified as ":bindname" without 313 needing to call ``text()`` explicitly. If "raw" SQL is 314 desired here, use ``session.connection().execute("raw 315 text")``. 316 317* ``session.Query().iterate_instances()`` has been renamed 318 to just ``instances()``. The old ``instances()`` method 319 returning a list instead of an iterator no longer exists. 320 If you were relying on that behavior, you should use 321 ``list(your_query.instances())``. 322 323Extending the ORM 324================= 325 326In 0.5 we're moving forward with more ways to modify and 327extend the ORM. Heres a summary: 328 329* **MapperExtension.** - This is the classic extension 330 class, which remains. Methods which should rarely be 331 needed are ``create_instance()`` and 332 ``populate_instance()``. To control the initialization of 333 an object when it's loaded from the database, use the 334 ``reconstruct_instance()`` method, or more easily the 335 ``@reconstructor`` decorator described in the 336 documentation. 337 338* **SessionExtension.** - This is an easy to use extension 339 class for session events. In particular, it provides 340 ``before_flush()``, ``after_flush()`` and 341 ``after_flush_postexec()`` methods. This usage is 342 recommended over ``MapperExtension.before_XXX`` in many 343 cases since within ``before_flush()`` you can modify the 344 flush plan of the session freely, something which cannot 345 be done from within ``MapperExtension``. 346 347* **AttributeExtension.** - This class is now part of the 348 public API, and allows the interception of userland events 349 on attributes, including attribute set and delete 350 operations, and collection appends and removes. It also 351 allows the value to be set or appended to be modified. 352 The ``@validates`` decorator, described in the 353 documentation, provides a quick way to mark any mapped 354 attributes as being "validated" by a particular class 355 method. 356 357* **Attribute Instrumentation Customization.** - An API is 358 provided for ambitious efforts to entirely replace 359 SQLAlchemy's attribute instrumentation, or just to augment 360 it in some cases. This API was produced for the purposes 361 of the Trellis toolkit, but is available as a public API. 362 Some examples are provided in the distribution in the 363 ``/examples/custom_attributes`` directory. 364 365Schema/Types 366============ 367 368* **String with no length no longer generates TEXT, it 369 generates VARCHAR** - The ``String`` type no longer 370 magically converts into a ``Text`` type when specified 371 with no length. This only has an effect when CREATE TABLE 372 is issued, as it will issue ``VARCHAR`` with no length 373 parameter, which is not valid on many (but not all) 374 databases. To create a TEXT (or CLOB, i.e. unbounded 375 string) column, use the ``Text`` type. 376 377* **PickleType() with mutable=True requires an __eq__() 378 method** - The ``PickleType`` type needs to compare values 379 when mutable=True. The method of comparing 380 ``pickle.dumps()`` is inefficient and unreliable. If an 381 incoming object does not implement ``__eq__()`` and is 382 also not ``None``, the ``dumps()`` comparison is used but 383 a warning is raised. For types which implement 384 ``__eq__()`` which includes all dictionaries, lists, etc., 385 comparison will use ``==`` and is now reliable by default. 386 387* **convert_bind_param() and convert_result_value() methods 388 of TypeEngine/TypeDecorator are removed.** - The O'Reilly 389 book unfortunately documented these methods even though 390 they were deprecated post 0.3. For a user-defined type 391 which subclasses ``TypeEngine``, the ``bind_processor()`` 392 and ``result_processor()`` methods should be used for 393 bind/result processing. Any user defined type, whether 394 extending ``TypeEngine`` or ``TypeDecorator``, which uses 395 the old 0.3 style can be easily adapted to the new style 396 using the following adapter: 397 398 :: 399 400 class AdaptOldConvertMethods(object): 401 """A mixin which adapts 0.3-style convert_bind_param and 402 convert_result_value methods 403 404 """ 405 def bind_processor(self, dialect): 406 def convert(value): 407 return self.convert_bind_param(value, dialect) 408 return convert 409 410 def result_processor(self, dialect): 411 def convert(value): 412 return self.convert_result_value(value, dialect) 413 return convert 414 415 def convert_result_value(self, value, dialect): 416 return value 417 418 def convert_bind_param(self, value, dialect): 419 return value 420 421 To use the above mixin: 422 423 :: 424 425 class MyType(AdaptOldConvertMethods, TypeEngine): 426 # ... 427 428* The ``quote`` flag on ``Column`` and ``Table`` as well as 429 the ``quote_schema`` flag on ``Table`` now control quoting 430 both positively and negatively. The default is ``None``, 431 meaning let regular quoting rules take effect. When 432 ``True``, quoting is forced on. When ``False``, quoting 433 is forced off. 434 435* Column ``DEFAULT`` value DDL can now be more conveniently 436 specified with ``Column(..., server_default='val')``, 437 deprecating ``Column(..., PassiveDefault('val'))``. 438 ``default=`` is now exclusively for Python-initiated 439 default values, and can coexist with server_default. A 440 new ``server_default=FetchedValue()`` replaces the 441 ``PassiveDefault('')`` idiom for marking columns as 442 subject to influence from external triggers and has no DDL 443 side effects. 444 445* SQLite's ``DateTime``, ``Time`` and ``Date`` types now 446 **only accept datetime objects, not strings** as bind 447 parameter input. If you'd like to create your own 448 "hybrid" type which accepts strings and returns results as 449 date objects (from whatever format you'd like), create a 450 ``TypeDecorator`` that builds on ``String``. If you only 451 want string-based dates, just use ``String``. 452 453* Additionally, the ``DateTime`` and ``Time`` types, when 454 used with SQLite, now represent the "microseconds" field 455 of the Python ``datetime.datetime`` object in the same 456 manner as ``str(datetime)`` - as fractional seconds, not a 457 count of microseconds. That is: 458 459 :: 460 461 dt = datetime.datetime(2008, 6, 27, 12, 0, 0, 125) # 125 usec 462 463 # old way 464 '2008-06-27 12:00:00.125' 465 466 # new way 467 '2008-06-27 12:00:00.000125' 468 469 So if an existing SQLite file-based database intends to be 470 used across 0.4 and 0.5, you either have to upgrade the 471 datetime columns to store the new format (NOTE: please 472 test this, I'm pretty sure its correct): 473 474 :: 475 476 UPDATE mytable SET somedatecol = 477 substr(somedatecol, 0, 19) || '.' || substr((substr(somedatecol, 21, -1) / 1000000), 3, -1); 478 479 or, enable "legacy" mode as follows: 480 481 :: 482 483 from sqlalchemy.databases.sqlite import DateTimeMixin 484 DateTimeMixin.__legacy_microseconds__ = True 485 486Connection Pool no longer threadlocal by default 487================================================ 488 4890.4 has an unfortunate default setting of 490"pool_threadlocal=True", leading to surprise behavior when, 491for example, using multiple Sessions within a single thread. 492This flag is now off in 0.5. To re-enable 0.4's behavior, 493specify ``pool_threadlocal=True`` to ``create_engine()``, or 494alternatively use the "threadlocal" strategy via 495``strategy="threadlocal"``. 496 497\*args Accepted, \*args No Longer Accepted 498========================================== 499 500The policy with ``method(\*args)`` vs. ``method([args])`` 501is, if the method accepts a variable-length set of items 502which represent a fixed structure, it takes ``\*args``. If 503the method accepts a variable-length set of items that are 504data-driven, it takes ``[args]``. 505 506* The various Query.options() functions ``eagerload()``, 507 ``eagerload_all()``, ``lazyload()``, ``contains_eager()``, 508 ``defer()``, ``undefer()`` all accept variable-length 509 ``\*keys`` as their argument now, which allows a path to 510 be formulated using descriptors, ie.: 511 512 :: 513 514 query.options(eagerload_all(User.orders, Order.items, Item.keywords)) 515 516 A single array argument is still accepted for backwards 517 compatibility. 518 519* Similarly, the ``Query.join()`` and ``Query.outerjoin()`` 520 methods accept a variable length \*args, with a single 521 array accepted for backwards compatibility: 522 523 :: 524 525 query.join('orders', 'items') 526 query.join(User.orders, Order.items) 527 528* the ``in_()`` method on columns and similar only accepts a 529 list argument now. It no longer accepts ``\*args``. 530 531Removed 532======= 533 534* **entity_name** - This feature was always problematic and 535 rarely used. 0.5's more deeply fleshed out use cases 536 revealed further issues with ``entity_name`` which led to 537 its removal. If different mappings are required for a 538 single class, break the class into separate subclasses and 539 map them separately. An example of this is at 540 [wiki:UsageRecipes/EntityName]. More information 541 regarding rationale is described at https://groups.google.c 542 om/group/sqlalchemy/browse_thread/thread/9e23a0641a88b96d? 543 hl=en . 544 545* **get()/load() cleanup** 546 547 548 The ``load()`` method has been removed. Its 549 functionality was kind of arbitrary and basically copied 550 from Hibernate, where it's also not a particularly 551 meaningful method. 552 553 To get equivalent functionality: 554 555 :: 556 557 x = session.query(SomeClass).populate_existing().get(7) 558 559 ``Session.get(cls, id)`` and ``Session.load(cls, id)`` 560 have been removed. ``Session.get()`` is redundant vs. 561 ``session.query(cls).get(id)``. 562 563 ``MapperExtension.get()`` is also removed (as is 564 ``MapperExtension.load()``). To override the 565 functionality of ``Query.get()``, use a subclass: 566 567 :: 568 569 class MyQuery(Query): 570 def get(self, ident): 571 # ... 572 573 session = sessionmaker(query_cls=MyQuery)() 574 575 ad1 = session.query(Address).get(1) 576 577* ``sqlalchemy.orm.relation()`` 578 579 580 The following deprecated keyword arguments have been 581 removed: 582 583 foreignkey, association, private, attributeext, is_backref 584 585 In particular, ``attributeext`` is replaced with 586 ``extension`` - the ``AttributeExtension`` class is now in 587 the public API. 588 589* ``session.Query()`` 590 591 592 The following deprecated functions have been removed: 593 594 list, scalar, count_by, select_whereclause, get_by, 595 select_by, join_by, selectfirst, selectone, select, 596 execute, select_statement, select_text, join_to, join_via, 597 selectfirst_by, selectone_by, apply_max, apply_min, 598 apply_avg, apply_sum 599 600 Additionally, the ``id`` keyword argument to ``join()``, 601 ``outerjoin()``, ``add_entity()`` and ``add_column()`` has 602 been removed. To target table aliases in ``Query`` to 603 result columns, use the ``aliased`` construct: 604 605 :: 606 607 from sqlalchemy.orm import aliased 608 address_alias = aliased(Address) 609 print(session.query(User, address_alias).join((address_alias, User.addresses)).all()) 610 611* ``sqlalchemy.orm.Mapper`` 612 613 614 * instances() 615 616 617 * get_session() - this method was not very noticeable, but 618 had the effect of associating lazy loads with a 619 particular session even if the parent object was 620 entirely detached, when an extension such as 621 ``scoped_session()`` or the old ``SessionContextExt`` 622 was used. It's possible that some applications which 623 relied upon this behavior will no longer work as 624 expected; but the better programming practice here is 625 to always ensure objects are present within sessions if 626 database access from their attributes are required. 627 628* ``mapper(MyClass, mytable)`` 629 630 631 Mapped classes no are longer instrumented with a "c" class 632 attribute; e.g. ``MyClass.c`` 633 634* ``sqlalchemy.orm.collections`` 635 636 637 The _prepare_instrumentation alias for 638 prepare_instrumentation has been removed. 639 640* ``sqlalchemy.orm`` 641 642 643 Removed the ``EXT_PASS`` alias of ``EXT_CONTINUE``. 644 645* ``sqlalchemy.engine`` 646 647 648 The alias from ``DefaultDialect.preexecute_sequences`` to 649 ``.preexecute_pk_sequences`` has been removed. 650 651 The deprecated engine_descriptors() function has been 652 removed. 653 654* ``sqlalchemy.ext.activemapper`` 655 656 657 Module removed. 658 659* ``sqlalchemy.ext.assignmapper`` 660 661 662 Module removed. 663 664* ``sqlalchemy.ext.associationproxy`` 665 666 667 Pass-through of keyword args on the proxy's 668 ``.append(item, \**kw)`` has been removed and is now 669 simply ``.append(item)`` 670 671* ``sqlalchemy.ext.selectresults``, 672 ``sqlalchemy.mods.selectresults`` 673 674 Modules removed. 675 676* ``sqlalchemy.ext.declarative`` 677 678 679 ``declared_synonym()`` removed. 680 681* ``sqlalchemy.ext.sessioncontext`` 682 683 684 Module removed. 685 686* ``sqlalchemy.log`` 687 688 689 The ``SADeprecationWarning`` alias to 690 ``sqlalchemy.exc.SADeprecationWarning`` has been removed. 691 692* ``sqlalchemy.exc`` 693 694 695 ``exc.AssertionError`` has been removed and usage replaced 696 by the Python built-in of the same name. 697 698* ``sqlalchemy.databases.mysql`` 699 700 701 The deprecated ``get_version_info`` dialect method has 702 been removed. 703 704Renamed or Moved 705================ 706 707* ``sqlalchemy.exceptions`` is now ``sqlalchemy.exc`` 708 709 710 The module may still be imported under the old name until 711 0.6. 712 713* ``FlushError``, ``ConcurrentModificationError``, 714 ``UnmappedColumnError`` -> sqlalchemy.orm.exc 715 716 These exceptions moved to the orm package. Importing 717 'sqlalchemy.orm' will install aliases in sqlalchemy.exc 718 for compatibility until 0.6. 719 720* ``sqlalchemy.logging`` -> ``sqlalchemy.log`` 721 722 723 This internal module was renamed. No longer needs to be 724 special cased when packaging SA with py2app and similar 725 tools that scan imports. 726 727* ``session.Query().iterate_instances()`` -> 728 ``session.Query().instances()``. 729 730Deprecated 731========== 732 733* ``Session.save()``, ``Session.update()``, 734 ``Session.save_or_update()`` 735 736 All three replaced by ``Session.add()`` 737 738* ``sqlalchemy.PassiveDefault`` 739 740 741 Use ``Column(server_default=...)`` Translates to 742 sqlalchemy.DefaultClause() under the hood. 743 744* ``session.Query().iterate_instances()``. It has been 745 renamed to ``instances()``. 746 747