diff options
| author | Mike Bayer <mike_mp@zzzcomputing.com> | 2012-09-10 21:14:09 -0400 |
|---|---|---|
| committer | Mike Bayer <mike_mp@zzzcomputing.com> | 2012-09-10 21:14:09 -0400 |
| commit | 9a4a565212fcc9db33b925addecfcc8560ba5445 (patch) | |
| tree | 7e392ba861661540d788b7cb2160116e168c49cd | |
| parent | 43b8346b3c14a5742080ff66ce03bb05e5891c50 (diff) | |
| download | sqlalchemy-9a4a565212fcc9db33b925addecfcc8560ba5445.tar.gz | |
updates
| -rw-r--r-- | doc/build/orm/session.rst | 20 | ||||
| -rw-r--r-- | lib/sqlalchemy/orm/scoping.py | 33 | ||||
| -rw-r--r-- | lib/sqlalchemy/orm/session.py | 11 |
3 files changed, 47 insertions, 17 deletions
diff --git a/doc/build/orm/session.rst b/doc/build/orm/session.rst index 039744221..c0ebe2362 100644 --- a/doc/build/orm/session.rst +++ b/doc/build/orm/session.rst @@ -48,6 +48,8 @@ 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. +.. _session_getting: + Getting a Session ================= @@ -222,7 +224,7 @@ Session Frequently Asked Questions example's sake! In reality, the :class:`.sessionmaker` would be somewhere at the module level. The calls to instantiate :class:`.Session` would then be placed at the point in the application where database - conversations begin (see :ref:`session_faq_whentocreate`). + conversations begin. * When do I construct a :class:`.Session`, when do I commit it, and when do I close it ? @@ -237,8 +239,8 @@ Session Frequently Asked Questions begin a new transaction if it is used again, subsequent to the previous transaction ending; from this it follows that the :class:`.Session` is capable of having a lifespan across many transactions, though only - one at a time. We refer to these two concepts as *transaction scope* - and *session scope*. + one at a time. We refer to these two concepts as **transaction scope** + and **session scope**. The implication here is that the SQLAlchemy ORM is encouraging the developer to establish these two scopes in his or her application, @@ -282,7 +284,7 @@ Session Frequently Asked Questions pattern which establishes one as soon as it is needed. The request then proceeds, with some system in place where application logic can access the current :class:`.Session` in a manner associated with how the actual - requset object is accessed. As the request ends, the :class:`.Session` + request object is accessed. As the request ends, the :class:`.Session` is torn down as well, usually through the usage of event hooks provided by the web framework. The transaction used by the :class:`.Session` may also be committed at this point, or alternatively the application may @@ -303,8 +305,8 @@ Session Frequently Asked Questions In those situations where integration libraries are not available, SQLAlchemy includes its own "helper" class known as - :class:`.scoped_session`. This object actually is used as the basis - of some integration packages, and provides both a quick way + :class:`.scoped_session`. A tutorial on the usage of this object + is at :ref:`unitofwork_contextual`. It provides both a quick way to associate a :class:`.Session` with the current thread, as well as patterns to associate :class:`.Session` objects with other kinds of scopes. @@ -316,7 +318,7 @@ Session Frequently Asked Questions a series of operations for some period of time, which can be committed at the end. Some examples: - * A background daemon which spawns off child forks for example + * A background daemon which spawns off child forks would want to create a :class:`.Session` local to each child process work with that :class:`.Session` through the life of the "job" that the fork is handling, then tear it down when the job is completed. @@ -1727,7 +1729,7 @@ This simple correspondence of web request and thread means that to associate a :class:`.Session` with a thread implies it is also associated with the web request running within that thread, and vice versa, provided that the :class:`.Session` is created only after the web request begins and torn down just before the web request ends. -So it is a very common practice to use :class:`.scoped_session` as a quick way +So it is a common practice to use :class:`.scoped_session` as a quick way to integrate the :class:`.Session` with a web application. The sequence diagram below illustrates this flow:: @@ -1792,7 +1794,7 @@ one of many options on how to "scope" a :class:`.Session`. A custom scope can based on any existing system of getting at "the current thing we are working with". Suppose a web framework defines a library function ``get_current_request()``. An application -build on this framework can call this function at any time, and the result will be +built using this framework can call this function at any time, and the result will be some kind of ``Request`` object that represents the current request being processed. If the ``Request`` object is hashable, then this function can be easily integrated with :class:`.scoped_session` to associate the :class:`.Session` with the request. Below we illustrate diff --git a/lib/sqlalchemy/orm/scoping.py b/lib/sqlalchemy/orm/scoping.py index fff17ee14..e827bc5b8 100644 --- a/lib/sqlalchemy/orm/scoping.py +++ b/lib/sqlalchemy/orm/scoping.py @@ -42,26 +42,45 @@ class scoped_session(object): else: self.registry = ThreadLocalRegistry(session_factory) - def __call__(self, **kwargs): - """Return the current :class:`.Session`.""" - if kwargs: - scope = kwargs.pop('scope', False) + def __call__(self, **kw): + """Return the current :class:`.Session`, creating it + using the session factory if not present. + + :param \**kw: Keyword arguments will be passed to the + session factory callable, if an existing :class:`.Session` + is not present. If the :class:`.Session` is present and + keyword arguments have been passed, :class:`.InvalidRequestError` + is raised. + + """ + if kw: + scope = kw.pop('scope', False) if scope is not None: if self.registry.has(): raise sa_exc.InvalidRequestError( "Scoped session is already present; " "no new arguments may be specified.") else: - sess = self.session_factory(**kwargs) + sess = self.session_factory(**kw) self.registry.set(sess) return sess else: - return self.session_factory(**kwargs) + return self.session_factory(**kw) else: return self.registry() def remove(self): - """Dispose of the current contextual session.""" + """Dispose of the current :class:`.Session`, if present. + + This will first call :meth:`.Session.close` method + on the current :class:`.Session`, which releases any existing + transactional/connection resources still being held; transactions + specifically are rolled back. The :class:`.Session` is then + discarded. Upon next usage within the same scope, + the :class:`.scoped_session` will produce a new + :class:`.Session` object. + + """ if self.registry.has(): self.registry().close() diff --git a/lib/sqlalchemy/orm/session.py b/lib/sqlalchemy/orm/session.py index 96a6983f8..e0f79cd8a 100644 --- a/lib/sqlalchemy/orm/session.py +++ b/lib/sqlalchemy/orm/session.py @@ -2041,11 +2041,20 @@ class sessionmaker(_SessionClassMethods): with an existing :class:`.sessionmaker` factory before it is first used:: + # application starts Session = sessionmaker() - Session.configure(bind=create_engine('sqlite:///foo.db')) + + # ... later + engine = create_engine('sqlite:///foo.db') + Session.configure(bind=engine) sess = Session() + .. seealso: + + :ref:`session_getting` - introductory text on creating + sessions using :class:`.sessionmaker`. + """ def __init__(self, bind=None, class_=Session, autoflush=True, |
