⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 session.txt

📁 SQLAlchemy. 经典的Python ORM框架。学习必看。
💻 TXT
📖 第 1 页 / 共 4 页
字号:
    Using the Session {@name=unitofwork}============The [Mapper](rel:advdatamapping) 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 [Session](rel:docstrings_sqlalchemy.orm.session_Session).## What does the Session do ?In the most general sense, the `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 `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 `Session` implements an interface which let's you move objects in or out of the session in a variety of ways, it provides the entryway to a `Query` object which is used to query the database for data, it is commonly used to provide transactional boundaries (though this is optional), and it also can serve as a configurational "home base" for one or more `Engine` objects, which allows various vertical and horizontal partitioning strategies to be achieved.## Getting a SessionThe `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}    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()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 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}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 achieve this, the constructor keyword `bind` is used for a basic single-database configuration:    {python}    # 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)    # 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}    # 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}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(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.  For example, an application which is calling `create_session()` in many places, which is typical for a pre-0.4 application, can be changed to use a `sessionmaker()` by just assigning the return of `sessionmaker()` to the `create_session` name:    {python}    # change from:    from sqlalchemy.orm import create_session    # to:    create_session = sessionmaker()## Using the Session A typical session conversation starts with creating a new session, or acquiring one from an ongoing context.    You save new objects and load existing ones, make changes, mark some as deleted, and then persist your changes to the database.  If your session is transactional, you use `commit()` to persist any remaining changes and to commit the transaction.  If not, you call `flush()` which will flush any remaining data to the database.Below, we open a new `Session` using a configured `sessionmaker()`, make some changes, and commit:    {python}    # configured Session class    Session = sessionmaker(autoflush=True, transactional=True)        sess = Session()    d = Data(value=10)    sess.save(d)    d2 = sess.query(Data).filter(Data.value=15).one()    d2.value = 19    sess.commit()### Quickie Intro to Object States {@name=states}It's helpful to know the states which an instance can have within a session:* *Transient* - an instance that's not in a session, and is not saved to the database; i.e. it has no database identity.  The only relationship such an object has to the ORM is that its class has a `mapper()` associated with it.* *Pending* - when you `save()` a transient instance, it becomes pending.  It still wasn't actually flushed to the database yet, but it will be when the next flush occurs.* *Persistent* - An instance which is present in the session and has a record in the database.  You get persistent instances by either flushing so that the pending instances become persistent, or by querying the database for existing instances (or moving persistent instances from other sessions into your local session).* *Detached* - an instance which has a record in the database, but is not in any session.  Theres nothing wrong with this, and you can use objects normally when they're detached, **except** they will not be able to issue any SQL in order to load collections or attributes which are not yet loaded, or were marked as "expired".Knowing these states is important, since the `Session` tries to be strict about ambiguous operations (such as trying to save the same object to two different sessions at the same time).### 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 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 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.        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`.    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 ?    Use the `object_session()` classmethod available on `Session`:            {python}        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; it's 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 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`:    {python}    >>> myobject._instance_key     (<class 'test.tables.User'>, (7,))    >>> myobject._instance_key in session.identity_map    True    >>> session.identity_map.values()    [<__main__.User object at 0x712630>, <__main__.Address object at 0x712a70>]The identity map is a weak-referencing dictionary by default.  This means that objects which are dereferenced on the outside will be removed from the session automatically.  Note that objects which are marked as "dirty" will not fall out of scope until after changes on them have been flushed; special logic kicks in at the point of auto-removal which ensures that no pending changes remain on the object, else a temporary strong reference is created to the object.Some people prefer objects to stay in the session until explicitly removed in all cases; for this,  you can specify the flag `weak_identity_map=False` to the `create_session` or `sessionmaker` functions so that the `Session` will use a regular dictionary.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}    for obj in session:        print obj

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -