]> git.ipfire.org Git - thirdparty/sqlalchemy/sqlalchemy.git/commitdiff
some doc updates
authorMike Bayer <mike_mp@zzzcomputing.com>
Tue, 24 Aug 2010 05:01:04 +0000 (01:01 -0400)
committerMike Bayer <mike_mp@zzzcomputing.com>
Tue, 24 Aug 2010 05:01:04 +0000 (01:01 -0400)
doc/build/examples.rst
doc/build/session.rst
lib/sqlalchemy/orm/__init__.py

index b62b808fa4b9e6a68c72c9430f860e8d068dcb4a..821c96952e3b97b60d1552a373a87731c9ba827e 100644 (file)
@@ -30,6 +30,8 @@ Location: /examples/custom_attributes/
 
 .. automodule:: custom_attributes
 
+.. _examples_caching:
+
 Beaker Caching
 --------------
 
index dd8c75b37834d3c1ba486e4335f8e41cdcaa137a..65f73e662aa98b558436596cca162b39ac36eee3 100644 (file)
@@ -4,47 +4,59 @@
 Using the Session
 =================
 
-The `Mapper` is the entrypoint to the configurational API of the SQLAlchemy
-object relational mapper. But the primary object one works with when using the
-ORM is the :class:`~sqlalchemy.orm.session.Session`.
+The :func:`.orm.mapper` function and :mod:`~sqlalchemy.ext.declarative` extensions
+are the primary configurational interface for the ORM. Once mappings are
+configured, the primary usage interface for persistence operations is the
+:class:`.Session`.
 
 What does the Session do ?
 ==========================
 
-In the most general sense, the :class:`~sqlalchemy.orm.session.Session`
-establishes all conversations with the database and represents a "holding
-zone" for all the mapped instances which you've loaded or created during its
-lifespan. It implements the `Unit of Work
-<http://martinfowler.com/eaaCatalog/unitOfWork.html>`_ pattern, which means it
-keeps track of all changes which occur, and is capable of **flushing** those
-changes to the database as appropriate. Another important facet of the
-:class:`~sqlalchemy.orm.session.Session` is that it's also maintaining
-**unique** copies of each instance, where "unique" means "only one object with
-a particular primary key" - this pattern is called the `Identity Map
-<http://martinfowler.com/eaaCatalog/identityMap.html>`_.
-
-Beyond that, the :class:`~sqlalchemy.orm.session.Session` implements an
-interface which lets you move objects in or out of the session in a variety of
-ways, it provides the entryway to a :class:`~sqlalchemy.orm.query.Query`
-object which is used to query the database for data, and it also provides a
-transactional context for SQL operations which rides on top of the
-transactional capabilities of :class:`~sqlalchemy.engine.base.Engine` and
-:class:`~sqlalchemy.engine.base.Connection` objects.
+In the most general sense, the :class:`~.Session` establishes all
+conversations with the database and represents a "holding zone" for all the
+objects which you've loaded or associated with it during its lifespan. It
+provides the entrypoint to acquire a :class:`.Query` object, which sends
+queries to the database using the :class:`~.Session` object's current database
+connection, populating result rows into objects that are then stored in the
+:class:`.Session`, inside a structure called the `Identity Map
+<http://martinfowler.com/eaaCatalog/identityMap.html>`_ - a data structure
+that maintains unique copies of each object, where "unique" means "only one
+object with a particular primary key".
+
+The :class:`.Session` begins in an essentially stateless form. Once queries
+are issued or other objects are persisted with it, it requests a connection
+resource from an :class:`.Engine` that is associated either with the
+:class:`.Session` itself or with the mapped :class:`.Table` objects being
+operated upon. This connection represents an ongoing transaction, which
+remains in effect until the :class:`.Session` is instructed to commit or roll
+back its pending state.
+
+All changes to objects maintained by a :class:`.Session` are tracked - before
+the database is queried again or before the current transaction is committed,
+it **flushes** all pending changes to the database. This is known as the `Unit
+of Work <http://martinfowler.com/eaaCatalog/unitOfWork.html>`_ pattern.
+
+When using a :class:`.Session`, it's important to note that the objects
+which are associated with it are **proxy objects** to the transaction being
+held by the :class:`.Session` - there are a variety of events that will cause
+objects to re-access the database in order to keep synchronized.   It is 
+possible to "detach" objects from a :class:`.Session`, and to continue using 
+them, though this practice has its caveats.  It's intended that
+usually, you'd re-associate detached objects another :class:`.Session` when you 
+want to work with them again, so that they can resume their normal task of 
+representing database state.
 
 Getting a Session
 =================
 
-:class:`~sqlalchemy.orm.session.Session` is a regular Python class which can
+:class:`.Session` is a regular Python class which can
 be directly instantiated. However, to standardize how sessions are configured
-and acquired, the :func:`~sqlalchemy.orm.sessionmaker` function is normally
-used to create a top level :class:`~sqlalchemy.orm.session.Session`
+and acquired, the :func:`.sessionmaker` function is normally
+used to create a top level :class:`.Session`
 configuration which can then be used throughout an application without the
 need to repeat the configurational arguments.
 
-Using a sessionmaker() Configuration
-------------------------------------
-
-The usage of :func:`~sqlalchemy.orm.sessionmaker` is illustrated below:
+The usage of :func:`.sessionmaker` is illustrated below:
 
 .. sourcecode:: python+sql
 
@@ -61,28 +73,26 @@ The usage of :func:`~sqlalchemy.orm.sessionmaker` is illustrated below:
     session.add(myobject)
     session.commit()
 
-    # close when finished
-    session.close()
-
-Above, the :func:`~sqlalchemy.orm.sessionmaker` call creates a class for us,
+Above, the :func:`.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 a
-particular bound engine.
+actual :class:`.Session` class, which when instantiated, will
+use the arguments we've given the function, in this case
+to use a particular :class:`.Engine` for connection resources.
 
 When you write your application, place the call to
-:func:`~sqlalchemy.orm.sessionmaker` somewhere global, and then make your new
+:func:`.sessionmaker` somewhere global, and then make your new
 ``Session`` class available to the rest of your application.
 
-Binding Session to an Engine
-----------------------------
-
-In our previous example regarding :func:`~sqlalchemy.orm.sessionmaker`, we
-specified a ``bind`` for a particular :class:`~sqlalchemy.engine.base.Engine`.
-If we'd like to construct a :func:`~sqlalchemy.orm.sessionmaker` without an
-engine available and bind it later on, or to specify other options to an
-existing :func:`~sqlalchemy.orm.sessionmaker`, we may use the ``configure()``
-method::
-
+A typical setup will associate the :func:`.sessionmaker` with an :class:`.Engine`,
+so that each :class:`.Session` generated will use this :class:`.Engine`
+to acquire connection resources.   This association can
+be set up as in the example above, using the ``bind`` argument.   You 
+can also associate a :class:`.Engine` with an existing :func:`.sessionmaker` 
+using the :meth:`.sessionmaker.configure` method::
+    
+    from sqlalchemy.orm import sessionmaker
+    from sqlalchemy import create_engine
+    
     # configure Session class with desired options
     Session = sessionmaker()
 
@@ -95,76 +105,54 @@ method::
     # work with the session
     session = Session()
 
-It's actually entirely optional to bind a Session to an engine. If the
-underlying mapped :class:`~sqlalchemy.schema.Table` objects use "bound"
-metadata, the :class:`~sqlalchemy.orm.session.Session` will make use of the
-bound engine instead (or will even use multiple engines if multiple binds are
-present within the mapped tables). "Bound" metadata is described at
-:ref:`metadata_binding`.
-
-The :class:`~sqlalchemy.orm.session.Session` also has the ability to be bound
-to multiple engines explicitly. Descriptions of these scenarios are described
-in :ref:`session_partitioning`.
-
-Binding Session to a Connection
--------------------------------
+you can also associate individual :class:`.Session` objects with an :class:`.Engine`
+on each invocation::
 
-The :class:`~sqlalchemy.orm.session.Session` can also be explicitly bound to
-an individual database :class:`~sqlalchemy.engine.base.Connection`. Reasons
-for doing this may include to join a :class:`~sqlalchemy.orm.session.Session`
-with an ongoing transaction local to a specific
-:class:`~sqlalchemy.engine.base.Connection` object, or to bypass connection
-pooling by just having connections persistently checked out and associated
-with distinct, long running sessions::
+    session = Session(bind=engine)
+    
+...or directly with a :class:`.Connection`.  This is useful in some situations, 
+such as within a test fixture that maintains an external transaction::
 
+    from sqlalchemy.orm import sessionmaker
+    from sqlalchemy import create_engine
+    from unittest import TestCase
+    
     # global application scope.  create Session class, engine
     Session = sessionmaker()
 
     engine = create_engine('postgresql://...')
 
-    ...
+    class SomeTest(TestCase):
+        def setUp(self):
+            # connect to the database
+            self.connection = engine.connect()
 
-    # local scope, such as within a controller function
-
-    # connect to the database
-    connection = engine.connect()
-
-    # bind an individual Session to the connection
-    session = Session(bind=connection)
-
-Using create_session()
-----------------------
-
-As an alternative to :func:`~sqlalchemy.orm.sessionmaker`,
-:func:`~sqlalchemy.orm.create_session` is a function which calls the normal
-:class:`~sqlalchemy.orm.session.Session` constructor directly. All arguments
-are passed through and the new :class:`~sqlalchemy.orm.session.Session` object
-is returned::
-
-    session = create_session(bind=myengine, autocommit=True, autoflush=False)
-
-Note that :func:`~sqlalchemy.orm.create_session` disables all optional
-"automation" by default. Called with no arguments, the session produced is not
-autoflushing, does not auto-expire, and does not maintain a transaction (i.e.
-it begins and commits a new transaction for each
-:func:`~sqlalchemy.orm.session.Session.flush`). SQLAlchemy uses
-:func:`~sqlalchemy.orm.create_session` extensively within its own unit tests.
+            # begin a non-ORM transaction
+            self.trans = connection.begin()
+    
+            # bind an individual Session to the connection
+            self.session = Session(bind=self.connection)
+    
+        def test_something(self):
+            # use the session in tests.   
+            
+            self.session.add(Foo())
+            self.session.commit()
+    
+        def tearDown(self):
+            # rollback - everything that happened with the
+            # Session above (including calls to commit()) 
+            # is rolled back.
+            self.trans.rollback()
+            self.session.close()
+            
 
 Configurational Arguments
 -------------------------
 
-Configurational arguments accepted by :func:`~sqlalchemy.orm.sessionmaker` and
-:func:`~sqlalchemy.orm.create_session` are the same as that of the
-:class:`~sqlalchemy.orm.session.Session` class itself, and are described at
-:func:`sqlalchemy.orm.sessionmaker`.
-
-Note that the defaults of :func:`~sqlalchemy.orm.create_session` are the
-opposite of that of :func:`~sqlalchemy.orm.sessionmaker`: autoflush and
-expire_on_commit are False, autocommit is True. It is recommended to use the
-:func:`~sqlalchemy.orm.sessionmaker` function instead of
-:func:`~sqlalchemy.orm.create_session`. :func:`~sqlalchemy.orm.create_session`
-is used to get a session with no automation turned on and is useful for
-testing.
+Configurational arguments accepted by :func:`.sessionmaker` are the same as that of the
+:class:`.Session` class itself, and are described at
+:func:`.sessionmaker`.
 
 Using the Session
 ==================
@@ -203,52 +191,68 @@ at the same time).
 Frequently Asked Questions
 --------------------------
 
-* When do I make a :func:`~sqlalchemy.orm.sessionmaker` ?
+* When do I make a :func:`.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 :func:`~sqlalchemy.orm.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
-    :class:`~sqlalchemy.orm.session.Session()`, and the configuration of that
-    session is controlled by that central point.
+    place the :func:`.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 :class:`.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
-    :class:`~sqlalchemy.orm.session.Session` at the "class" level to the
+    :class:`.Session` at the "class" level to the
     engine later on, using ``configure()``.
     
     In the examples in this section, we will frequently show the
-    :func:`~sqlalchemy.orm.sessionmaker` being created right above the line
-    where we actually invoke :class:`~sqlalchemy.orm.session.Session()`. But
-    that's just for example's sake ! In reality, the
-    :func:`~sqlalchemy.orm.sessionmaker` would be somewhere at the module
-    level, and your individual :class:`~sqlalchemy.orm.session.Session()`
-    calls would be sprinkled all throughout your app, such as in a web
-    application within each controller method.
-
-* When do I make a :class:`~sqlalchemy.orm.session.Session` ?
-
-    You typically invoke :class:`~sqlalchemy.orm.session.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
-    :func:`~sqlalchemy.orm.session.Session.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 !
+    :func:`.sessionmaker` being created right above the line where we actually
+    invoke :class:`~sqlalchemy.orm.session.Session()`. But that's just for
+    example's sake ! In reality, the :func:`.sessionmaker` would be somewhere
+    at the module level, and your individual
+    :class:`~sqlalchemy.orm.session.Session()` calls would be sprinkled all
+    throughout your app, such as in a web application within each controller
+    method.
+
+* When do I make a :class:`.Session` ?
+
+    You typically invoke :class:`.Session` when you first need to talk to your
+    database, and want to save some objects or load some existing ones. It
+    then remains in use for the lifespan of a particular database
+    conversation, which includes not just the initial loading of objects but
+    throughout the whole usage of those instances.
+    
+    Objects become detached if their owning session is discarded. They are
+    still functional in the detached state if the user has ensured that their
+    state has not been expired before detachment, but they will not be able to
+    represent the current state of database data. Because of this, it's best
+    to consider persisted objects as an extension of the state of a particular
+    :class:`.Session`, and to keep that session around until all referenced
+    objects have been discarded.
     
-    There is also a pattern whereby you're using a **contextual session**,
-    this is described later in :ref:`unitofwork_contextual`. In this pattern,
-    a helper object is maintaining a :class:`~sqlalchemy.orm.session.Session`
-    for you, most commonly one that is local to the current thread (and
-    sometimes also local to an application instance). SQLAlchemy 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 :class:`~sqlalchemy.orm.session.Session()` whenever you want a
-    session.
+    An exception to this is when objects are placed in caches or otherwise
+    shared among threads or processes, in which case their detached state can
+    be stored, transmitted, or shared. However, the state of detached objects
+    should still be transferred back into a new :class:`.Session` using
+    :meth:`.Session.add` or :meth:`.Session.merge` before working with the
+    object (or in the case of merge, its state) again.
+    
+    It is also very common that a :class:`.Session` as well as its associated
+    objects are only referenced by a single thread.  Sharing objects between
+    threads is most safely accomplished by sharing their state among multiple
+    instances of those objects, each associated with a distinct
+    :class:`.Session` per thread, :meth:`.Session.merge` to transfer state
+    between threads.   This pattern is not a strict requirement by any means, 
+    but it has the least chance of introducing concurrency issues.
+    
+    To help with the recommended :class:`.Session` -per-thread,
+    :class:`.Session` -per-set-of-objects patterns, the
+    :func:`.scoped_session` function is provided which produces a
+    thread-managed registry of :class:`.Session` objects. It is commonly used
+    in web applications so that a single global variable can be used to safely
+    represent transactional sessions with sets of objects, localized to a
+    single thread. More on this object is in :ref:`unitofwork_contextual`.
 
 * Is the Session a cache ?
 
@@ -264,19 +268,13 @@ Frequently Asked Questions
     :class:`~sqlalchemy.orm.session.Session` doesn't have to issue a query.
     
     Additionally, the Session stores object instances using a weak reference
-    by default. This also defeats the purpose of using the Session as a cache,
-    unless the ``weak_identity_map`` flag is set to ``False``.
+    by default. This also defeats the purpose of using the Session as a cache.
     
-    The :class:`~sqlalchemy.orm.session.Session` is not designed to be a
+    The :class:`.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
-    :class:`~sqlalchemy.orm.session.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.
+    That's more the job of a **second level cache**.   SQLAlchemy provides
+    a pattern for implementing second level caching using `Beaker <http://beaker.groovie.org/>`_, 
+    via the :ref:`examples_caching` example.
 
 * How can I get the :class:`~sqlalchemy.orm.session.Session` for a certain object ?
 
@@ -312,6 +310,10 @@ Frequently Asked Questions
     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.
+    
+    A multithreaded application is usually going to want to make usage of
+    :func:`.scoped_session` to transparently manage sessions per thread.
+    More on this at :ref:`unitofwork_contextual`.
 
 Querying
 --------
@@ -497,7 +499,7 @@ Regardless of the autoflush setting, a flush can always be forced by issuing
     session.flush()
 
 The "flush-on-Query" aspect of the behavior can be disabled by constructing
-:func:`~sqlalchemy.orm.sessionmaker` with the flag ``autoflush=False``::
+:func:`.sessionmaker` with the flag ``autoflush=False``::
 
     Session = sessionmaker(autoflush=False)
 
@@ -540,7 +542,7 @@ complete. This is so that when the instances are next accessed, either through
 attribute access or by them being present in a
 :class:`~sqlalchemy.orm.query.Query` result set, they receive the most recent
 state. To disable this behavior, configure
-:func:`~sqlalchemy.orm.sessionmaker` with ``expire_on_commit=False``.
+:func:`.sessionmaker` with ``expire_on_commit=False``.
 
 Normally, instances loaded into the :class:`~sqlalchemy.orm.session.Session`
 are never changed by subsequent queries; the assumption is that the current
@@ -718,7 +720,7 @@ as deleted, or persistent objects which have pending changes on them. After a
 full flush, these collections are all empty, and all objects are again weakly
 referenced. To disable the weak referencing behavior and force all objects
 within the session to remain until explicitly expunged, configure
-:func:`~sqlalchemy.orm.sessionmaker` with the ``weak_identity_map=False``
+:func:`.sessionmaker` with the ``weak_identity_map=False``
 setting.
 
 .. _unitofwork_cascades:
@@ -1047,7 +1049,7 @@ some kind of "global" session object, or at least "global" to all the parts of
 an application which are tasked with servicing the current request. For this
 pattern, SQLAlchemy provides the ability to enhance the
 :class:`~sqlalchemy.orm.session.Session` class generated by
-:func:`~sqlalchemy.orm.sessionmaker` to provide auto-contextualizing support.
+:func:`.sessionmaker` to provide auto-contextualizing support.
 This means that whenever you create a :class:`~sqlalchemy.orm.session.Session`
 instance with its constructor, you get an *existing*
 :class:`~sqlalchemy.orm.session.Session` object which is bound to some
@@ -1059,9 +1061,9 @@ Creating a Thread-local Context
 -------------------------------
 
 The :func:`~sqlalchemy.orm.scoped_session` function wraps around the
-:func:`~sqlalchemy.orm.sessionmaker` function, and produces an object which
+:func:`.sessionmaker` function, and produces an object which
 behaves the same as the :class:`~sqlalchemy.orm.session.Session` subclass
-returned by :func:`~sqlalchemy.orm.sessionmaker`::
+returned by :func:`.sessionmaker`::
 
     from sqlalchemy.orm import scoped_session, sessionmaker
     Session = scoped_session(sessionmaker())
@@ -1069,7 +1071,7 @@ returned by :func:`~sqlalchemy.orm.sessionmaker`::
 However, when you instantiate this :class:`~sqlalchemy.orm.session.Session`
 "class", in reality the object is pulled from a threadlocal variable, or if it
 doesn't exist yet, it's created using the underlying class generated by
-:func:`~sqlalchemy.orm.sessionmaker`::
+:func:`.sessionmaker`::
 
     >>> # call Session() the first time.  the new Session instance is created.
     >>> session = Session()
index c74fabacd7de74b6f667e8f30aa966316eb20f8d..50ea4c28388818680a68e3a852a9aee91376cc92 100644 (file)
@@ -115,8 +115,7 @@ def scoped_session(session_factory, scopefunc=None):
     :class:`~sqlalchemy.orm.scoping.ScopedSession`.
 
     :param session_factory: a callable function that produces
-      :class:`Session` instances, such as :func:`sessionmaker` or
-      :func:`create_session`.
+      :class:`Session` instances, such as :func:`sessionmaker`.
 
     :param scopefunc: optional, TODO
 
@@ -141,7 +140,12 @@ def scoped_session(session_factory, scopefunc=None):
     return ScopedSession(session_factory, scopefunc=scopefunc)
 
 def create_session(bind=None, **kwargs):
-    """Create a new :class:`~sqlalchemy.orm.session.Session`.
+    """Create a new :class:`.Session` 
+    with no automation enabled by default.
+    
+    This function is used primarily for testing.   The usual
+    route to :class:`.Session` creation is via its constructor
+    or the :func:`.sessionmaker` function.
 
     :param bind: optional, a single Connectable to use for all
       database access in the created