diff options
| author | Mike Bayer <mike_mp@zzzcomputing.com> | 2007-08-05 20:19:26 +0000 |
|---|---|---|
| committer | Mike Bayer <mike_mp@zzzcomputing.com> | 2007-08-05 20:19:26 +0000 |
| commit | 942cba7208764e34f870ae796c97d52a705ce3ba (patch) | |
| tree | 7f06d37147c39ef70a63bebe219876323441fb7b | |
| parent | 0d7bce40f80d1d56ad3e43f2bd430f0ee960b812 (diff) | |
| download | sqlalchemy-942cba7208764e34f870ae796c97d52a705ce3ba.tar.gz | |
docstring stuff
| -rw-r--r-- | doc/build/gen_docstrings.py | 6 | ||||
| -rw-r--r-- | lib/sqlalchemy/orm/__init__.py | 37 | ||||
| -rw-r--r-- | lib/sqlalchemy/orm/dynamic.py | 3 | ||||
| -rw-r--r-- | lib/sqlalchemy/orm/session.py | 76 | ||||
| -rw-r--r-- | lib/sqlalchemy/orm/shard.py | 2 | ||||
| -rw-r--r-- | test/orm/unitofwork.py | 8 |
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): |
