Plugins - SQLAlchemy 0.2 Documentation

Plugins
Version: 0.2.0 Last Updated: 05/28/06 23:25:13

SQLAlchemy has a variety of extensions and "mods" available which provide extra functionality to SA, either via explicit usage or by augmenting the core behavior.

threadlocal

Author: Mike Bayer and Daniel Miller

Establishes `threadlocal` as the default strategy for new `ComposedSQLEngine` objects, installs a threadlocal `SessionContext` that is attached to all Mappers via a global `MapperExtension`, and establishes the global `SessionContext` under the name `sqlalchemy.objectstore`. Usually this is used in combination with `Tables` that are associated with `BoundMetaData` or `DynamicMetaData`, so that the `Session` does not need to be bound to any `Engine` explicitly.

    import sqlalchemy.mods.threadlocal
    from sqlalchemy import *

    metadata = BoundMetaData('sqlite:///')
    user_table = Table('users', metadata,
        Column('user_id', Integer, primary_key=True),
        Column('user_name', String(50), nullable=False)
    )

    class User(object):
        pass
    mapper(User, user_table)

    # thread local session
    session = objectstore.get_session()

    # "user" object is added to the session automatically
    user = User()

    session.flush()

get_session() Implemented on All Mappers

All `Mapper` objects constructed after the `threadlocal` import will receive a default `MapperExtension` which implements the `get_session()` method, returning the `Session` that is associated with the current thread by the global `SessionContext`. All newly constructed objects will automatically be attached to the `Session` corresponding to the current thread, i.e. they will skip the "transient" state and go right to "pending".

This occurs because when a `Mapper` is first constructed for a class, it decorates the classes' `__init__()` method in a manner like the following:

    oldinit = class_.__init__   # the previous init method
    def __init__(self):
        session = ext.get_session() # get Session from this Mapper's MapperExtension
        if session is EXT_PASS:
            session = None
        if session is not None:
            session.save(self)  # attach to the current session
        oldinit(self)   # call previous init method

An instance can be redirected at construction time to a different `Session` by specifying the keyword parameter `_sa_session`:

    session = create_session()  # create a new session distinct from the thread-local session
    myuser = User(_sa_session=session)  # make a new User that is saved to this session

Similarly, the entity_name parameter, which specifies an alternate `Mapper` to be used when attaching this instance to the `Session`, can be specified via `_sa_entity_name`:

    myuser = User(_sa_session=session, _sa_entity_name='altentity')

back to section top
Default Query Objects

The `MapperExtension` object's `get_session()` method is also used by the `Query` object to locate a `Session` with which to store newly loaded instances, if the `Query` is not already associated with a specific `Session`. As a result, the `Query` can be constructed standalone from a mapper or class:

    # create a Query from a class
    query = Query(User)

    # specify entity name
    query = Query(User, entity_name='foo')

    # create a Query from a mapper
    query = Query(mapper)

back to section top
objectstore Namespace

The `objectstore` is an instance of `SessionContext`, available in the `sqlalchemy` namespace which provides a proxy to the underlying `Session` bound to the current thread. `objectstore` can be treated just like the `Session` itself:

    objectstore.save(instance)
    objectstore.flush()

    objectstore.clear()

back to section top
Attaching Mappers to their Class

With `get_session()` handling the details of providing a `Session` in all cases, the `assign_mapper` function provides some of the functionality of `Query` and `Session` directly off the mapped instances themselves. This is a "monkeypatch" function that creates a primary mapper, attaches the mapper to the class, and also the methods `get`, `select`, `select_by`, `selectone`, `get_by`, `join_to`, `join_via`, `flush`, `delete`, `expire`, `refresh`, `expunge`, `merge`, `update`, `save`, and `save_or_update`:

    # "assign" a mapper to the User class/users table
    assign_mapper(User, users)

    # methods are attached to the class for selecting
    userlist = User.select_by(user_id=12)

    myuser = User.get(1)

    # mark an object as deleted for the next commit
    myuser.delete()

    # flush the changes on a specific object
    myotheruser.flush()

A more generic version of `assign_mapper` that works with any `SessionContext` is available in the assignmapper plugin.

back to section top
Engine Strategy Set to threadlocal By Default

The `threadlocal` mod also establishes `threadlocal` as the default strategy when calling the `create_engine()` function. This strategy is specified by the `strategy` keyword argument to `create_engine()` and can still be overridden to be "`plain`" or "`threadlocal`" explicitly.

An `Engine` created with the `threadlocal` strategy will use a thread-locally managed connection object for all implicit statement executions and schema operations. Recall from Database Engines that an implicit execution is an execution where the `Connection` object is opened and closed internally, and the `connect()` method on `Engine` is not used; such as:

    result = table.select().execute()

Above, the `result` variable holds onto a `ResultProxy` which is still referencing a connection returned by the connection pool. `threadlocal` strategy means that a second `execute()` statement in the same thread will use the same connection as the one referenced by `result`, assuming `result` is still referenced in memory.

The `Mapper`, `Session`, and `Query` implementations work equally well with either the `default` or `threadlocal` engine strategies. However, using the `threadlocal` strategy means that `Session` operations will use the same underlying connection as that of straight `execute()` calls with constructed SQL objects:

    # assume "threadlocal" strategy is enabled, and there is no transaction in progress

    result = table.select().execute()   # 'result' references a DBAPI connection, bound to the current thread

    object = session.select()           # the 'select' operation also uses the current thread's connection,
                                        # i.e. the same connection referenced by 'result'

    result.close()                      # return the connection to the pool.  now there is no connection
                                        # associated with the current thread.  the next execution will re-check out a
                                        # connection and re-attach to the current thread.

back to section top
SessionContext

Author: Daniel Miller

This plugin is a generalized version of the `objectstore` object provided by the `threadlocal` plugin:

    import sqlalchemy
    from sqlalchemy.ext.sessioncontext import SessionContext

    ctx = SessionContext(sqlalchemy.create_session)

    class User(object):
        pass

    mapper(User, users_table, extension=ctx.mapperextension)

    # 'u' is automatically added to the current session of 'ctx'
    u = User()

    # get the current session and flush
    ctx.current.flush()

The construction of each `Session` instance can be customized by providing a "creation function" which returns a new `Session`. The "scope" to which the session is associated, which by default is the current thread, can be customized by providing a "scope callable" which returns a hashable key that represents the current scope:

    import sqlalchemy
    from sqlalchemy.ext.sessioncontext import SessionContext

    # create an engine
    someengine = sqlalchemy.create_engine('sqlite:///')

    # a function to return a Session bound to our engine
    def make_session():
        return sqlalchemy.create_session(bind_to=someengine)

    # global declaration of "scope"
    scope = "scope1"

    # a function to return the current "session scope"
    def global_scope_func():
        return scope

    # create SessionContext with our two functions
    ctx = SessionContext(make_session, scopefunc=global_scope_func)

    # get the session corresponding to "scope1", bound to engine "someengine":
    session = ctx.current

    # switch the "scope"
    scope = "scope2"

    # get the session corresponding to "scope2", bound to engine "someengine":
    session = ctx.current

back to section top
assignmapper

Author: Mike Bayer

This is a generic version of the `assign_mapper` function present in the threadlocal mod. It requires an explicit SessionContext.

    import sqlalchemy
    from sqlalchemy.ext.sessioncontext import SessionContext
    from sqlalchemy.ext.assignmapper import assign_mapper

    # session context
    ctx = SessionContext(sqlalchemy.create_session)

    # assign mapper to class MyClass using table 'sometable', getting
    # Sessions from 'ctx'.
    assign_mapper(ctx, MyClass, sometable)

back to section top
ActiveMapper

Author: Jonathan LaCour

ActiveMapper is a so-called "declarative layer" which allows the construction of a class, a `Table`, and a `Mapper` all in one step:

    class Person(ActiveMapper):
        class mapping:
            id          = column(Integer, primary_key=True)
            full_name   = column(String)
            first_name  = column(String)
            middle_name = column(String)
            last_name   = column(String)
            birth_date  = column(DateTime)
            ssn         = column(String)
            gender      = column(String)
            home_phone  = column(String)
            cell_phone  = column(String)
            work_phone  = column(String)
            prefs_id    = column(Integer, foreign_key=ForeignKey('preferences.id'))
            addresses   = one_to_many('Address', colname='person_id', backref='person')
            preferences = one_to_one('Preferences', colname='pref_id', backref='person')

        def __str__(self):
            s =  '%s\n' % self.full_name
            s += '  * birthdate: %s\n' % (self.birth_date or 'not provided')
            s += '  * fave color: %s\n' % (self.preferences.favorite_color or 'Unknown')
            s += '  * personality: %s\n' % (self.preferences.personality_type or 'Unknown')

            for address in self.addresses:
                s += '  * address: %s\n' % address.address_1
                s += '             %s, %s %s\n' % (address.city, address.state, address.postal_code)

            return s

    class Preferences(ActiveMapper):
        class mapping:
            __table__        = 'preferences'
            id               = column(Integer, primary_key=True)
            favorite_color   = column(String)
            personality_type = column(String)

    class Address(ActiveMapper):
        class mapping:
            id          = column(Integer, primary_key=True)
            type        = column(String)
            address_1   = column(String)
            city        = column(String)
            state       = column(String)
            postal_code = column(String)
            person_id   = column(Integer, foreign_key=ForeignKey('person.id'))

More discussion on ActiveMapper can be found at Jonathan LaCour's Blog as well as the SQLAlchemy Wiki.

back to section top
SqlSoup

Author: Jonathan Ellis

SqlSoup creates mapped classes on the fly from tables, which are automatically reflected from the database based on name. It is essentially a nicer version of the "row data gateway" pattern.

    >>> from sqlalchemy.ext.sqlsoup import SqlSoup
    >>> soup = SqlSoup('sqlite:///')

    >>> users = soup.users.select()
    >>> users.sort()
    >>> users
    [Class_Users(name='Bhargan Basepair',email='basepair@example.edu',password='basepair',classname=None,admin=1),
     Class_Users(name='Joe Student',email='student@example.edu',password='student',classname=None,admin=0)]

Read more about SqlSoup on Jonathan Ellis' Blog.

back to section top
ProxyEngine

Author: Jason Pellerin

The `ProxyEngine` is used to "wrap" an `Engine`, and via subclassing `ProxyEngine` one can instrument the functionality of an arbitrary `Engine` instance through the decorator pattern. It also provides a `connect()` method which will send all `Engine` requests to different underlying engines. Its functionality in that regard is largely superceded now by `DynamicMetaData` which is a better solution.

    from sqlalchemy.ext.proxy import ProxyEngine
    proxy = ProxyEngine()

    proxy.connect('postgres://user:pw@host/db')

back to section top
SelectResults

Author: Jonas Borgström

SelectResults gives transformative behavior to the results returned from the `select` and `select_by` method of `Query`. It supports three modes of operation; per-query, per-mapper, and per-application.

SelectResults with a Query Object

    from sqlalchemy.ext.selectresults import SelectResults

    query = session.query(MyClass)
    res = SelectResults(query, table.c.column == "something")
    res = res.order_by([table.c.column]) #add an order clause

    for x in res[:10]:  # Fetch and print the top ten instances
      print x.column2

    x = list(res) # execute the query

    # Count how many instances that have column2 > 42
    # and column == "something"
    print res.filter(table.c.column2 > 42).count()

Per mapper:

SelectResults with a Mapper Object

    from sqlalchemy.ext.selectresults import SelectResultsExt
    mapper(MyClass, mytable, extension=SelectResultsExt())
    session.query(MyClass).select(mytable.c.column=="something").order_by([mytable.c.column])[2:7]

Or across an application via the `selectresults` mod:

SelectResults via mod

    import sqlalchemy.mods.selectresults

    mapper(MyClass, mytable)
    session.query(MyClass).select(mytable.c.column=="something").order_by([mytable.c.column])[2:7]

For a full listing of methods, see the generated documentation.

back to section top
Previous: Connection Pooling | Next: Modules and Classes