Commits

Mike Bayer committed 90b55df

session docs, CHANGES updates

  • Participants
  • Parent commits 10000ea

Comments (0)

Files changed (5)

         - the aliasing logic used by eager loading has been generalized, so that
           it also adds full automatic aliasing support to Query.  It's no longer 
           necessary to create an explicit Alias to join to the same tables multiple times;
-          *even for self-referential relationships!!*
+          *even for self-referential relationships*.
+          
             - join() and outerjoin() take arguments "aliased=True".  this causes
             their joins to be built on aliased tables; subsequent calls
             to filter() and filter_by() will translate all table expressions
 
         - added eagerload_all(), allows eagerload_all('x.y.z') to specify eager
           loading of all properties in the given path
+
+    - major overhaul for Session:
+        - new function which "configures" a session called "sessionmaker()".  send
+          various keyword arguments to this function once, returns a new class which
+          creates a Session against that stereotype.
+        - SessionTransaction removed from "public" API.  you now can call begin()/
+          commit()/rollback() on the Session itself.
+        - Session also supports SAVEPOINT transactions; call begin_nested().
+        - Session supports two-phase commit behavior when vertically or horizontally
+          partitioning (i.e., using more than one engine).  use twophase=True.
+        - Session flag "transactional=True" produces a session which always places itself
+          into a transaction when first used.  upon commit()/rollback()/close(), the 
+          transaction ends; but begins again on the next usage.
+        - Session supports "autoflush=True".  this issues a flush() before each query.
+          Use in conjunction with transactional, and you can just save()/update()
+          and then query, the new objects will be there.  use commit() at the end
+          (or flush() if non-transactional) to flush remaining changes.
+        - new scoped_session() function replaces SessionContext and assignmapper.  
+          builds onto "sessionmaker()" concept to produce a class whos Session()
+          construction returns the thread-local session.  Or, call all Session methods
+          as class methods, i.e. Session.save(foo); Session.commit().  just like the
+          old "objectstore" days.
+        - added new "binds" argument to Session to support configuration of multiple 
+          binds with sessionmaker() function.
     
+    - query-based relation()s available with dynamic_loader().  This is a *writable* 
+      collection (supporting append() and remove()) which is also a live Query object
+      when accessed for reads.  Ideal for dealing with very large collections where
+      only partial loading is desired.
+          
+    - flush-embedded inline INSERT/UPDATE expressions.  assign any SQL expression, like
+      "sometable.c.column + 1", to an instance's attribute.  Upon flush(), the mapper
+      detects the expression and embeds it directly in the INSERT or UPDATE statement;
+      the attribute gets deferred on the instance so it loads the new value 
+      the next time you access it.
+      
     - a rudimental sharding (horizontal scaling) system is introduced.  This system
       uses a modified Session which can distribute read and write operations among
       multiple databases, based on user-defined functions defining the 
       created/mapped to a single attribute, comprised of the values
       correponding to *columns [ticket:211]
 
-    - added inline UPDATE/INSERT clauses, settable as regular object attributes.
-      the clause gets executed inline during a flush().
-
     - improved support for custom column_property() attributes which
       feature correlated subqueries...work better with eager loading now.
 
           argument, which can be set to 'select' or 'deferred'
 
     - it's now possible to map only a subset of available selectable columns
-      onto mapper properties [ticket:696].
+      onto mapper properties, using include_columns/exclude_columns
+      [ticket:696].
 
     - added undefer_group() MapperOption, sets a set of "deferred"
       columns joined by a "group" to load as "undeferred".
-      
-    - session enhancements/fixes:
-        - session can be bound to Connections
     
     - rewrite of the "deterministic alias name" logic to be part of the 
     SQL layer, produces much simpler alias and label names more in the
   - all "type" keyword arguments, such as those to bindparam(), column(),
     Column(), and func.<something>(), renamed to "type_".  those objects
     still name their "type" attribute as "type".
+    
   - transactions:
     - added context manager (with statement) support for transactions
     - added support for two phase commit, works with mysql and postgres so far.
     - added a subtransaction implementation that uses savepoints.
     - added support for savepoints.
+    
   - MetaData:
     - Tables can be reflected from the database en-masse without
       declaring them in advance.  MetaData(engine, reflect=True) will load
     - DynamicMetaData has been renamed to ThreadLocalMetaData
     - The ThreadLocalMetaData constructor now takes no arguments.
     - BoundMetaData has been removed- regular MetaData is equivalent
+    
   - Numeric and Float types now have an "asdecimal" flag; defaults to 
     True for Numeric, False for Float.  when True, values are returned as
     decimal.Decimal objects; when False, values are returned as float().
     the defaults of True/False are already the behavior for PG and MySQL's
     DBAPI modules. [ticket:646]
+    
   - new SQL operator implementation which removes all hardcoded operators
     from expression structures and moves them into compilation; 
     allows greater flexibility of operator compilation; for example, "+" 
     compiles to "||" when used in a string context, or "concat(a,b)" on 
     MySQL; whereas in a numeric context it compiles to "+".  fixes [ticket:475].
+    
   - "anonymous" alias and label names are now generated at SQL compilation
     time in a completely deterministic fashion...no more random hex IDs
+    
   - significant architectural overhaul to SQL elements (ClauseElement).  
     all elements share  a common "mutability" framework which allows a 
     consistent approach to in-place modifications of elements as well as 
     generative behavior.  improves stability of the ORM which makes 
     heavy usage of mutations to SQL expressions.  
+    
   - select() and union()'s now have "generative" behavior.  methods like
     order_by() and group_by() return a *new* instance - the original instance
     is left unchanged.  non-generative methods remain as well.  
+    
   - the internals of select/union vastly simplified - all decision making
     regarding "is subquery" and "correlation" pushed to SQL generation phase.
     select() elements are now *never* mutated by their enclosing containers
     or by any dialect's compilation process [ticket:52] [ticket:569]
+    
   - select(scalar=True) argument is deprecated; use select(..).as_scalar().
     the resulting object obeys the full "column" interface and plays better
     within expressions
+    
   - added select().with_prefix('foo') allowing any set of keywords to be
     placed before the columns clause of the SELECT [ticket:504]
+    
   - added array slice support to row[<index>] [ticket:686]
+  
   - result sets make a better attempt at matching the DBAPI types present
     in cursor.description to the TypeEngine objects defined by the dialect,
     which are then used for result-processing. Note this only takes effect 
     for textual SQL; constructed SQL statements always have an explicit type map.  
+    
   - result sets from CRUD operations close their underlying cursor immediately.
     will also autoclose the connection if defined for the operation; this 
     allows more efficient usage of connections for successive CRUD operations
     with less chance of "dangling connections".
+    
   - Column defaults and onupdate Python functions (i.e. passed to ColumnDefault)
     may take zero or one arguments; the one argument is the ExecutionContext,
     from which you can call "context.parameters[someparam]" to access the other
-    bind parameter values affixed to the statement [ticket:559]
+    bind parameter values affixed to the statement [ticket:559].  The
+    connection used for the execution is available as well so that you can 
+    pre-execute statements.
+    
   - added "explcit" create/drop/execute support for sequences 
     (i.e. you can pass a "connectable" to each of those methods
     on Sequence)
+    
   - better quoting of identifiers when manipulating schemas
+  
   - standardized the behavior for table reflection where types can't be located;
     NullType is substituted instead, warning is raised.
+
   - ColumnCollection (i.e. the 'c' attribute on tables) follows dictionary
     semantics for "__contains__" [ticket:606]
     
     lifetime of the underlying DBAPI connection
   - removed auto_close_cursors and disallow_open_cursors arguments from Pool;
     reduces overhead as cursors are normally closed by ResultProxy and Connection.
+
 - extensions
   - proxyengine is temporarily removed, pending an actually working
     replacement.
     SelectResultsExt still exist but just return a slightly modified
     Query object for backwards-compatibility.  join_to() method 
     from SelectResults isn't present anymore, need to use join(). 
+
 - postgres
   - Added PGArray datatype for using postgres array datatypes
+
 - oracle
   - very rudimental support for OUT parameters added; use sql.outparam(name, type)
     to set up an OUT parameter, just like bindparam(); after execution, values are

doc/build/content/mappers.txt

 
 The first mapper created for a certain class is known as that class's "primary mapper."  Other mappers can be created as well, these come in two varieties.
 
-* **secondary mapper** - this is a mapper that must be constructed with the keyword argument `non_primary=True`, and represents a load-only mapper.  Objects that are loaded with a secondary mapper will have their save operation processed by the primary mapper.  It is also invalid to add new `relation()`s to a non-primary mapper. To use this mapper with the Session, specify it to the `query` method:
+* **secondary mapper**
+    this is a mapper that must be constructed with the keyword argument `non_primary=True`, and represents a load-only mapper.  Objects that are loaded with a secondary mapper will have their save operation processed by the primary mapper.  It is also invalid to add new `relation()`s to a non-primary mapper. To use this mapper with the Session, specify it to the `query` method:
 
-example:
+    example:
 
-    {python}
-    # primary mapper
-    mapper(User, users_table)
+        {python}
+        # primary mapper
+        mapper(User, users_table)
     
-    # make a secondary mapper to load User against a join
-    othermapper = mapper(User, users_table.join(someothertable), non_primary=True)
+        # make a secondary mapper to load User against a join
+        othermapper = mapper(User, users_table.join(someothertable), non_primary=True)
     
-    # select
-    result = session.query(othermapper).select()
+        # select
+        result = session.query(othermapper).select()
 
-The "non primary mapper" is a rarely needed feature of SQLAlchemy; in most cases, the `Query` object can produce any kind of query that's desired.  It's recommended that a straight `Query` be used in place of a non-primary mapper unless the mapper approach is absolutely needed.  Current use cases for the "non primary mapper" are when you want to map the class to a particular select statement or view to which additional query criterion can be added, and for when the particular mapped select statement or view is to be placed in a `relation()` of a parent mapper.
+    The "non primary mapper" is a rarely needed feature of SQLAlchemy; in most cases, the `Query` object can produce any kind of query that's desired.  It's recommended that a straight `Query` be used in place of a non-primary mapper unless the mapper approach is absolutely needed.  Current use cases for the "non primary mapper" are when you want to map the class to a particular select statement or view to which additional query criterion can be added, and for when the particular mapped select statement or view is to be placed in a `relation()` of a parent mapper.
 
-* **entity name mapper** - this is a mapper that is a fully functioning primary mapper for a class, which is distinguished from the regular primary mapper by an `entity_name` parameter.  Instances loaded with this mapper will be totally managed by this new mapper and have no connection to the original one.  Most methods on `Session` include an optional `entity_name` parameter in order to specify this condition.
+* **entity name mapper**
+    this is a mapper that is a fully functioning primary mapper for a class, which is distinguished from the regular primary mapper by an `entity_name` parameter.  Instances loaded with this mapper will be totally managed by this new mapper and have no connection to the original one.  Most methods on `Session` include an optional `entity_name` parameter in order to specify this condition.
 
-example:
+    example:
 
-    {python}
-    # primary mapper
-    mapper(User, users_table)
+        {python}
+        # primary mapper
+        mapper(User, users_table)
     
-    # make an entity name mapper that stores User objects in another table
-    mapper(User, alternate_users_table, entity_name='alt')
+        # make an entity name mapper that stores User objects in another table
+        mapper(User, alternate_users_table, entity_name='alt')
     
-    # make two User objects
-    user1 = User()
-    user2 = User()
+        # make two User objects
+        user1 = User()
+        user2 = User()
     
-    # save one in in the "users" table
-    session.save(user1)
+        # save one in in the "users" table
+        session.save(user1)
     
-    # save the other in the "alternate_users_table"
-    session.save(user2, entity_name='alt')
+        # save the other in the "alternate_users_table"
+        session.save(user2, entity_name='alt')
     
-    session.flush()
+        session.flush()
     
-    # select from the alternate mapper
-    session.query(User, entity_name='alt').select()
+        # select from the alternate mapper
+        session.query(User, entity_name='alt').select()
 
-Use the "entity name" mapper when different instances of the same class are persisted in completely different tables.  The "entity name" approach can also perform limited levels of horizontal partitioning as well.   A more comprehensive approach to horizontal partitioning is provided by the Sharding API.
+    Use the "entity name" mapper when different instances of the same class are persisted in completely different tables.  The "entity name" approach can also perform limited levels of horizontal partitioning as well.   A more comprehensive approach to horizontal partitioning is provided by the Sharding API.
 
 #### Extending Mapper {@name=extending}
 

doc/build/content/session.txt

 
 ## Getting a Session
 
-[This section is under construction !]
+The `Session` object exists just as a regular Python object, which can be directly instantiated.  However, it takes a fair amount of keyword options, several of which you probably want to set explicitly.  It's fairly inconvenient to deal with the "configuration" of a session every time you want to create one.  Therefore, SQLAlchemy recommends the usage of a helper function called `sessionmaker()`, which typically you call only once for the lifespan of an application.  This function creates a customized `Session` subclass for you, with your desired configurational arguments pre-loaded.  Then, whenever you need a new `Session`, you use your custom `Session` class with no arguments to create the session.
 
 ### Using a sessionmaker() Configuration {@name=sessionmaker}
 
+The usage of `sessionmaker()` is illustrated below:
+
     {python}
-    Session = sessionmaker(autoflush=True, transactional=True, **kwargs)
+    from sqlalchemy.orm import sessionmaker
     
+    # create a configured "Session" class
+    Session = sessionmaker(autoflush=True, transactional=True)
+
+    # create a Session
     sess = Session()
+    
     # work with sess
+    sess.save(x)
+    sess.commit()
     
+    # close when finished
     sess.close()
 
-[Generated docstrings for sessionmaker()](rel:docstrings_sqlalchemy.orm_modfunc_sessionmaker)
-    
+Above, the `sessionmaker` call creates a class for us, which we assign to the name `Session`.  This class is a subclass of the actual `sqlalchemy.orm.session.Session` class, which will instantiate with the default arguments of `autoflush=True` and `transactional=True`.
+
+When you write your application, place the call to `sessionmaker()` somewhere global, and then make your new `Session` class available to the rest of your application.
+
 ### Binding Session to an Engine or Connection {@name=binding}
 
-Bind to an engine:
+In our previous example regarding `sessionmaker()`, nowhere did we specify how our session would connect to our database.  When the session is configured in this manner, it will look for a database engine to connect with via the `Table` objects that it works with - the chapter called [metadata_tables_binding](rel:metadata_tables_binding) describes how to associate `Table` objects directly with a source of database connections.
+
+However, it is often more straightforward to explicitly tell the session what database engine (or engines) you'd like it to communicate with.  This is particularly handy with multiple-database scenarios where the session can be used as the central point of configuration.  To acheive this, the constructor keyword `bind` is used for a basic single-database configuration:
 
     {python}
-    Session = sessionmaker(...)
+    # create engine
+    engine = create_engine('postgres://...')
+    
+    # bind custom Session class to the engine
+    Session = sessionmaker(bind=engine, autoflush=True, transactional=True)
+    
+    # work with the session
+    sess = Session()
+    
+One common issue with the above scenario is that an application will often organize its global imports before it ever connects to a database.  Since the `Session` class created by `sessionmaker()` is meant to be a global application object (note we are saying the session *class*, not a session *instance*), we may not have a `bind` argument available.  For this, the `Session` class returned by `sessionmaker()` supports post-configuration of all options, through its method `configure()`:
+
+    {python}
+    # configure Session class with desired options
+    Session = sessionmaker(autoflush=True, transactional=True)
+
+    # later, we create the engine
+    engine = create_engine('postgres://...')
+    
+    # associate it with our custom Session class
     Session.configure(bind=engine)
 
-Bind to a connection:
+    # work with the session
+    sess = Session()
+
+The `Session` also has the ability to be bound to multiple engines.   Descriptions of these scenarios are described in [unitofwork_partitioning](rel:unitofwork_partitioning).
+
+
+#### Binding Session to a Connection {@name=connection}
+
+The examples involving `bind` so far are dealing with the `Engine` object, which is, like the `Session` class itself, a global configurational object.  The `Session` can also be bound to an individual database `Connection`.  The reason you might want to do this is if your application controls the boundaries of transactions using distinct `Transaction` objects (these objects are described in [dbengine_transactions](rel:dbengine_transactions)).  You'd have a transactional `Connection`, and then you'd want to work with an ORM-level `Session` which participates in that transaction.  Since `Connection` is definitely not a globally-scoped object in all but the most rudimental commandline applications, you can bind an individual `Session()` instance to a particular `Connection` not at class configuration time, but at session instance construction time:
 
     {python}
-    Session = sessionmaker(...)
+    # global application scope.  create Session class, engine
+    Session = sessionmaker(autoflush=True, transactional=True)
+
+    engine = create_engine('postgres://...')
     
+    ...
+    
+    # local scope, such as within a controller function
+    
+    # connect to the database
     connection = engine.connect()
     
+    # bind an individual Session to the connection
     sess = Session(bind=connection)
-    
+
 ### Using create_session() {@name=createsession}
 
-This function is a "lower level" version of `sessionmaker()`.  It creates a new `Session` directly:
+As an alternative to `sessionmaker()`, `create_session()` exists literally as a function which calls the normal `Session` constructor directly.  All arguments are passed through and the new `Session` object is returned:
 
     {python}
-    session = create_session(**kwargs)
-
-[Generated docstrings for create_session()](rel:docstrings_sqlalchemy.orm_modfunc_create_session)
+    session = create_session(bind=myengine)
+    
+The `create_session()` function doesn't add any functionality to the regular `Session`, it just sets up a default argument set of `autoflush=False, transactional=False`.  But also, by calling `create_session()` instead of instantiating `Session` directly, you leave room in your application to change the type of session which the function creates.
 
     
 ## Using the Session 
 ### Frequently Asked Questions {@name=faq}
 
 * When do I make a `sessionmaker` ?
-  
- Just one time, somewhere in your application's global scope.  It should be looked upon as part of your application's configuration.  If your application has three .py files in a package, you could, for example, place the `sessionmaker` line in your `__init__.py` file; from that point on your other modules say "from mypackage import Session".   That way, everyone else just uses `Session()`, and the configuration of that session is controlled by that central point.
-  
- If your application starts up, does imports, but does not know what database it's going to be connecting to, you can bind the `Session` at the "class" level to the engine later on, using `configure()`.
-  
-In the examples in this section, we will frequently show the `sessionmaker` being created right above the line where we actually create the `Session`.  But that's just for example's sake !  In reality, the `sessionmaker` would be somewhere at the module level, and your individual `Session()` calls would be sprinkled all throughout your app, such as in a web application within each controller method.
-  
+
+    Just one time, somewhere in your application's global scope.  It should be looked upon as part of your application's configuration.  If your application has three .py files in a package, you could, for example, place the `sessionmaker` line in your `__init__.py` file; from that point on your other modules say "from mypackage import Session".   That way, everyone else just uses `Session()`, and the configuration of that session is controlled by that central point.
+
+    If your application starts up, does imports, but does not know what database it's going to be connecting to, you can bind the `Session` at the "class" level to the engine later on, using `configure()`.
+
+    In the examples in this section, we will frequently show the `sessionmaker` being created right above the line where we actually invoke `Session()`.  But that's just for example's sake !  In reality, the `sessionmaker` would be somewhere at the module level, and your individual `Session()` calls would be sprinkled all throughout your app, such as in a web application within each controller method.
+
 * When do I make a `Session` ? 
-  
-You typically create a `Session` when you first need to talk to your database, and want to save some objects or load some existing ones.  Then, you work with it, save your changes, and then dispose of it....or at the very least `close()` it.  It's not a "global" kind of object, and should be handled more like a "local variable", as it's generally **not** safe to use with concurrent threads.  Sessions are very inexpensive to make, and don't use any resources whatsoever until they are first used...so create some !
-  
-There is also a pattern whereby you're using a **contextual session**, this is described later in [unitofwork_contextual](rel:unitofwork_contextual).  In this pattern, a helper object is maintaining a `Session` for you, most commonly one that is local to the current thread (and sometimes also local to an application instance).  SQLAlchemy 0.4 has worked this pattern out such that it still *looks* like you're creating a new session as you need one...so in that case, it's still a guaranteed win to just say `Session()` whenever you want a session.  
+
+    You typically invoke `Session()` when you first need to talk to your database, and want to save some objects or load some existing ones.  Then, you work with it, save your changes, and then dispose of it....or at the very least `close()` it.  It's not a "global" kind of object, and should be handled more like a "local variable", as it's generally **not** safe to use with concurrent threads.  Sessions are very inexpensive to make, and don't use any resources whatsoever until they are first used...so create some !
+
+    There is also a pattern whereby you're using a **contextual session**, this is described later in [unitofwork_contextual](rel:unitofwork_contextual).  In this pattern, a helper object is maintaining a `Session` for you, most commonly one that is local to the current thread (and sometimes also local to an application instance).  SQLAlchemy 0.4 has worked this pattern out such that it still *looks* like you're creating a new session as you need one...so in that case, it's still a guaranteed win to just say `Session()` whenever you want a session.  
 
 * Is the Session a cache ? 
-  
-Yeee...no.  It's somewhat used as a cache, in that it implements the identity map pattern, and stores objects keyed to their primary key.  However, it doesn't do any kind of query caching.  This means, if you say `session.query(Foo).filter_by(name='bar')`, even if `Foo(name='bar')` is right there, in the identity map, the session has no idea about that.  It has to issue SQL to the database, get the rows back, and then when it sees the primary key in the row, *then* it can look in the local identity map and see that the object is already there.  It's only when you say `query.get({some primary key})` that the `Session` doesn't have to issue a query.
-  
-Also, the `Session` is not designed to be a global object from which everyone consults as a "registry" of objects.  That is the job of a **second level cache**.  A good library for implementing second level caching is [Memcached](http://www.danga.com/memcached/).  It *is* possible to "sort of" use the `Session` in this manner, if you set it to be non-transactional and it never flushes any SQL, but it's not a terrific solution,  since if concurrent threads load the same objects at the same time, you may have multiple copies of the same objects present in collections.
-  
+
+    Yeee...no.  It's somewhat used as a cache, in that it implements the identity map pattern, and stores objects keyed to their primary key.  However, it doesn't do any kind of query caching.  This means, if you say `session.query(Foo).filter_by(name='bar')`, even if `Foo(name='bar')` is right there, in the identity map, the session has no idea about that.  It has to issue SQL to the database, get the rows back, and then when it sees the primary key in the row, *then* it can look in the local identity map and see that the object is already there.  It's only when you say `query.get({some primary key})` that the `Session` doesn't have to issue a query.
+
+    Also, the `Session` is not designed to be a global object from which everyone consults as a "registry" of objects.  That is the job of a **second level cache**.  A good library for implementing second level caching is [Memcached](http://www.danga.com/memcached/).  It *is* possible to "sort of" use the `Session` in this manner, if you set it to be non-transactional and it never flushes any SQL, but it's not a terrific solution,  since if concurrent threads load the same objects at the same time, you may have multiple copies of the same objects present in collections.
+
 * How can I get the `Session` for a certain object ?
- 
-    {python}
-    from sqlalchemy.orm import object_session
-    session = object_session(someobject)
-  
+
+        {python}
+        from sqlalchemy.orm import object_session
+        session = object_session(someobject)
+
 * Is the session threadsafe ?
 
-Nope.  It has no thread synchronization of any kind built in, and particularly when you do a flush operation, it definitely is not open to concurrent threads accessing it, because it holds onto a single database connection at that point.  If you use a session which is non-transactional for read operations only, it's still not thread-"safe", but you also wont get any catastrophic failures either, since it opens and closes connections on an as-needed basis; its just that different threads might load the same objects independently of each other, but only one will wind up in the identity map (however, the other one might still live in a collection somewhere).
-  
-But the bigger point here is, you should not *want* to use the session with multiple concurrent threads.  That would be like having everyone at a restaurant all eat from the same plate.  The session is a local "workspace" that you use for a specific set of tasks; you don't want to, or need to, share that session with other threads who are doing some other task.  If, on the other hand, there are other threads  participating in the same task you are, such as in a desktop graphical application, then you would be sharing the session with those threads, but you also will have implemented a proper locking scheme (or your graphical framework does) so that those threads do not collide.
+    Nope.  It has no thread synchronization of any kind built in, and particularly when you do a flush operation, it definitely is not open to concurrent threads accessing it, because it holds onto a single database connection at that point.  If you use a session which is non-transactional for read operations only, it's still not thread-"safe", but you also wont get any catastrophic failures either, since it opens and closes connections on an as-needed basis; its just that different threads might load the same objects independently of each other, but only one will wind up in the identity map (however, the other one might still live in a collection somewhere).
+
+    But the bigger point here is, you should not *want* to use the session with multiple concurrent threads.  That would be like having everyone at a restaurant all eat from the same plate.  The session is a local "workspace" that you use for a specific set of tasks; you don't want to, or need to, share that session with other threads who are doing some other task.  If, on the other hand, there are other threads  participating in the same task you are, such as in a desktop graphical application, then you would be sharing the session with those threads, but you also will have implemented a proper locking scheme (or your graphical framework does) so that those threads do not collide.
   
 ### Session Attributes {@name=attributes} 
 
-The session provides a set of attributes and collection-oriented methods which allow you to view (but not to change) the current state of the session.
+The session provides a set of attributes and collection-oriented methods which allow you to view the current state of the session.
 
 The **identity map** is accessed by the `identity_map` attribute, which provides a dictionary interface.  The keys are "identity keys", which are attached to all persistent objects by the attribute `_instance_key`:
 
 
 The identity map is a `dict` by default.  This means that objects stay in the session until they are explicitly removed, or the session is cleared.  Some people prefer objects to be automatically garbage collected instead.  For this,  you can specify the flag `weak_identity_map=True` to the `create_session` or `sessionmaker` functions so that the `Session` will use a `weakref.WeakValueDictionary`.  But note that an object that has changes marked on it (i.e. "dirty"), and is then discarded, will not have its changes saved to the database.  It's because of this misleading behavior that SQLAlchemy's identity map is by default strong-referencing.
 
+While the `identity_map` accessor is currently the actual dictionary used by the `Session` to store instances, you should not add or remove items from this dictionary.  Use the session methods `save_or_update()` and `expunge()` to add or remove items.
+
 The Session also supports an iterator interface in order to see all objects in the identity map:
 
     {python}
 
 Note that if a session is created with the `weak_identity_map` flag, an item which is marked as "dirty" will be silently removed from the session if the item falls out of scope in the user application.  This is because the unit of work does not look for "dirty" changes except for within a flush operation (or any time the session.dirty collection is accessed). 
 
-As for objects inside of `new` and `deleted`, if you abandon all references to new or modified objects within a session, *they are still present* in either of those two lists, and will be saved on the next flush operation, unless they are removed from the Session explicitly or the session is cleared.
+However, the `new` and `deleted` collections are strong-referencing sets.  Even if you're using `weak_identity_map`, if you abandon all references to new or deleted objects within a session, *they are still present* in either of those two lists, and will be saved on the next flush operation, unless they are removed from the Session explicitly or the session is cleared.
 
 ### Querying
 

lib/sqlalchemy/orm/__init__.py

 
 def scoped_session(session_factory, scopefunc=None):
   """Provides thread-local management of Sessions.
+  
+  This is a front-end function to the [sqlalchemy.orm.scoping#ScopedSession]
+  class.
 
   Usage::
 

lib/sqlalchemy/orm/session.py

 __all__ = ['Session', 'SessionTransaction']
 
 def sessionmaker(bind=None, class_=None, autoflush=True, transactional=True, **kwargs):
-    """Generate a Session configuration."""
+    """Generate a custom-configured [sqlalchemy.orm.session#Session] class.
+    
+    The returned object is a subclass of ``Session``, which, when instantiated with no
+    arguments, uses the
+    keyword arguments configured here as its constructor arguments.  It is intended
+    that the `sessionmaker()` function be called within the global scope of an application,
+    and the returned class be made available to the rest of the application as the 
+    single class used to instantiate sessions.
+    
+    e.g.::
+    
+        # global scope
+        Session = sessionmaker(autoflush=False)
+        
+        # later, in a local scope, create and use a session:
+        sess = Session()
+    
+    Any keyword arguments sent to the constructor itself will override the "configured"
+    keywords::
+    
+        Session = sessionmaker()
+        
+        # bind an individual session to a connection
+        sess = Session(bind=connection)
+        
+    The class also includes a special classmethod ``configure()``, which allows 
+    additional configurational options to take place after the custom ``Session``
+    class has been generated.  This is useful particularly for defining the 
+    specific ``Engine`` (or engines) to which new instances of ``Session``
+    should be bound::
+    
+        Session = sessionmaker()
+        Session.configure(bind=create_engine('sqlite:///foo.db'))
+        
+        sess = Session()
+    
+    The function features a single keyword argument of its own, `class_`, which
+    may be used to specify an alternate class other than ``sqlalchemy.orm.session.Session``
+    which should be used by the returned class.  All other keyword arguments sent to 
+    `sessionmaker()` are passed through to the instantiated `Session()` object.
+    """
     
     kwargs['bind'] = bind
     kwargs['autoflush'] = autoflush
 class SessionTransaction(object):
     """Represents a Session-level Transaction.
 
-    This corresponds to one or more sqlalchemy.engine.Transaction
-    instances behind the scenes, with one Transaction per Engine in
+    This corresponds to one or more [sqlalchemy.engine_Transaction]
+    instances behind the scenes, with one ``Transaction`` per ``Engine`` in
     use.
 
-    The SessionTransaction object is **not** threadsafe.
+    Typically, usage of ``SessionTransaction`` is not necessary; use
+    the ``begin()`` and ``commit()`` methods on ``Session`` itself.
+    
+    The ``SessionTransaction`` object is **not** threadsafe.
     """
 
     def __init__(self, session, parent=None, autoflush=True, nested=False):
     """
 
     def __init__(self, bind=None, autoflush=True, transactional=False, twophase=False, echo_uow=False, weak_identity_map=False, binds=None):
+        """Construct a new Session.
+
+            autoflush
+                when ``True``, all query operations will issue a ``flush()`` call to this
+                ``Session`` before proceeding. This is a convenience feature so that
+                ``flush()`` need not be called repeatedly in order for database queries to
+                retrieve results. It's typical that ``autoflush`` is used in conjunction with
+                ``transactional=True``, so that ``flush()`` is never called; you just call
+                ``commit()`` when changes are complete to finalize all changes to the
+                database.
+        
+            bind
+                an optional ``Engine`` or ``Connection`` to which this ``Session`` should be
+                bound. When specified, all SQL operations performed by this session will
+                execute via this connectable.
+                
+            binds
+                an optional dictionary, which contains more granular "bind" information than
+                the ``bind`` parameter provides. This dictionary can map individual ``Table``
+                instances as well as ``Mapper`` instances to individual ``Engine`` or
+                ``Connection`` objects. Operations which proceed relative to a particular
+                ``Mapper`` will consult this dictionary for the direct ``Mapper`` instance as
+                well as the mapper's ``mapped_table`` attribute in order to locate an
+                connectable to use. The full resolution is described in the ``get_bind()``
+                method of ``Session``. Usage looks like::
+                
+                    sess = Session(binds={
+                        SomeMappedClass : create_engine('postgres://engine1'),
+                        somemapper : create_engine('postgres://engine2'),
+                        some_table : create_engine('postgres://engine3'),
+                    })
+                    
+                Also see the ``bind_mapper()`` and ``bind_table()`` methods.
+            
+            echo_uow
+                When ``True``, configure Python logging to dump all unit-of-work
+                transactions. This is the equivalent of
+                ``logging.getLogger('sqlalchemy.orm.unitofwork').setLevel(logging.DEBUG)``.
+                
+            transactional
+                Set up this ``Session`` to automatically begin transactions. Setting this
+                flag to ``True`` is the rough equivalent of calling ``begin()`` after each
+                ``commit()`` operation, after each ``rollback()``, and after each
+                ``close()``. Basically, this has the effect that all session operations are
+                performed within the context of a transaction. Note that the ``begin()``
+                operation does not immediately utilize any connection resources; only when
+                connection resources are first required do they get allocated into a
+                transactional context.
+
+            twophase
+                when ``True``, all transactions will be started using
+                [sqlalchemy.engine_TwoPhaseTransaction]. During a ``commit()``, after
+                ``flush()`` has been issued for all attached databaes, the ``prepare()``
+                method on each database's ``TwoPhaseTransaction`` will be called. This allows
+                each database to roll back the entire transaction, before each transaction is
+                committed.
+
+            weak_identity_map
+                when ``True``, use a ``WeakValueDictionary`` instead of a regular ``dict``
+                for this ``Session`` object's identity map. This will allow objects which
+                fall out of scope to be automatically removed from the ``Session``. However,
+                objects who have been marked as "dirty" will also be garbage collected, and
+                those changes will not be persisted.
+            
+        """
         self.uow = unitofwork.UnitOfWork(weak_identity_map=weak_identity_map)
 
         self.bind = bind
     create_transaction = begin
 
     def begin_nested(self):
+        """begin a 'nested' transaction on this Session.
+        
+        this utilizes a SAVEPOINT transaction for databases 
+        which support this feature.
+        """
         return self.begin(nested=True)
     
     def rollback(self):
+        """rollback the current transaction in progress.
+        
+        If no transaction is in progress, this method is a 
+        pass-thru.
+        """
+        
         if self.transaction is None:
             pass
         else:
             self.begin()
             
     def commit(self):
+        """commit the current transaction in progress.
+        
+        If no transaction is in progress, this method raises
+        an InvalidRequestError.  
+        
+        If the ``begin()`` method was called on this ``Session``
+        additional times subsequent to its first call, 
+        ``commit()`` will not actually commit, and instead
+        pops an internal SessionTransaction off its internal stack
+        of transactions.  Only when the "root" SessionTransaction
+        is reached does an actual database-level commit occur.
+        
+        """
         if self.transaction is None:
             if self.transactional:
                 self.begin()
         progress.
         
         the "mapper" argument is a class or mapper to which a bound engine
-        will be located; use this when the Session itself is unbound.
+        will be located; use this when the Session itself is either bound
+        to multiple engines or connections, or is not bound to any connectable.
         """
 
         if self.transaction is not None:
 
         If this method allocates a new ``Connection`` for the operation,
         then the ``ResultProxy`` 's ``close()`` method will release the
-        resources of the underlying ``Connection``, otherwise its a no-op.
+        resources of the underlying ``Connection``.
         """
         return self.connection(mapper, close_with_result=True).execute(clause, params or {}, **kwargs)
 
         """Close this Session.  
         
         This clears all items and ends any transaction in progress.
+        
+        If this session were created with ``transactional=True``, a
+        new transaction is immediately begun.  Note that this new
+        transaction does not use any connection resources until they 
+        are first needed.
         """
 
         self.clear()