summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMike Bayer <mike_mp@zzzcomputing.com>2007-08-05 20:19:26 +0000
committerMike Bayer <mike_mp@zzzcomputing.com>2007-08-05 20:19:26 +0000
commit942cba7208764e34f870ae796c97d52a705ce3ba (patch)
tree7f06d37147c39ef70a63bebe219876323441fb7b
parent0d7bce40f80d1d56ad3e43f2bd430f0ee960b812 (diff)
downloadsqlalchemy-942cba7208764e34f870ae796c97d52a705ce3ba.tar.gz
docstring stuff
-rw-r--r--doc/build/gen_docstrings.py6
-rw-r--r--lib/sqlalchemy/orm/__init__.py37
-rw-r--r--lib/sqlalchemy/orm/dynamic.py3
-rw-r--r--lib/sqlalchemy/orm/session.py76
-rw-r--r--lib/sqlalchemy/orm/shard.py2
-rw-r--r--test/orm/unitofwork.py8
6 files changed, 115 insertions, 17 deletions
diff --git a/doc/build/gen_docstrings.py b/doc/build/gen_docstrings.py
index b917469d9..28e8c1dc8 100644
--- a/doc/build/gen_docstrings.py
+++ b/doc/build/gen_docstrings.py
@@ -38,14 +38,12 @@ def make_all_docs():
make_doc(obj=orm.interfaces),
make_doc(obj=orm.mapperlib, classes=[orm.mapperlib.MapperExtension, orm.mapperlib.Mapper]),
make_doc(obj=orm.properties),
- make_doc(obj=orm.query, classes=[orm.query.Query, orm.query.QueryContext, orm.query.SelectionContext]),
- make_doc(obj=orm.session, classes=[orm.session.Session, orm.session.SessionTransaction]),
+ make_doc(obj=orm.query, classes=[orm.query.Query]),
+ make_doc(obj=orm.session, classes=[orm.session.Session]),
make_doc(obj=orm.shard),
make_doc(obj=exceptions),
- make_doc(obj=assignmapper),
make_doc(obj=associationproxy, classes=[associationproxy.AssociationProxy]),
make_doc(obj=orderinglist, classes=[orderinglist.OrderingList]),
- make_doc(obj=sessioncontext),
make_doc(obj=sqlsoup),
] + [make_doc(getattr(__import__('sqlalchemy.databases.%s' % m).databases, m)) for m in databases.__all__]
return objects
diff --git a/lib/sqlalchemy/orm/__init__.py b/lib/sqlalchemy/orm/__init__.py
index 2c1a4ffc4..871580660 100644
--- a/lib/sqlalchemy/orm/__init__.py
+++ b/lib/sqlalchemy/orm/__init__.py
@@ -21,24 +21,55 @@ from sqlalchemy.orm.query import Query
from sqlalchemy.orm.util import polymorphic_union
from sqlalchemy.orm.session import Session as _Session
from sqlalchemy.orm.session import object_session, attribute_manager, sessionmaker
-from sqlalchemy.orm.scoping import ScopedSession as scoped_session
+from sqlalchemy.orm.scoping import ScopedSession
__all__ = [ 'relation', 'column_property', 'composite', 'backref', 'eagerload',
'eagerload_all', 'lazyload', 'noload', 'deferred', 'defer',
'undefer', 'undefer_group', 'extension', 'mapper', 'clear_mappers',
'compile_mappers', 'class_mapper', 'object_mapper', 'sessionmaker',
- 'scoped_session', 'dynamic_loader', 'MapperExtension', 'Query', 'polymorphic_union',
+ 'scoped_session', 'dynamic_loader', 'MapperExtension', 'polymorphic_union',
'create_session', 'synonym', 'contains_alias',
'contains_eager', 'EXT_CONTINUE', 'EXT_STOP', 'EXT_PASS',
'object_session', 'PropComparator' ]
+def scoped_session(session_factory, scopefunc=None):
+ """Provides thread-local management of Sessions.
+
+ Usage::
+
+ Session = scoped_session(sessionmaker(autoflush=True))
+
+ To instantiate a Session object which is part of the scoped
+ context, instantiate normally::
+
+ session = Session()
+
+ Most session methods are available as classmethods from
+ the scoped session::
+
+ Session.commit()
+ Session.close()
+
+ To map classes so that new instances are saved in the current
+ Session automatically, as well as to provide session-aware
+ class attributes such as "query", use the `mapper` classmethod
+ from the scoped session::
+
+ mapper = Session.mapper
+ mapper(Class, table, ...)
+
+ """
+
+ return ScopedSession(session_factory, scopefunc=scopefunc)
+
def create_session(bind=None, **kwargs):
"""create a new [sqlalchemy.orm.session#Session].
The session by default does not begin a transaction, and requires that
flush() be called explicitly in order to persist results to the database.
- It is recommended to use the sessionmaker() function instead of create_session().
+ It is recommended to use the [sqlalchemy.orm#sessionmaker()] function
+ instead of create_session().
"""
kwargs.setdefault('autoflush', False)
kwargs.setdefault('transactional', False)
diff --git a/lib/sqlalchemy/orm/dynamic.py b/lib/sqlalchemy/orm/dynamic.py
index d06b874c9..dee2e28ce 100644
--- a/lib/sqlalchemy/orm/dynamic.py
+++ b/lib/sqlalchemy/orm/dynamic.py
@@ -2,7 +2,8 @@
a special AttributeHistory on the 'write' side."""
from sqlalchemy import exceptions
-from sqlalchemy.orm import attributes, Query, object_session
+from sqlalchemy.orm import attributes, object_session
+from sqlalchemy.orm.query import Query
from sqlalchemy.orm.mapper import has_identity
class DynamicCollectionAttribute(attributes.InstrumentedAttribute):
diff --git a/lib/sqlalchemy/orm/session.py b/lib/sqlalchemy/orm/session.py
index 80c1a5b0d..1b0c1a9a2 100644
--- a/lib/sqlalchemy/orm/session.py
+++ b/lib/sqlalchemy/orm/session.py
@@ -159,11 +159,79 @@ class SessionTransaction(object):
self.rollback()
class Session(object):
- """Encapsulates a set of objects being operated upon within an
- object-relational operation.
+ """Encapsulates a set of objects being operated upon within an object-relational operation.
- The Session object is **not** threadsafe. For thread-management
- of Sessions, see the ``sqlalchemy.ext.sessioncontext`` module.
+ The Session is the front end to SQLAlchemy's **Unit of Work** implementation. The concept
+ behind Unit of Work is to track modifications to a field of objects, and then be able to
+ flush those changes to the database in a single operation.
+
+ SQLAlchemy's unit of work includes these functions:
+
+ * The ability to track in-memory changes on scalar- and collection-based object
+ attributes, such that database persistence operations can be assembled based on those
+ changes.
+
+ * The ability to organize individual SQL queries and population of newly generated
+ primary and foreign key-holding attributes during a persist operation
+ such that referential integrity is maintained at all times.
+
+ * The ability to maintain insert ordering against the order in which
+ new instances were added to the session.
+
+ * an Identity Map, which is a dictionary keying instances to their unique primary key
+ identity. This ensures that only one copy of a particular entity is ever present
+ within the session, even if repeated load operations for the same entity occur. This
+ allows many parts of an application to get a handle to a particular object without
+ any chance of modifications going to two different places.
+
+ When dealing with instances of mapped classes, an instance may be *attached* to a
+ particular Session, else it is *unattached* . An instance also may or may not correspond
+ to an actual row in the database. These conditions break up into four distinct states:
+
+ * *Transient* - a transient instance exists within memory only and is not associated with
+ any Session. It also has no database identity and does not have a corresponding record
+ in the database. When a new instance of a class is constructed, and no default session
+ context exists with which to automatically attach the new instance, it is a transient
+ instance. The instance can then be saved to a particular session in which case it
+ becomes a *pending* instance. If a default session context exists, new instances are
+ added to that Session by default and therefore become *pending* instances immediately.
+
+ * *Pending* - a pending instance is a Session-attached object that has not yet been
+ assigned a database identity. When the Session is flushed (i.e. changes are persisted to
+ the database), a pending instance becomes persistent.
+
+ * *Persistent* - a persistent instance has a database identity and a corresponding record
+ in the database, and is also associated with a particular Session. By "database
+ identity" we mean the object is associated with a table or relational concept in the
+ database combined with a particular primary key in that table. Objects that are loaded
+ by SQLAlchemy in the context of a particular session are automatically considered
+ persistent, as are formerly pending instances which have been subject to a session
+ `flush()`.
+
+ * *Detached* - a detached instance is an instance which has a database identity and
+ corresponding row in the database, but is not attached to any Session. This occurs when
+ an instance has been removed from a Session, either because the session itself was
+ cleared or closed, or the instance was explicitly removed from the Session. The object
+ can be re-attached to a session in which case it becomes Persistent again; any
+ un-persisted changes that exist on the instance, whether they occurred during its
+ previous persistent state or during its detached state will be detected and maintained
+ by the new session. Detached instances are useful when an application needs to represent
+ a long-running operation across multiple Sessions, needs to store an object in a
+ serialized state and then restore it later (such as within an HTTP "session" object), or
+ in some cases where code needs to load instances locally which will later be associated
+ with some other Session.
+
+ The session methods which control instance state include ``save()``, ``update()``,
+ ``save_or_update()``, ``delete()``, ``merge()``, and ``expunge()``.
+
+ The Session object is **not** threadsafe, particularly during flush operations. A session
+ which is only read from (i.e. is never flushed) can be used by concurrent threads if it's
+ acceptable that some object instances may be loaded twice.
+
+ The typical pattern to managing Sessions in a multi-threaded environment is either to use
+ mutexes to limit concurrent access to one thread at a time, or more commonly to establish
+ a unique session for every thread, using a threadlocal variable. SQLAlchemy provides
+ a thread-managed Session adapter, provided by the [sqlalchemy.orm#scoped_session()] function.
"""
def __init__(self, bind=None, autoflush=True, transactional=False, twophase=False, echo_uow=False, weak_identity_map=False):
diff --git a/lib/sqlalchemy/orm/shard.py b/lib/sqlalchemy/orm/shard.py
index 9d4396d2b..69d692167 100644
--- a/lib/sqlalchemy/orm/shard.py
+++ b/lib/sqlalchemy/orm/shard.py
@@ -1,5 +1,5 @@
from sqlalchemy.orm.session import Session
-from sqlalchemy.orm import Query
+from sqlalchemy.orm.query import Query
__all__ = ['ShardedSession', 'ShardedQuery']
diff --git a/test/orm/unitofwork.py b/test/orm/unitofwork.py
index f065267f7..e0b0a23d3 100644
--- a/test/orm/unitofwork.py
+++ b/test/orm/unitofwork.py
@@ -640,7 +640,7 @@ class DefaultTest(UnitOfWorkTest):
Session.close()
- l = Query(Hoho).select()
+ l = Hoho.query.all()
(h1, h2, h3, h4, h5) = l
@@ -941,7 +941,7 @@ class SaveTest(UnitOfWorkTest):
# select both
#Session.close()
- userlist = Query(m).select(users.c.user_id.in_(u.user_id, u2.user_id), order_by=[users.c.user_name])
+ userlist = User.query.filter(users.c.user_id.in_(u.user_id, u2.user_id)).order_by([users.c.user_name]).all()
print repr(u.user_id), repr(userlist[0].user_id), repr(userlist[0].user_name)
self.assert_(u.user_id == userlist[0].user_id and userlist[0].user_name == 'modifiedname')
self.assert_(u2.user_id == userlist[1].user_id and userlist[1].user_name == 'savetester2')
@@ -1464,7 +1464,7 @@ class ManyToManyTest(UnitOfWorkTest):
item.keywords = []
for kname in [e['keyword'][1]['name'] for e in elem['keywords'][1]]:
try:
- k = Query(keywordmapper).select(keywords.c.name == kname)[0]
+ k = Keyword.query.filter(keywords.c.name == kname)[0]
except IndexError:
k = Keyword()
k.name= kname
@@ -1474,7 +1474,7 @@ class ManyToManyTest(UnitOfWorkTest):
Session.commit()
Session.close()
- l = Query(m).select(items.c.item_name.in_(*[e['item_name'] for e in data[1:]]), order_by=[items.c.item_name])
+ l = Item.query.filter(items.c.item_name.in_(*[e['item_name'] for e in data[1:]])).order_by(items.c.item_name).all()
self.assert_result(l, *data)
def testm2mmultitable(self):