sqlalchemy-commits Mailing List for SQLAlchemy (Page 362)
Brought to you by:
zzzeek
You can subscribe to this list here.
2006 |
Jan
|
Feb
(74) |
Mar
(167) |
Apr
(127) |
May
(190) |
Jun
(119) |
Jul
(77) |
Aug
(82) |
Sep
(84) |
Oct
(153) |
Nov
(45) |
Dec
(54) |
---|---|---|---|---|---|---|---|---|---|---|---|---|
2007 |
Jan
(109) |
Feb
(80) |
Mar
(110) |
Apr
(106) |
May
(92) |
Jun
(147) |
Jul
(288) |
Aug
(307) |
Sep
(108) |
Oct
(156) |
Nov
(147) |
Dec
(134) |
2008 |
Jan
(126) |
Feb
(91) |
Mar
(184) |
Apr
(208) |
May
(212) |
Jun
(54) |
Jul
(106) |
Aug
(80) |
Sep
(58) |
Oct
(80) |
Nov
(119) |
Dec
(220) |
2009 |
Jan
(202) |
Feb
(50) |
Mar
(70) |
Apr
(46) |
May
(80) |
Jun
(61) |
Jul
(146) |
Aug
(81) |
Sep
(71) |
Oct
(74) |
Nov
(66) |
Dec
(82) |
2010 |
Jan
(112) |
Feb
(169) |
Mar
(235) |
Apr
(77) |
May
(22) |
Jun
(31) |
Jul
(46) |
Aug
(46) |
Sep
(70) |
Oct
(36) |
Nov
(37) |
Dec
(79) |
2011 |
Jan
(46) |
Feb
(54) |
Mar
(65) |
Apr
(73) |
May
(31) |
Jun
(46) |
Jul
(40) |
Aug
(36) |
Sep
(44) |
Oct
(33) |
Nov
(19) |
Dec
(10) |
2012 |
Jan
(60) |
Feb
(37) |
Mar
(35) |
Apr
(28) |
May
(27) |
Jun
(50) |
Jul
(33) |
Aug
(88) |
Sep
(64) |
Oct
(74) |
Nov
(62) |
Dec
(41) |
2013 |
Jan
(30) |
Feb
(37) |
Mar
(39) |
Apr
(52) |
May
(40) |
Jun
(85) |
Jul
(74) |
Aug
(76) |
Sep
(26) |
Oct
(76) |
Nov
(63) |
Dec
(65) |
2014 |
Jan
(68) |
Feb
(82) |
Mar
(87) |
Apr
(24) |
May
(66) |
Jun
(34) |
Jul
(86) |
Aug
(75) |
Sep
(70) |
Oct
(41) |
Nov
(23) |
Dec
(53) |
2015 |
Jan
(40) |
Feb
(39) |
Mar
(69) |
Apr
(64) |
May
(40) |
Jun
(43) |
Jul
(20) |
Aug
(48) |
Sep
(38) |
Oct
(28) |
Nov
(34) |
Dec
(44) |
2016 |
Jan
(82) |
Feb
(49) |
Mar
(25) |
Apr
(21) |
May
(19) |
Jun
(46) |
Jul
(38) |
Aug
(21) |
Sep
(33) |
Oct
(44) |
Nov
(26) |
Dec
(10) |
2017 |
Jan
(52) |
Feb
(18) |
Mar
(61) |
Apr
(43) |
May
(57) |
Jun
(36) |
Jul
(37) |
Aug
|
Sep
|
Oct
|
Nov
|
Dec
|
From: <co...@sq...> - 2006-05-06 00:55:06
|
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head><style type="text/css"><!-- #msg dl { border: 1px #006 solid; background: #369; padding: 6px; color: #fff; } #msg dt { float: left; width: 6em; font-weight: bold; } #msg dt:after { content:':';} #msg dl, #msg dt, #msg ul, #msg li { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; } #msg dl a { font-weight: bold} #msg dl a:link { color:#fc3; } #msg dl a:active { color:#ff0; } #msg dl a:visited { color:#cc6; } h3 { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; font-weight: bold; } #msg pre { overflow: auto; background: #ffc; border: 1px #fc0 solid; padding: 6px; } #msg ul, pre { overflow: auto; } #patch { width: 100%; } #patch h4 {font-family: verdana,arial,helvetica,sans-serif;font-size:10pt;padding:8px;background:#369;color:#fff;margin:0;} #patch .propset h4, #patch .binary h4 {margin:0;} #patch pre {padding:0;line-height:1.2em;margin:0;} #patch .diff {width:100%;background:#eee;padding: 0 0 10px 0;overflow:auto;} #patch .propset .diff, #patch .binary .diff {padding:10px 0;} #patch span {display:block;padding:0 10px;} #patch .modfile, #patch .addfile, #patch .delfile, #patch .propset, #patch .binary, #patch .copfile {border:1px solid #ccc;margin:10px 0;} #patch ins {background:#dfd;text-decoration:none;display:block;padding:0 10px;} #patch del {background:#fdd;text-decoration:none;display:block;padding:0 10px;} #patch .lines, .info {color:#888;background:#fff;} --></style> <title>[1411] sqlalchemy/branches/schema/test: more doc fixes, unit tests working</title> </head> <body> <div id="msg"> <dl> <dt>Revision</dt> <dd>1411</dd> <dt>Author</dt> <dd>zzzeek</dd> <dt>Date</dt> <dd>2006-05-05 19:54:41 -0500 (Fri, 05 May 2006)</dd> </dl> <h3>Log Message</h3> <pre>more doc fixes, unit tests working</pre> <h3>Modified Paths</h3> <ul> <li><a href="#sqlalchemybranchesschemadocbuildcomponentspydocmyt">sqlalchemy/branches/schema/doc/build/components/pydoc.myt</a></li> <li><a href="#sqlalchemybranchesschemadocbuildcontentdbenginetxt">sqlalchemy/branches/schema/doc/build/content/dbengine.txt</a></li> <li><a href="#sqlalchemybranchesschemadocbuildcontentmetadatatxt">sqlalchemy/branches/schema/doc/build/content/metadata.txt</a></li> <li><a href="#sqlalchemybranchesschemadocbuildcontentpluginstxt">sqlalchemy/branches/schema/doc/build/content/plugins.txt</a></li> <li><a href="#sqlalchemybranchesschemadocbuildcontentpoolingmyt">sqlalchemy/branches/schema/doc/build/content/pooling.myt</a></li> <li><a href="#sqlalchemybranchesschemadocbuildcontenttutorialtxt">sqlalchemy/branches/schema/doc/build/content/tutorial.txt</a></li> <li><a href="#sqlalchemybranchesschemadocbuildcontenttypestxt">sqlalchemy/branches/schema/doc/build/content/types.txt</a></li> <li><a href="#sqlalchemybranchesschemalibsqlalchemyexceptionspy">sqlalchemy/branches/schema/lib/sqlalchemy/exceptions.py</a></li> <li><a href="#sqlalchemybranchesschemalibsqlalchemymodsthreadlocalpy">sqlalchemy/branches/schema/lib/sqlalchemy/mods/threadlocal.py</a></li> <li><a href="#sqlalchemybranchesschemalibsqlalchemyorm__init__py">sqlalchemy/branches/schema/lib/sqlalchemy/orm/__init__.py</a></li> <li><a href="#sqlalchemybranchesschemalibsqlalchemyormdependencypy">sqlalchemy/branches/schema/lib/sqlalchemy/orm/dependency.py</a></li> <li><a href="#sqlalchemybranchesschemalibsqlalchemyormmapperpy">sqlalchemy/branches/schema/lib/sqlalchemy/orm/mapper.py</a></li> <li><a href="#sqlalchemybranchesschemalibsqlalchemyormquerypy">sqlalchemy/branches/schema/lib/sqlalchemy/orm/query.py</a></li> <li><a href="#sqlalchemybranchesschemalibsqlalchemyormunitofworkpy">sqlalchemy/branches/schema/lib/sqlalchemy/orm/unitofwork.py</a></li> <li><a href="#sqlalchemybranchesschemalibsqlalchemyschemapy">sqlalchemy/branches/schema/lib/sqlalchemy/schema.py</a></li> <li><a href="#sqlalchemybranchesschemalibsqlalchemysqlpy">sqlalchemy/branches/schema/lib/sqlalchemy/sql.py</a></li> <li><a href="#sqlalchemybranchesschemalibsqlalchemytypespy">sqlalchemy/branches/schema/lib/sqlalchemy/types.py</a></li> <li><a href="#sqlalchemybranchesschematestmanytomanypy">sqlalchemy/branches/schema/test/manytomany.py</a></li> <li><a href="#sqlalchemybranchesschematestobjectstorepy">sqlalchemy/branches/schema/test/objectstore.py</a></li> </ul> </div> <div id="patch"> <h3>Diff</h3> <a id="sqlalchemybranchesschemadocbuildcomponentspydocmyt"></a> <div class="modfile"><h4>Modified: sqlalchemy/branches/schema/doc/build/components/pydoc.myt (1410 => 1411)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/schema/doc/build/components/pydoc.myt 2006-05-05 17:52:32 UTC (rev 1410) +++ sqlalchemy/branches/schema/doc/build/components/pydoc.myt 2006-05-06 00:54:41 UTC (rev 1411) </span><span class="lines">@@ -1,7 +1,7 @@ </span><span class="cx"> <%global> </span><span class="cx"> import re, types, string </span><span class="cx"> def format_paragraphs(text): </span><del>- return re.sub(r'([\w ])\n([\w ])', r'\1 \2', text or '', re.S) </del><ins>+ return re.sub(r'([\w])\n([\w])', r'\1 \2', text or '', re.S) </ins><span class="cx"> </%global> </span><span class="cx"> </span><span class="cx"> <%method obj_doc> </span></span></pre></div> <a id="sqlalchemybranchesschemadocbuildcontentdbenginetxt"></a> <div class="modfile"><h4>Modified: sqlalchemy/branches/schema/doc/build/content/dbengine.txt (1410 => 1411)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/schema/doc/build/content/dbengine.txt 2006-05-05 17:52:32 UTC (rev 1410) +++ sqlalchemy/branches/schema/doc/build/content/dbengine.txt 2006-05-06 00:54:41 UTC (rev 1411) </span><span class="lines">@@ -176,6 +176,20 @@ </span><span class="cx"> </span><span class="cx"> While the `close()` method is still available with the "threadlocal" strategy, it should be used carefully. Above, if we issued a `close()` call on `r1`, and then tried to further work with results from `r2`, `r2` would be in an invalid state since its connection was already returned to the pool. By relying on `__del__()` to automatically clean up resources, this condition will never occur. </span><span class="cx"> </span><ins>+To get at the actual `Connection` object which is used by implicit executions, call the `contextual_connection()` method on `Engine`: + + {python title="Contextual Connection"} + # threadlocal strategy + db = create_engine('mysql://localhost/test', strategy='threadlocal') + + conn1 = db.contextual_connection() + conn2 = db.contextual_connection() + + >>> assert conn1 is conn2 + True + +When the `plain` strategy is used, the `contextual_connection()` method is synonymous with the `connect()` method; both return a distinct connection from the pool. + </ins><span class="cx"> At this point, you're probably saying, "wow, why would anyone *ever* want to use the [insert name here] strategy ??" Advantages to `plain` include that connection resources are immediately returned to the connection pool, without any reliance upon the `__del__()` method; there is no chance of resources being left around by a Python implementation that doesn't necessarily call `__del__()` immediately. Advantages to `threadlocal` include that resources can be left to clean up after themselves, application code can be more minimal, its guaranteed that only one connection is used per thread, and there is no chance of a "connection pool block", which is when an execution hangs because the current thread has already checked out all remaining resources. </span><span class="cx"> </span><span class="cx"> ### Transactions {@name=transactions} </span></span></pre></div> <a id="sqlalchemybranchesschemadocbuildcontentmetadatatxt"></a> <div class="modfile"><h4>Modified: sqlalchemy/branches/schema/doc/build/content/metadata.txt (1410 => 1411)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/schema/doc/build/content/metadata.txt 2006-05-05 17:52:32 UTC (rev 1410) +++ sqlalchemy/branches/schema/doc/build/content/metadata.txt 2006-05-06 00:54:41 UTC (rev 1411) </span><span class="lines">@@ -29,7 +29,7 @@ </span><span class="cx"> Column('pref_value', String(100)) </span><span class="cx"> ) </span><span class="cx"> </span><del>-The specific datatypes for each Column, such as Integer, String, etc. are defined in [types](rel:types) and are part of the `sqlalchemy` module namespace. </del><ins>+The specific datatypes for each Column, such as Integer, String, etc. are described in [types](rel:types), and exist within the module `sqlalchemy.types` as well as the global `sqlalchemy` namespace. </ins><span class="cx"> </span><span class="cx"> The `MetaData` object supports some handy methods, such as getting a list of Tables in the order (or reverse) of their dependency: </span><span class="cx"> </span></span></pre></div> <a id="sqlalchemybranchesschemadocbuildcontentpluginstxt"></a> <div class="modfile"><h4>Modified: sqlalchemy/branches/schema/doc/build/content/plugins.txt (1410 => 1411)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/schema/doc/build/content/plugins.txt 2006-05-05 17:52:32 UTC (rev 1410) +++ sqlalchemy/branches/schema/doc/build/content/plugins.txt 2006-05-06 00:54:41 UTC (rev 1411) </span><span class="lines">@@ -7,6 +7,8 @@ </span><span class="cx"> </span><span class="cx"> ### threadlocal </span><span class="cx"> </span><ins>+Establishes 'threadlocal' as the default strategy for new `Engine`s, and installs an implicitly-accessible threadlocal `Session` context for the `current_session()` function. 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. + </ins><span class="cx"> {python} </span><span class="cx"> import sqlalchemy.mods.threadlocal </span><span class="cx"> from sqlalchemy import * </span><span class="lines">@@ -22,20 +24,68 @@ </span><span class="cx"> mapper(User, user_table) </span><span class="cx"> </span><span class="cx"> # thread local session </span><del>- session = get_session() </del><ins>+ session = current_session() </ins><span class="cx"> </span><span class="cx"> # "user" object is added to the session automatically </span><span class="cx"> user = User() </span><span class="cx"> </span><span class="cx"> session.flush() </span><span class="cx"> </span><del>-Establishes 'threadlocal' as the default strategy for new `Engine`s, and installs a thread local Session context for the `get_session()` function. Usually this is used in combination with `BoundMetaData` or `DynamicMetaData` for `Table` objects, so that the `Session` does not need to be bound to any `Engine` explicitly. </del><span class="cx"> </span><del>-#### get_session() Method {@name=getsession} </del><ins>+#### current_session() Method {@name=currentsession} + +`current_session()` is a method that always exists in the `sqlalchemy.orm.session` module, however by default it returns `None`. When `threadlocal` is installed, `current_session()` returns the `Session` that is associated with the current thread. `current_session()` also takes an object instance as an optional argument, which is to allow objects or classes that are associated with a specific session context; this feature is not used by the `threadlocal` mod. + +This method is called when new, transient objects are created in order to locate a `Session` to which the new object can be attached to. This occurs because when a `Mapper` is first constructed for a class, it decorates the classes' `__init__()` method in a manner like the following: + + {python} + oldinit = class_.__init__ # the previous init method + def __init__(self): + session = current_session(self) + if session is not None: + session.save(self) # attach to the current session + oldinit(self) # call previous init method + +Since the `threadlocal` module provides an implementation for `current_session()` which returns the thread-associated `Session`, the result is that the `__init__()` method of all mapped classes will automatically add the new instance to the current thread's `Session`. + +An instance can be redirected at construction time to a different `Session` upon construction by specifying the keyword parameter `_sa_session`: + + {python} + 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 for persisting an instance, can be specified via `_sa_entity_name`: + + {python} + myuser = User(_sa_session=session, _sa_entity_name='altentity') + +#### Default Query Objects + +`current_session()` 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: + + {python} + # 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) + </ins><span class="cx"> #### objectstore Namespace {@name=objectstore} </span><ins>+ +The `objectstore` is an object added to the `sqlalchemy` namespace which provides a global proxy to the `Session` returned by `current_session()`. `objectstore` can be treated just like the `Session` itself: + + {python} + objectstore.save(instance) + objectstore.flush() + + objectstore.clear() + </ins><span class="cx"> #### Attaching Mappers to their Class {@name=attaching} </span><span class="cx"> </span><del>-A full-blown "monkeypatch" function that creates a primary mapper, attaches the mapper to the class, and also the methods `get, get_by, select, select_by, selectone, selectfirst, commit, expire, refresh, expunge` and `delete`: </del><ins>+With `current_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, get_by, select, select_by, selectone, selectfirst, commit, expire, refresh, expunge` and `delete`: </ins><span class="cx"> </span><span class="cx"> {python} </span><span class="cx"> # "assign" a mapper to the User class/users table </span><span class="lines">@@ -51,7 +101,20 @@ </span><span class="cx"> </span><span class="cx"> # flush the changes on a specific object </span><span class="cx"> myotheruser.flush() </span><ins>+ +#### Engine Strategy Set to threadlocal By Default {@name=engine} + +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 [dbengine](rel:dbengine) 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: + + {python} + result = table.select().execute() </ins><span class="cx"> </span><ins>+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 have no dependency upon the strategy used by the underlying `Engine`; `Session` provides explicit connections for all internal executions which are properly managed. Additionally, when the `Session` uses a transaction, it internally insures that all operations are performed with the single `Connection` corresponding to the transaction. However, when the `threadlocal` strategy is used, orm operations will make usage of the same `contextual_connection()` method which will return the same connection used by other implicit executions. + </ins><span class="cx"> ### SessionContext </span><span class="cx"> </span><span class="cx"> This plugin solves many of the problems that `threadlocal` solves, but does it in a more class-localized way. </span></span></pre></div> <a id="sqlalchemybranchesschemadocbuildcontentpoolingmyt"></a> <div class="modfile"><h4>Modified: sqlalchemy/branches/schema/doc/build/content/pooling.myt (1410 => 1411)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/schema/doc/build/content/pooling.myt 2006-05-05 17:52:32 UTC (rev 1410) +++ sqlalchemy/branches/schema/doc/build/content/pooling.myt 2006-05-06 00:54:41 UTC (rev 1411) </span><span class="lines">@@ -1,66 +1,87 @@ </span><span class="cx"> <%flags>inherit='document_base.myt'</%flags> </span><span class="cx"> <%attr>title='Connection Pooling'</%attr> </span><del>-<&|doclib.myt:item, name="pooling", description="Connection Pooling" &> - <P><b>Note:</b>This section describes the connection pool module of SQLAlchemy, which is the smallest component of the library that can be used on its own. If you are interested in using SQLAlchemy for query construction or Object Relational Mapping, this module is automatically managed behind the scenes; you can skip ahead to <&formatting.myt:link,path="dbengine"&> in that case.</p> - <p>At the base of any database helper library is a system of efficiently acquiring connections to the database. Since the establishment of a database connection is typically a somewhat expensive operation, an application needs a way to get at database connections repeatedly without incurring the full overhead each time. Particularly for server-side web applications, a connection pool is the standard way to maintain a "pool" of database connections which are used over and over again among many requests. Connection pools typically are configured to maintain a certain "size", which represents how many connections can be used simultaneously without resorting to creating more newly-established connections. - </p> - <p>SQLAlchemy includes a pooling module that can be used completely independently of the rest of the toolset. This section describes how it can be used on its own, as well as the available options. If SQLAlchemy is being used more fully, the connection pooling described below occurs automatically. The options are still available, though, so this core feature is a good place to start. - </p> - <&|doclib.myt:item, name="establishing", description="Establishing a Transparent Connection Pool" &> - Any DBAPI module can be "proxied" through the connection pool using the following technique (note that the usage of 'psycopg2' is <b>just an example</b>; substitute whatever DBAPI module you'd like): - - <&|formatting.myt:code&> - import sqlalchemy.pool as pool - import psycopg2 as psycopg - psycopg = pool.manage(psycopg) - - # then connect normally - connection = psycopg.connect(database='test', username='scott', password='tiger') - </&> - <p>This produces a <span class="codeline">sqlalchemy.pool.DBProxy</span> object which supports the same <span class="codeline">connect()</span> function as the original DBAPI module. Upon connection, a thread-local connection proxy object is returned, which delegates its calls to a real DBAPI connection object. This connection object is stored persistently within a connection pool (an instance of <span class="codeline">sqlalchemy.pool.Pool</span>) that corresponds to the exact connection arguments sent to the <span class="codeline">connect()</span> function. The connection proxy also returns a proxied cursor object upon calling <span class="codeline">connection.cursor()</span>. When all cursors as well as the connection proxy are de-referenced, the connection is automatically made available again by the owning pool object.</p> - - <p>Basically, the <span class="codeline">connect()</span> function is used in its usual way, and the pool module transparently returns thread-local pooled connections. Each distinct set of connect arguments corresponds to a brand new connection pool created; in this way, an application can maintain connections to multiple schemas and/or databases, and each unique connect argument set will be managed by a different pool object.</p> - </&> </del><ins>+<!-- WARNING! This file was automatically generated. + Modify .txt file if need you to change the content.--> +<&|doclib.myt:item, name="pooling", description="Connection Pooling"&> </ins><span class="cx"> </span><del>- <&|doclib.myt:item, name="configuration", description="Connection Pool Configuration" &> - <p>When proxying a DBAPI module through the <span class="codeline">pool</span> module, options exist for how the connections should be pooled: - </p> - <ul> - <li>echo=False : if set to True, connections being pulled and retrieved from/to the pool will be logged to the standard output, as well as pool sizing information.</li> - <li>use_threadlocal=True : if set to True, repeated calls to connect() within the same application thread will be guaranteed to return the <b>same</b> connection object, if one has already been retrieved from the pool and has not been returned yet. This allows code to retrieve a connection from the pool, and then while still holding on to that connection, to call other functions which also ask the pool for a connection of the same arguments; those functions will act upon the same connection that the calling method is using. Note that once the connection is returned to the pool, it then may be used by another thread. To guarantee a single unique connection per thread that <b>never</b> changes, use the option <span class="codeline">poolclass=SingletonThreadPool</span>, in which case the use_threadlocal parameter is automatically set to False.</li> - <li>poolclass=QueuePool : the Pool class used by the pool module to provide pooling. QueuePool uses the Python <span class="codeline">Queue.Queue</span> class to maintain a list of available connections. A developer can supply his or her own Pool class to supply a different pooling algorithm. Also included is the ThreadSingletonPool, which provides a single distinct connection per thread and is required with SQLite.</li> - <li>pool_size=5 : used by QueuePool - the size of the pool to be maintained. This is the largest number of connections that will be kept persistently in the pool. Note that the pool begins with no connections; once this number of connections is requested, that number of connections will remain.</li> - <li>max_overflow=10 : used by QueuePool - the maximum overflow size of the pool. When the number of checked-out connections reaches the size set in pool_size, additional connections will be returned up to this limit. When those additional connections are returned to the pool, they are disconnected and discarded. It follows then that the total number of simultaneous connections the pool will allow is pool_size + max_overflow, and the total number of "sleeping" connections the pool will allow is pool_size. max_overflow can be set to -1 to indicate no overflow limit; no limit will be placed on the total number of concurrent connections.</li> - </ul> - </&> - - <&|doclib.myt:item, name="custom", description="Custom Pool Construction" &> - <p>One level below using a DBProxy to make transparent pools is creating the pool yourself. The pool module comes with two implementations of connection pools: <span class="codeline">QueuePool</span> and <span class="codeline">SingletonThreadPool</span>. While QueuePool uses Queue.Queue to provide connections, SingletonThreadPool provides a single per-thread connection which SQLite requires.</p> - - <p>Constructing your own pool involves passing a callable used to create a connection. Through this method, custom connection schemes can be made, such as a connection that automatically executes some initialization commands to start. The options from the previous section can be used as they apply to QueuePool or SingletonThreadPool.</p> - <&|formatting.myt:code, title="Plain QueuePool"&> - import sqlalchemy.pool as pool - import psycopg2 - - def getconn(): - c = psycopg2.connect(username='ed', host='127.0.0.1', dbname='test') - # execute an initialization function on the connection before returning - c.cursor.execute("setup_encodings()") - return c - - p = pool.QueuePool(getconn, max_overflow=10, pool_size=5, use_threadlocal=True) - </&> </del><ins>+<p>This section describes the connection pool module of SQLAlchemy. The <code>Pool</code> object it provides is normally embedded within an <code>Engine</code> instance. For most cases, explicit access to the pool module is not required. However, the <code>Pool</code> object can be used on its own, without the rest of SA, to manage DBAPI connections; this section describes that usage. Also, this section will describe in more detail how to customize the pooling strategy used by an <code>Engine</code>. +</p> +<p>At the base of any database helper library is a system of efficiently acquiring connections to the database. Since the establishment of a database connection is typically a somewhat expensive operation, an application needs a way to get at database connections repeatedly without incurring the full overhead each time. Particularly for server-side web applications, a connection pool is the standard way to maintain a "pool" of database connections which are used over and over again among many requests. Connection pools typically are configured to maintain a certain "size", which represents how many connections can be used simultaneously without resorting to creating more newly-established connections. +</p> </ins><span class="cx"> </span><del>- <&|formatting.myt:code, title="SingletonThreadPool"&> - import sqlalchemy.pool as pool - import sqlite - - def getconn(): - return sqlite.connect(filename='myfile.db') - - # SQLite connections require the SingletonThreadPool - p = pool.SingletonThreadPool(getconn) - </&> </del><ins>+<&|doclib.myt:item, name="establishing", description="Establishing a Transparent Connection Pool"&> </ins><span class="cx"> </span><del>- </&> -</&> </del><span class="cx">\ No newline at end of file </span><ins>+<p>Any DBAPI module can be "proxied" through the connection pool using the following technique (note that the usage of 'psycopg2' is <strong>just an example</strong>; substitute whatever DBAPI module you'd like): +</p> +<&|formatting.myt:code, use_sliders="True", syntaxtype="python"&> import sqlalchemy.pool as pool + import psycopg2 as psycopg + psycopg = pool.manage(psycopg) + + # then connect normally + connection = psycopg.connect(database='test', username='scott', password='tiger') + </&><p>This produces a <code>sqlalchemy.pool.DBProxy</code> object which supports the same <code>connect()</code> function as the original DBAPI module. Upon connection, a thread-local connection proxy object is returned, which delegates its calls to a real DBAPI connection object. This connection object is stored persistently within a connection pool (an instance of <code>sqlalchemy.pool.Pool</code>) that corresponds to the exact connection arguments sent to the <code>connect()</code> function. The connection proxy also returns a proxied cursor object upon calling <code>connection.cursor()</code>. When all cursors as well as the connection proxy are de-referenced, the connection is automatically made available again by the owning pool object. +</p> +<p>Basically, the <code>connect()</code> function is used in its usual way, and the pool module transparently returns thread-local pooled connections. Each distinct set of connect arguments corresponds to a brand new connection pool created; in this way, an application can maintain connections to multiple schemas and/or databases, and each unique connect argument set will be managed by a different pool object. +</p> + +</&> +<&|doclib.myt:item, name="configuration", description="Connection Pool Configuration"&> + +<p>When proxying a DBAPI module through the <code>pool</code> module, options exist for how the connections should be pooled: +</p> +<ul> + <li> + echo=False : if set to True, connections being pulled and retrieved from/to the pool will be logged to the standard output, as well as pool sizing information. + </li> + + <li> + use_threadlocal=True : if set to True, repeated calls to connect() within the same application thread will be guaranteed to return the <strong>same</strong> connection object, if one has already been retrieved from the pool and has not been returned yet. This allows code to retrieve a connection from the pool, and then while still holding on to that connection, to call other functions which also ask the pool for a connection of the same arguments; those functions will act upon the same connection that the calling method is using. Note that once the connection is returned to the pool, it then may be used by another thread. To guarantee a single unique connection per thread that <strong>never</strong> changes, use the option <code>poolclass=SingletonThreadPool</code>, in which case the use_threadlocal parameter is automatically set to False. + </li> + + <li> + poolclass=QueuePool : the Pool class used by the pool module to provide pooling. QueuePool uses the Python <code>Queue.Queue</code> class to maintain a list of available connections. A developer can supply his or her own Pool class to supply a different pooling algorithm. Also included is the <code>SingletonThreadPool</code>, which provides a single distinct connection per thread and is required with SQLite. + </li> + + <li> + pool_size=5 : used by <code>QueuePool</code> - the size of the pool to be maintained. This is the largest number of connections that will be kept persistently in the pool. Note that the pool begins with no connections; once this number of connections is requested, that number of connections will remain. + </li> + + <li> + max_overflow=10 : used by <code>QueuePool</code> - the maximum overflow size of the pool. When the number of checked-out connections reaches the size set in pool_size, additional connections will be returned up to this limit. When those additional connections are returned to the pool, they are disconnected and discarded. It follows then that the total number of simultaneous connections the pool will allow is <code>pool_size</code> + <code>max_overflow</code>, and the total number of "sleeping" connections the pool will allow is <code>pool_size</code>. <code>max_overflow</code> can be set to -1 to indicate no overflow limit; no limit will be placed on the total number of concurrent connections. + </li> + + <li> + timeout=30 : used by <code>QueuePool</code> - the timeout before giving up on returning a connection, if none are available and the <code>max_overflow</code> has been reached. + </li> +</ul> + +</&> +<&|doclib.myt:item, name="custom", description="Custom Pool Construction"&> + +<p>One level below using a DBProxy to make transparent pools is creating the pool yourself. The pool module comes with two implementations of connection pools: <code>QueuePool</code> and <code>SingletonThreadPool</code>. While <code>QueuePool</code> uses <code>Queue.Queue</code> to provide connections, <code>SingletonThreadPool</code> provides a single per-thread connection which SQLite requires. +</p> +<p>Constructing your own pool involves passing a callable used to create a connection. Through this method, custom connection schemes can be made, such as a connection that automatically executes some initialization commands to start. The options from the previous section can be used as they apply to <code>QueuePool</code> or <code>SingletonThreadPool</code>. +</p> +<&|formatting.myt:code, use_sliders="True", syntaxtype="python", title="Plain QueuePool"&> import sqlalchemy.pool as pool + import psycopg2 + + def getconn(): + c = psycopg2.connect(username='ed', host='127.0.0.1', dbname='test') + # execute an initialization function on the connection before returning + c.cursor.execute("setup_encodings()") + return c + + p = pool.QueuePool(getconn, max_overflow=10, pool_size=5, use_threadlocal=True) + </&><p>Or with SingletonThreadPool: +</p> +<&|formatting.myt:code, use_sliders="True", syntaxtype="python", title="SingletonThreadPool"&> import sqlalchemy.pool as pool + import sqlite + + def getconn(): + return sqlite.connect(filename='myfile.db') + + # SQLite connections require the SingletonThreadPool + p = pool.SingletonThreadPool(getconn) + </&> +</&> +</&> </ins></span></pre></div> <a id="sqlalchemybranchesschemadocbuildcontenttutorialtxt"></a> <div class="modfile"><h4>Modified: sqlalchemy/branches/schema/doc/build/content/tutorial.txt (1410 => 1411)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/schema/doc/build/content/tutorial.txt 2006-05-05 17:52:32 UTC (rev 1410) +++ sqlalchemy/branches/schema/doc/build/content/tutorial.txt 2006-05-06 00:54:41 UTC (rev 1411) </span><span class="lines">@@ -87,9 +87,9 @@ </span><span class="cx"> {python} </span><span class="cx"> >>> dynamic = DynamicMetaData() # create a Dynamic metadata object </span><span class="cx"> >>> dynamic.connect('sqlite:///:memory:') # connect it to SQLite </span><del>- >>> dynamic.connect('postgres:///scott:tiger@localhost/mydb') # connect it to PostGres </del><ins>+ >>> dynamic.connect('postgres://scott:tiger@localhost/mydb') # connect it to PostGres </ins><span class="cx"> </span><del>- >>> myengine = create_engine('mysql:///127.0.0.1') </del><ins>+ >>> myengine = create_engine('mysql://127.0.0.1') </ins><span class="cx"> >>> dynamic.connect(myengine) # connect to an externally-defined engine </span><span class="cx"> </span><span class="cx"> The `DynamicMetaData` object binds to different engines on a thread local basis. This means that one thread of your application can be connected to one database, while another is connected to a different database. The `DynamicMetaData` object also keeps a reference to each bound engine internally, so that each connection string is only initialized once. </span></span></pre></div> <a id="sqlalchemybranchesschemadocbuildcontenttypestxt"></a> <div class="modfile"><h4>Modified: sqlalchemy/branches/schema/doc/build/content/types.txt (1410 => 1411)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/schema/doc/build/content/types.txt 2006-05-05 17:52:32 UTC (rev 1410) +++ sqlalchemy/branches/schema/doc/build/content/types.txt 2006-05-06 00:54:41 UTC (rev 1411) </span><span class="lines">@@ -23,6 +23,7 @@ </span><span class="cx"> class Float(Numeric): </span><span class="cx"> def __init__(self, precision=10) </span><span class="cx"> </span><ins>+ # DateTime, Date and Time types deal with datetime objects from the Python datetime module </ins><span class="cx"> class DateTime(TypeEngine) </span><span class="cx"> </span><span class="cx"> class Date(TypeEngine) </span><span class="lines">@@ -38,11 +39,13 @@ </span><span class="cx"> # as bind params, raw bytes to unicode as </span><span class="cx"> # rowset values, using the unicode encoding </span><span class="cx"> # setting on the engine (defaults to 'utf-8') </span><del>- class Unicode(String) </del><ins>+ class Unicode(TypeDecorator): + impl = String </ins><span class="cx"> </span><span class="cx"> # uses the pickle protocol to serialize data </span><span class="cx"> # in/out of Binary columns </span><del>- class PickleType(Binary) </del><ins>+ class PickleType(TypeDecorator): + impl = Binary </ins><span class="cx"> </span><span class="cx"> More specific subclasses of these types are available, which various database engines may choose to implement specifically, allowing finer grained control over types: </span><span class="cx"> </span><span class="lines">@@ -76,53 +79,25 @@ </span><span class="cx"> </span><span class="cx"> ### Creating your Own Types {@name=custom} </span><span class="cx"> </span><del>-User-defined types can be created, to support either database-specific types, or customized pre-processing of query parameters as well as post-processing of result set data. You can make your own classes to perform these operations. They are specified by subclassing the desired type class: </del><ins>+User-defined types can be created, to support either database-specific types, or customized pre-processing of query parameters as well as post-processing of result set data. You can make your own classes to perform these operations. To augment the behavior of a `TypeEngine` type, such as `String`, the `TypeDecorator` class is used: </ins><span class="cx"> </span><del>- {python title="Basic Example"} </del><ins>+ {python} </ins><span class="cx"> import sqlalchemy.types as types </span><span class="cx"> </span><del>- class MyType(types.String): </del><ins>+ class MyType(types.TypeDecorator): </ins><span class="cx"> """basic type that decorates String, prefixes values with "PREFIX:" on </span><span class="cx"> the way in and strips it off on the way out.""" </span><ins>+ impl = types.String </ins><span class="cx"> def convert_bind_param(self, value, engine): </span><span class="cx"> return "PREFIX:" + value </span><span class="cx"> def convert_result_value(self, value, engine): </span><span class="cx"> return value[7:] </span><ins>+ +The `Unicode` and `PickleType` classes are instances of `TypeDecorator` already and can be subclassed directly. </ins><span class="cx"> </span><del>-A common desire is for a "pickle" type, which overrides a Binary object to provide pickling behavior: </del><ins>+To build a type object from scratch, which will not have a corresponding database-specific implementation, subclass `TypeEngine`: </ins><span class="cx"> </span><del>- {python title="Pickle Type"} - import cPickle - - class PickleType(Binary): - def __init__(self, protocol=pickle.HIGHEST_PROTOCOL): - """allows the pickle protocol to be specified""" - self.protocol = protocol - def convert_result_value(self, value, engine): - if value is None: - return None - buf = Binary.convert_result_value(self, value, engine) - return pickle.loads(str(buf)) - def convert_bind_param(self, value, engine): - if value is None: - return None - return Binary.convert_bind_param(self, pickle.dumps(value, self.protocol), engine) - def get_constructor_args(self): - return {} - -Which can be used like: - </del><span class="cx"> {python} </span><del>- mytable = Table('mytable', engine, - Column('id', Integer, primary_key=True), - Column('data', PickleType())) - - my_object = MyObject() - mytable.insert().execute(data=my_object) - -Another example, which illustrates a fully defined datatype. This just overrides the base type class TypeEngine: - - {python} </del><span class="cx"> import sqlalchemy.types as types </span><span class="cx"> </span><span class="cx"> class MyType(types.TypeEngine): </span><span class="lines">@@ -134,7 +109,4 @@ </span><span class="cx"> return value </span><span class="cx"> def convert_result_value(self, value, engine): </span><span class="cx"> return value </span><del>- def adapt_args(self): - """allows for the adaptation of this TypeEngine object into a new kind of type depending on its arguments.""" - return self </del><span class="cx"> </span></span></pre></div> <a id="sqlalchemybranchesschemalibsqlalchemyexceptionspy"></a> <div class="modfile"><h4>Modified: sqlalchemy/branches/schema/lib/sqlalchemy/exceptions.py (1410 => 1411)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/schema/lib/sqlalchemy/exceptions.py 2006-05-05 17:52:32 UTC (rev 1410) +++ sqlalchemy/branches/schema/lib/sqlalchemy/exceptions.py 2006-05-06 00:54:41 UTC (rev 1411) </span><span class="lines">@@ -25,8 +25,8 @@ </span><span class="cx"> objects. This error generally corresponds to construction time state errors.""" </span><span class="cx"> pass </span><span class="cx"> </span><del>-class CommitError(SQLAlchemyError): - """raised when an invalid condition is detected upon a commit()""" </del><ins>+class FlushError(SQLAlchemyError): + """raised when an invalid condition is detected upon a flush()""" </ins><span class="cx"> pass </span><span class="cx"> </span><span class="cx"> class InvalidRequestError(SQLAlchemyError): </span></span></pre></div> <a id="sqlalchemybranchesschemalibsqlalchemymodsthreadlocalpy"></a> <div class="modfile"><h4>Modified: sqlalchemy/branches/schema/lib/sqlalchemy/mods/threadlocal.py (1410 => 1411)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/schema/lib/sqlalchemy/mods/threadlocal.py 2006-05-05 17:52:32 UTC (rev 1410) +++ sqlalchemy/branches/schema/lib/sqlalchemy/mods/threadlocal.py 2006-05-06 00:54:41 UTC (rev 1411) </span><span class="lines">@@ -1,5 +1,5 @@ </span><span class="cx"> from sqlalchemy import util, engine, mapper </span><del>-from sqlalchemy.orm import unitofwork, session </del><ins>+from sqlalchemy.orm import session, current_session </ins><span class="cx"> import sqlalchemy </span><span class="cx"> import sys, types </span><span class="cx"> </span><span class="lines">@@ -10,107 +10,44 @@ </span><span class="cx"> from the pool. this greatly helps functions that call multiple statements to be able to easily use just one connection </span><span class="cx"> without explicit "close" statements on result handles. </span><span class="cx"> </span><del>-on the Session side, the get_session() method will be modified to return a thread-local Session when no arguments </del><ins>+on the Session side, the current_session() method will be modified to return a thread-local Session when no arguments </ins><span class="cx"> are sent. It will also install module-level methods within the objectstore module, such as flush(), delete(), etc. </span><del>-which call this method on the thread-local session returned by get_session(). </del><ins>+which call this method on the thread-local session returned by current_session(). </ins><span class="cx"> </span><del>-Without this plugin in use, all statement.execute() calls must be matched by a corresponding close() statement -on the returned result (or the result must be consumed completely). Also, all mapper operations must use -explicit Session objects when creating instances and creating queries. </del><ins>+ </ins><span class="cx"> """ </span><span class="cx"> </span><del>-get_session = session.get_session - </del><span class="cx"> class Objectstore(object): </span><del>- def begin(self, obj): - return get_session().begin(obj) - def commit(self, obj): - return get_session().commit(obj) - def get_session(self, obj=None): - return get_session(obj=obj) - def flush(self, obj=None): - """flushes the current UnitOfWork transaction. if a transaction was begun - via begin(), flushes only those objects that were created, modified, or deleted - since that begin statement. otherwise flushes all objects that have been - changed. </del><ins>+ def __getattr__(self, key): + return getattr(current_session(), key) + def get_session(self): + return current_session() + +def monkeypatch_query_method(class_, name): + def do(self, *args, **kwargs): + query = class_.mapper.query() + getattr(query, name)(*args, **kwargs) + setattr(class_, name, classmethod(do)) </ins><span class="cx"> </span><del>- if individual objects are submitted, then only those objects are committed, and the - begin/commit cycle is not affected.""" - get_session().flush(obj) - - def clear(self): - """removes all current UnitOfWorks and IdentityMaps for this thread and - establishes a new one. It is probably a good idea to discard all - current mapped object instances, as they are no longer in the Identity Map.""" - get_session().clear() - - def refresh(self, obj): - """reloads the state of this object from the database, and cancels any in-memory - changes.""" - get_session().refresh(obj) - - def expire(self, obj): - """invalidates the data in the given objects and sets them to refresh themselves - the next time they are requested.""" - get_session().expire(obj) - - def expunge(self, obj): - get_session().expunge(obj) - - def delete(self, obj): - """registers the given objects as to be deleted upon the next commit""" - s = get_session().delete(obj) - - def has_key(self, key): - """returns True if the current thread-local IdentityMap contains the given instance key""" - return get_session().has_key(key) - - def has_instance(self, instance): - """returns True if the current thread-local IdentityMap contains the given instance""" - return get_session().has_instance(instance) - - def is_dirty(self, obj): - """returns True if the given object is in the current UnitOfWork's new or dirty list, - or if its a modified list attribute on an object.""" - return get_session().is_dirty(obj) - - def instance_key(self, instance): - """returns the IdentityMap key for the given instance""" - return get_session().instance_key(instance) - - def import_instance(self, instance): - return get_session().import_instance(instance) - -def assign_mapper(class_, *args, **params): - params.setdefault("is_primary", True) </del><ins>+def monkeypatch_objectstore_method(class_, name): + def do(self, *args, **kwargs): + session = current_session() + getattr(session, name)(self, *args, **kwargs) + setattr(class_, name, do) + +def assign_mapper(class_, *args, **kwargs): + kwargs.setdefault("is_primary", True) </ins><span class="cx"> if not isinstance(getattr(class_, '__init__'), types.MethodType): </span><span class="cx"> def __init__(self, **kwargs): </span><span class="cx"> for key, value in kwargs.items(): </span><span class="cx"> setattr(self, key, value) </span><span class="cx"> class_.__init__ = __init__ </span><del>- m = mapper(class_, *args, **params) </del><ins>+ m = mapper(class_, *args, **kwargs) </ins><span class="cx"> class_.mapper = m </span><del>- # TODO: get these outta here, have to go off explicit session - class_.get = m.get - class_.select = m.select - class_.select_by = m.select_by - class_.selectone = m.selectone - class_.get_by = m.get_by - def commit(self): - sqlalchemy.objectstore.commit(self) - def delete(self): - sqlalchemy.objectstore.delete(self) - def expire(self): - sqlalchemy.objectstore.expire(self) - def refresh(self): - sqlalchemy.objectstore.refresh(self) - def expunge(self): - sqlalchemy.objectstore.expunge(self) - class_.commit = commit - class_.delete = delete - class_.expire = expire - class_.refresh = refresh - class_.expunge = expunge </del><ins>+ for name in ['get', 'select', 'select_by', 'selectone', 'get_by']: + monkeypatch_query_method(class_, name) + for name in ['flush', 'delete', 'expire', 'refresh', 'expunge', 'merge', 'update', 'save_or_update']: + monkeypatch_objectstore_method(class_, name) </ins><span class="cx"> </span><span class="cx"> def install_plugin(): </span><span class="cx"> reg = util.ScopedRegistry(session.Session) </span></span></pre></div> <a id="sqlalchemybranchesschemalibsqlalchemyorm__init__py"></a> <div class="modfile"><h4>Modified: sqlalchemy/branches/schema/lib/sqlalchemy/orm/__init__.py (1410 => 1411)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/schema/lib/sqlalchemy/orm/__init__.py 2006-05-05 17:52:32 UTC (rev 1410) +++ sqlalchemy/branches/schema/lib/sqlalchemy/orm/__init__.py 2006-05-06 00:54:41 UTC (rev 1411) </span><span class="lines">@@ -11,14 +11,15 @@ </span><span class="cx"> from sqlalchemy import sql, schema, engine, util, exceptions </span><span class="cx"> from mapper import * </span><span class="cx"> from mapper import mapper_registry </span><ins>+from query import Query </ins><span class="cx"> from util import polymorphic_union </span><span class="cx"> import properties </span><span class="cx"> from session import current_session </span><span class="cx"> from session import Session as create_session </span><span class="cx"> </span><span class="cx"> __all__ = ['relation', 'backref', 'eagerload', 'lazyload', 'noload', 'deferred', 'defer', 'undefer', </span><del>- 'mapper', 'clear_mappers', 'sql', 'extension', 'class_mapper', 'object_mapper', 'MapperExtension', - 'cascade_mappers', 'polymorphic_union', 'current_session', 'create_session', 'class_mapper', 'object_mapper' </del><ins>+ 'mapper', 'clear_mappers', 'sql', 'extension', 'class_mapper', 'object_mapper', 'MapperExtension', 'Query', + 'cascade_mappers', 'polymorphic_union', 'current_session', 'create_session', 'class_mapper', 'object_mapper' </ins><span class="cx"> ] </span><span class="cx"> </span><span class="cx"> def relation(*args, **kwargs): </span></span></pre></div> <a id="sqlalchemybranchesschemalibsqlalchemyormdependencypy"></a> <div class="modfile"><h4>Modified: sqlalchemy/branches/schema/lib/sqlalchemy/orm/dependency.py (1410 => 1411)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/schema/lib/sqlalchemy/orm/dependency.py 2006-05-05 17:52:32 UTC (rev 1410) +++ sqlalchemy/branches/schema/lib/sqlalchemy/orm/dependency.py 2006-05-06 00:54:41 UTC (rev 1411) </span><span class="lines">@@ -1,3 +1,10 @@ </span><ins>+# orm/dependency.py +# Copyright (C) 2005,2006 Michael Bayer mi...@zz... +# +# This module is part of SQLAlchemy and is released under +# the MIT License: http://www.opensource.org/licenses/mit-license.php + + </ins><span class="cx"> """bridges the PropertyLoader (i.e. a relation()) and the UOWTransaction </span><span class="cx"> together to allow processing of scalar- and list-based dependencies at flush time.""" </span><span class="cx"> </span><span class="lines">@@ -20,7 +27,7 @@ </span><span class="cx"> </span><span class="cx"> class MapperStub(object): </span><span class="cx"> """poses as a Mapper representing the association table in a many-to-many </span><del>- join, when performing a commit(). </del><ins>+ join, when performing a flush(). </ins><span class="cx"> </span><span class="cx"> The Task objects in the objectstore module treat it just like </span><span class="cx"> any other Mapper, but in fact it only serves as a "dependency" placeholder </span><span class="lines">@@ -97,11 +104,9 @@ </span><span class="cx"> else: </span><span class="cx"> raise AssertionError(" no foreign key ?") </span><span class="cx"> </span><del>- # TODO: this method should be moved to an external object </del><span class="cx"> def get_object_dependencies(self, obj, uowcommit, passive = True): </span><span class="cx"> return uowcommit.uow.attributes.get_history(obj, self.key, passive = passive) </span><span class="cx"> </span><del>- # TODO: this method should be moved to an external object </del><span class="cx"> def whose_dependent_on_who(self, obj1, obj2): </span><span class="cx"> """given an object pair assuming obj2 is a child of obj1, returns a tuple </span><span class="cx"> with the dependent object second, or None if they are equal. </span><span class="lines">@@ -114,7 +119,6 @@ </span><span class="cx"> else: </span><span class="cx"> return (obj2, obj1) </span><span class="cx"> </span><del>- # TODO: this method should be moved to an external object </del><span class="cx"> def process_dependencies(self, task, deplist, uowcommit, delete = False): </span><span class="cx"> """this method is called during a commit operation to synchronize data between a parent and child object. </span><span class="cx"> it also can establish child or parent objects within the unit of work as "to be saved" or "deleted" </span><span class="lines">@@ -248,7 +252,6 @@ </span><span class="cx"> elif childlist.hasparent(child) is False: </span><span class="cx"> uowcommit.register_object(child, isdelete=True) </span><span class="cx"> </span><del>- # TODO: this method should be moved to an external object </del><span class="cx"> def _synchronize(self, obj, child, associationrow, clearkeys): </span><span class="cx"> """called during a commit to execute the full list of syncrules on the </span><span class="cx"> given object/child/optional association row""" </span></span></pre></div> <a id="sqlalchemybranchesschemalibsqlalchemyormmapperpy"></a> <div class="modfile"><h4>Modified: sqlalchemy/branches/schema/lib/sqlalchemy/orm/mapper.py (1410 => 1411)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/schema/lib/sqlalchemy/orm/mapper.py 2006-05-05 17:52:32 UTC (rev 1410) +++ sqlalchemy/branches/schema/lib/sqlalchemy/orm/mapper.py 2006-05-06 00:54:41 UTC (rev 1411) </span><span class="lines">@@ -1,4 +1,4 @@ </span><del>-# mapper/mapper.py </del><ins>+# orm/mapper.py </ins><span class="cx"> # Copyright (C) 2005,2006 Michael Bayer mi...@zz... </span><span class="cx"> # </span><span class="cx"> # This module is part of SQLAlchemy and is released under </span><span class="lines">@@ -660,7 +660,7 @@ </span><span class="cx"> self.extension.after_update(self, connection, obj) </span><span class="cx"> rows += c.cursor.rowcount </span><span class="cx"> if c.supports_sane_rowcount() and rows != len(update): </span><del>- raise CommitError("ConcurrencyError - updated rowcount %d does not match number of objects updated %d" % (rows, len(update))) </del><ins>+ raise exceptions.FlushError("ConcurrencyError - updated rowcount %d does not match number of objects updated %d" % (rows, len(update))) </ins><span class="cx"> if len(insert): </span><span class="cx"> statement = table.insert() </span><span class="cx"> for rec in insert: </span><span class="lines">@@ -729,10 +729,9 @@ </span><span class="cx"> if self.version_id_col is not None: </span><span class="cx"> clause.clauses.append(self.version_id_col == sql.bindparam(self.version_id_col.key, type=self.version_id_col.type)) </span><span class="cx"> statement = table.delete(clause) </span><del>- print "DELETE IS", delete </del><span class="cx"> c = connection.execute(statement, delete) </span><span class="cx"> if c.supports_sane_rowcount() and c.rowcount != len(delete): </span><del>- raise CommitError("ConcurrencyError - updated rowcount %d does not match number of objects updated %d" % (c.cursor.rowcount, len(delete))) </del><ins>+ raise exceptions.FlushError("ConcurrencyError - updated rowcount %d does not match number of objects updated %d" % (c.cursor.rowcount, len(delete))) </ins><span class="cx"> </span><span class="cx"> def _has_pks(self, table): </span><span class="cx"> try: </span></span></pre></div> <a id="sqlalchemybranchesschemalibsqlalchemyormquerypy"></a> <div class="modfile"><h4>Modified: sqlalchemy/branches/schema/lib/sqlalchemy/orm/query.py (1410 => 1411)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/schema/lib/sqlalchemy/orm/query.py 2006-05-05 17:52:32 UTC (rev 1410) +++ sqlalchemy/branches/schema/lib/sqlalchemy/orm/query.py 2006-05-06 00:54:41 UTC (rev 1411) </span><span class="lines">@@ -1,4 +1,4 @@ </span><del>-# mapper/query.py </del><ins>+# orm/query.py </ins><span class="cx"> # Copyright (C) 2005,2006 Michael Bayer mi...@zz... </span><span class="cx"> # </span><span class="cx"> # This module is part of SQLAlchemy and is released under </span><span class="lines">@@ -11,8 +11,11 @@ </span><span class="cx"> </span><span class="cx"> class Query(object): </span><span class="cx"> """encapsulates the object-fetching operations provided by Mappers.""&quo... [truncated message content] |
From: <co...@sq...> - 2006-05-05 17:52:42
|
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head><style type="text/css"><!-- #msg dl { border: 1px #006 solid; background: #369; padding: 6px; color: #fff; } #msg dt { float: left; width: 6em; font-weight: bold; } #msg dt:after { content:':';} #msg dl, #msg dt, #msg ul, #msg li { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; } #msg dl a { font-weight: bold} #msg dl a:link { color:#fc3; } #msg dl a:active { color:#ff0; } #msg dl a:visited { color:#cc6; } h3 { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; font-weight: bold; } #msg pre { overflow: auto; background: #ffc; border: 1px #fc0 solid; padding: 6px; } #msg ul, pre { overflow: auto; } #patch { width: 100%; } #patch h4 {font-family: verdana,arial,helvetica,sans-serif;font-size:10pt;padding:8px;background:#369;color:#fff;margin:0;} #patch .propset h4, #patch .binary h4 {margin:0;} #patch pre {padding:0;line-height:1.2em;margin:0;} #patch .diff {width:100%;background:#eee;padding: 0 0 10px 0;overflow:auto;} #patch .propset .diff, #patch .binary .diff {padding:10px 0;} #patch span {display:block;padding:0 10px;} #patch .modfile, #patch .addfile, #patch .delfile, #patch .propset, #patch .binary, #patch .copfile {border:1px solid #ccc;margin:10px 0;} #patch ins {background:#dfd;text-decoration:none;display:block;padding:0 10px;} #patch del {background:#fdd;text-decoration:none;display:block;padding:0 10px;} #patch .lines, .info {color:#888;background:#fff;} --></style> <title>[1410] sqlalchemy/tags/rel_0_1_7/test/engine.py: corrercted removed file</title> </head> <body> <div id="msg"> <dl> <dt>Revision</dt> <dd>1410</dd> <dt>Author</dt> <dd>zzzeek</dd> <dt>Date</dt> <dd>2006-05-05 12:52:32 -0500 (Fri, 05 May 2006)</dd> </dl> <h3>Log Message</h3> <pre>corrercted removed file</pre> <h3>Added Paths</h3> <ul> <li><a href="#sqlalchemytagsrel_0_1_7testenginepy">sqlalchemy/tags/rel_0_1_7/test/engine.py</a></li> </ul> </div> <div id="patch"> <h3>Diff</h3> <a id="sqlalchemytagsrel_0_1_7testenginepy"></a> <div class="addfile"><h4>Added: sqlalchemy/tags/rel_0_1_7/test/engine.py (1409 => 1410)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/tags/rel_0_1_7/test/engine.py 2006-05-05 17:52:00 UTC (rev 1409) +++ sqlalchemy/tags/rel_0_1_7/test/engine.py 2006-05-05 17:52:32 UTC (rev 1410) </span><span class="lines">@@ -0,0 +1,64 @@ </span><ins>+from sqlalchemy import * + +from testbase import PersistTest +import testbase +import unittest, re +import tables + +class TransactionTest(PersistTest): + def setUpAll(self): + tables.create() + def tearDownAll(self): + tables.drop() + def tearDown(self): + tables.delete() + + def testbasic(self): + testbase.db.begin() + tables.users.insert().execute(user_name='jack') + tables.users.insert().execute(user_name='fred') + testbase.db.commit() + l = tables.users.select().execute().fetchall() + print l + self.assert_(len(l) == 2) + + def testrollback(self): + testbase.db.begin() + tables.users.insert().execute(user_name='jack') + tables.users.insert().execute(user_name='fred') + testbase.db.rollback() + l = tables.users.select().execute().fetchall() + print l + self.assert_(len(l) == 0) + + @testbase.unsupported('sqlite') + def testnested(self): + """tests nested sessions. SQLite should raise an error.""" + testbase.db.begin() + tables.users.insert().execute(user_name='jack') + tables.users.insert().execute(user_name='fred') + testbase.db.push_session() + tables.users.insert().execute(user_name='ed') + tables.users.insert().execute(user_name='wendy') + testbase.db.pop_session() + testbase.db.rollback() + l = tables.users.select().execute().fetchall() + print l + self.assert_(len(l) == 2) + + def testtwo(self): + testbase.db.begin() + tables.users.insert().execute(user_name='jack') + tables.users.insert().execute(user_name='fred') + testbase.db.commit() + testbase.db.begin() + tables.users.insert().execute(user_name='ed') + tables.users.insert().execute(user_name='wendy') + testbase.db.commit() + testbase.db.rollback() + l = tables.users.select().execute().fetchall() + print l + self.assert_(len(l) == 4) + +if __name__ == "__main__": + testbase.main() </ins></span></pre> </div> </div> </body> </html> |
From: <co...@sq...> - 2006-05-05 17:52:14
|
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head><style type="text/css"><!-- #msg dl { border: 1px #006 solid; background: #369; padding: 6px; color: #fff; } #msg dt { float: left; width: 6em; font-weight: bold; } #msg dt:after { content:':';} #msg dl, #msg dt, #msg ul, #msg li { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; } #msg dl a { font-weight: bold} #msg dl a:link { color:#fc3; } #msg dl a:active { color:#ff0; } #msg dl a:visited { color:#cc6; } h3 { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; font-weight: bold; } #msg pre { overflow: auto; background: #ffc; border: 1px #fc0 solid; padding: 6px; } #msg ul, pre { overflow: auto; } #patch { width: 100%; } #patch h4 {font-family: verdana,arial,helvetica,sans-serif;font-size:10pt;padding:8px;background:#369;color:#fff;margin:0;} #patch .propset h4, #patch .binary h4 {margin:0;} #patch pre {padding:0;line-height:1.2em;margin:0;} #patch .diff {width:100%;background:#eee;padding: 0 0 10px 0;overflow:auto;} #patch .propset .diff, #patch .binary .diff {padding:10px 0;} #patch span {display:block;padding:0 10px;} #patch .modfile, #patch .addfile, #patch .delfile, #patch .propset, #patch .binary, #patch .copfile {border:1px solid #ccc;margin:10px 0;} #patch ins {background:#dfd;text-decoration:none;display:block;padding:0 10px;} #patch del {background:#fdd;text-decoration:none;display:block;padding:0 10px;} #patch .lines, .info {color:#888;background:#fff;} --></style> <title>[1409] sqlalchemy/trunk/test/engine.py: got removed somehow</title> </head> <body> <div id="msg"> <dl> <dt>Revision</dt> <dd>1409</dd> <dt>Author</dt> <dd>zzzeek</dd> <dt>Date</dt> <dd>2006-05-05 12:52:00 -0500 (Fri, 05 May 2006)</dd> </dl> <h3>Log Message</h3> <pre>got removed somehow</pre> <h3>Added Paths</h3> <ul> <li><a href="#sqlalchemytrunktestenginepy">sqlalchemy/trunk/test/engine.py</a></li> </ul> </div> <div id="patch"> <h3>Diff</h3> <a id="sqlalchemytrunktestenginepy"></a> <div class="addfile"><h4>Added: sqlalchemy/trunk/test/engine.py (1408 => 1409)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/trunk/test/engine.py 2006-05-05 17:45:48 UTC (rev 1408) +++ sqlalchemy/trunk/test/engine.py 2006-05-05 17:52:00 UTC (rev 1409) </span><span class="lines">@@ -0,0 +1,64 @@ </span><ins>+from sqlalchemy import * + +from testbase import PersistTest +import testbase +import unittest, re +import tables + +class TransactionTest(PersistTest): + def setUpAll(self): + tables.create() + def tearDownAll(self): + tables.drop() + def tearDown(self): + tables.delete() + + def testbasic(self): + testbase.db.begin() + tables.users.insert().execute(user_name='jack') + tables.users.insert().execute(user_name='fred') + testbase.db.commit() + l = tables.users.select().execute().fetchall() + print l + self.assert_(len(l) == 2) + + def testrollback(self): + testbase.db.begin() + tables.users.insert().execute(user_name='jack') + tables.users.insert().execute(user_name='fred') + testbase.db.rollback() + l = tables.users.select().execute().fetchall() + print l + self.assert_(len(l) == 0) + + @testbase.unsupported('sqlite') + def testnested(self): + """tests nested sessions. SQLite should raise an error.""" + testbase.db.begin() + tables.users.insert().execute(user_name='jack') + tables.users.insert().execute(user_name='fred') + testbase.db.push_session() + tables.users.insert().execute(user_name='ed') + tables.users.insert().execute(user_name='wendy') + testbase.db.pop_session() + testbase.db.rollback() + l = tables.users.select().execute().fetchall() + print l + self.assert_(len(l) == 2) + + def testtwo(self): + testbase.db.begin() + tables.users.insert().execute(user_name='jack') + tables.users.insert().execute(user_name='fred') + testbase.db.commit() + testbase.db.begin() + tables.users.insert().execute(user_name='ed') + tables.users.insert().execute(user_name='wendy') + testbase.db.commit() + testbase.db.rollback() + l = tables.users.select().execute().fetchall() + print l + self.assert_(len(l) == 4) + +if __name__ == "__main__": + testbase.main() </ins></span></pre> </div> </div> </body> </html> |
From: <co...@sq...> - 2006-05-05 17:46:00
|
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head><style type="text/css"><!-- #msg dl { border: 1px #006 solid; background: #369; padding: 6px; color: #fff; } #msg dt { float: left; width: 6em; font-weight: bold; } #msg dt:after { content:':';} #msg dl, #msg dt, #msg ul, #msg li { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; } #msg dl a { font-weight: bold} #msg dl a:link { color:#fc3; } #msg dl a:active { color:#ff0; } #msg dl a:visited { color:#cc6; } h3 { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; font-weight: bold; } #msg pre { overflow: auto; background: #ffc; border: 1px #fc0 solid; padding: 6px; } #msg ul, pre { overflow: auto; } #patch { width: 100%; } #patch h4 {font-family: verdana,arial,helvetica,sans-serif;font-size:10pt;padding:8px;background:#369;color:#fff;margin:0;} #patch .propset h4, #patch .binary h4 {margin:0;} #patch pre {padding:0;line-height:1.2em;margin:0;} #patch .diff {width:100%;background:#eee;padding: 0 0 10px 0;overflow:auto;} #patch .propset .diff, #patch .binary .diff {padding:10px 0;} #patch span {display:block;padding:0 10px;} #patch .modfile, #patch .addfile, #patch .delfile, #patch .propset, #patch .binary, #patch .copfile {border:1px solid #ccc;margin:10px 0;} #patch ins {background:#dfd;text-decoration:none;display:block;padding:0 10px;} #patch del {background:#fdd;text-decoration:none;display:block;padding:0 10px;} #patch .lines, .info {color:#888;background:#fff;} --></style> <title>[1408] sqlalchemy/tags/rel_0_1_7/: 0.1.7</title> </head> <body> <div id="msg"> <dl> <dt>Revision</dt> <dd>1408</dd> <dt>Author</dt> <dd>zzzeek</dd> <dt>Date</dt> <dd>2006-05-05 12:45:48 -0500 (Fri, 05 May 2006)</dd> </dl> <h3>Log Message</h3> <pre>0.1.7</pre> <h3>Added Paths</h3> <ul> <li>sqlalchemy/tags/rel_0_1_7/</li> </ul> </div> <div id="patch"> <h3>Diff</h3> <a id="sqlalchemytagsrel_0_1_7fromrev1407sqlalchemytrunk"></a> <div class="copfile"><h4>Copied: sqlalchemy/tags/rel_0_1_7 (from rev 1407, sqlalchemy/trunk) ( => )</h4> <pre class="diff"><span> <span class="info"> </span></span></pre> </div> </div> </body> </html> |
From: <co...@sq...> - 2006-05-05 17:45:19
|
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head><style type="text/css"><!-- #msg dl { border: 1px #006 solid; background: #369; padding: 6px; color: #fff; } #msg dt { float: left; width: 6em; font-weight: bold; } #msg dt:after { content:':';} #msg dl, #msg dt, #msg ul, #msg li { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; } #msg dl a { font-weight: bold} #msg dl a:link { color:#fc3; } #msg dl a:active { color:#ff0; } #msg dl a:visited { color:#cc6; } h3 { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; font-weight: bold; } #msg pre { overflow: auto; background: #ffc; border: 1px #fc0 solid; padding: 6px; } #msg ul, pre { overflow: auto; } #patch { width: 100%; } #patch h4 {font-family: verdana,arial,helvetica,sans-serif;font-size:10pt;padding:8px;background:#369;color:#fff;margin:0;} #patch .propset h4, #patch .binary h4 {margin:0;} #patch pre {padding:0;line-height:1.2em;margin:0;} #patch .diff {width:100%;background:#eee;padding: 0 0 10px 0;overflow:auto;} #patch .propset .diff, #patch .binary .diff {padding:10px 0;} #patch span {display:block;padding:0 10px;} #patch .modfile, #patch .addfile, #patch .delfile, #patch .propset, #patch .binary, #patch .copfile {border:1px solid #ccc;margin:10px 0;} #patch ins {background:#dfd;text-decoration:none;display:block;padding:0 10px;} #patch del {background:#fdd;text-decoration:none;display:block;padding:0 10px;} #patch .lines, .info {color:#888;background:#fff;} --></style> <title>[1407] sqlalchemy/trunk: added __mod__ type</title> </head> <body> <div id="msg"> <dl> <dt>Revision</dt> <dd>1407</dd> <dt>Author</dt> <dd>zzzeek</dd> <dt>Date</dt> <dd>2006-05-05 12:45:06 -0500 (Fri, 05 May 2006)</dd> </dl> <h3>Log Message</h3> <pre>added __mod__ type</pre> <h3>Modified Paths</h3> <ul> <li><a href="#sqlalchemytrunkCHANGES">sqlalchemy/trunk/CHANGES</a></li> <li><a href="#sqlalchemytrunklibsqlalchemysqlpy">sqlalchemy/trunk/lib/sqlalchemy/sql.py</a></li> <li><a href="#sqlalchemytrunklibsqlalchemytypespy">sqlalchemy/trunk/lib/sqlalchemy/types.py</a></li> </ul> </div> <div id="patch"> <h3>Diff</h3> <a id="sqlalchemytrunkCHANGES"></a> <div class="modfile"><h4>Modified: sqlalchemy/trunk/CHANGES (1406 => 1407)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/trunk/CHANGES 2006-05-05 17:31:33 UTC (rev 1406) +++ sqlalchemy/trunk/CHANGES 2006-05-05 17:45:06 UTC (rev 1407) </span><span class="lines">@@ -1,6 +1,7 @@ </span><span class="cx"> 0.1.7 </span><span class="cx"> - some fixes to topological sort algorithm </span><span class="cx"> - added DISTINCT ON support to Postgres (just supply distinct=[col1,col2..]) </span><ins>+- added __mod__ (% operator) to sql expressions </ins><span class="cx"> - "order_by" mapper property inherited from inheriting mapper </span><span class="cx"> - fix to column type used when mapper UPDATES/DELETEs </span><span class="cx"> - with convert_unicode=True, reflection was failing, has been fixed </span></span></pre></div> <a id="sqlalchemytrunklibsqlalchemysqlpy"></a> <div class="modfile"><h4>Modified: sqlalchemy/trunk/lib/sqlalchemy/sql.py (1406 => 1407)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/trunk/lib/sqlalchemy/sql.py 2006-05-05 17:31:33 UTC (rev 1406) +++ sqlalchemy/trunk/lib/sqlalchemy/sql.py 2006-05-05 17:45:06 UTC (rev 1407) </span><span class="lines">@@ -531,6 +531,8 @@ </span><span class="cx"> return self._operate('*', other) </span><span class="cx"> def __div__(self, other): </span><span class="cx"> return self._operate('/', other) </span><ins>+ def __mod__(self, other): + return self._operate('%', other) </ins><span class="cx"> def __truediv__(self, other): </span><span class="cx"> return self._operate('/', other) </span><span class="cx"> def _bind_param(self, obj): </span></span></pre></div> <a id="sqlalchemytrunklibsqlalchemytypespy"></a> <div class="modfile"><h4>Modified: sqlalchemy/trunk/lib/sqlalchemy/types.py (1406 => 1407)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/trunk/lib/sqlalchemy/types.py 2006-05-05 17:31:33 UTC (rev 1406) +++ sqlalchemy/trunk/lib/sqlalchemy/types.py 2006-05-05 17:45:06 UTC (rev 1407) </span><span class="lines">@@ -33,7 +33,7 @@ </span><span class="cx"> def engine_impl(self, engine): </span><span class="cx"> try: </span><span class="cx"> return self.impl_dict[engine] </span><del>- except: </del><ins>+ except KeyError: </ins><span class="cx"> return self.impl_dict.setdefault(engine, engine.type_descriptor(self)) </span><span class="cx"> def get_col_spec(self): </span><span class="cx"> raise NotImplementedError() </span></span></pre> </div> </div> </body> </html> |
From: <co...@sq...> - 2006-05-05 17:31:44
|
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head><style type="text/css"><!-- #msg dl { border: 1px #006 solid; background: #369; padding: 6px; color: #fff; } #msg dt { float: left; width: 6em; font-weight: bold; } #msg dt:after { content:':';} #msg dl, #msg dt, #msg ul, #msg li { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; } #msg dl a { font-weight: bold} #msg dl a:link { color:#fc3; } #msg dl a:active { color:#ff0; } #msg dl a:visited { color:#cc6; } h3 { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; font-weight: bold; } #msg pre { overflow: auto; background: #ffc; border: 1px #fc0 solid; padding: 6px; } #msg ul, pre { overflow: auto; } #patch { width: 100%; } #patch h4 {font-family: verdana,arial,helvetica,sans-serif;font-size:10pt;padding:8px;background:#369;color:#fff;margin:0;} #patch .propset h4, #patch .binary h4 {margin:0;} #patch pre {padding:0;line-height:1.2em;margin:0;} #patch .diff {width:100%;background:#eee;padding: 0 0 10px 0;overflow:auto;} #patch .propset .diff, #patch .binary .diff {padding:10px 0;} #patch span {display:block;padding:0 10px;} #patch .modfile, #patch .addfile, #patch .delfile, #patch .propset, #patch .binary, #patch .copfile {border:1px solid #ccc;margin:10px 0;} #patch ins {background:#dfd;text-decoration:none;display:block;padding:0 10px;} #patch del {background:#fdd;text-decoration:none;display:block;padding:0 10px;} #patch .lines, .info {color:#888;background:#fff;} --></style> <title>[1406] sqlalchemy/tags/rel_0_1_7/: spoke too soon</title> </head> <body> <div id="msg"> <dl> <dt>Revision</dt> <dd>1406</dd> <dt>Author</dt> <dd>zzzeek</dd> <dt>Date</dt> <dd>2006-05-05 12:31:33 -0500 (Fri, 05 May 2006)</dd> </dl> <h3>Log Message</h3> <pre>spoke too soon</pre> <h3>Removed Paths</h3> <ul> <li>sqlalchemy/tags/rel_0_1_7/</li> </ul> </div> <div id="patch"> <h3>Diff</h3> </div> </body> </html> |
From: <co...@sq...> - 2006-05-05 17:26:13
|
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head><style type="text/css"><!-- #msg dl { border: 1px #006 solid; background: #369; padding: 6px; color: #fff; } #msg dt { float: left; width: 6em; font-weight: bold; } #msg dt:after { content:':';} #msg dl, #msg dt, #msg ul, #msg li { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; } #msg dl a { font-weight: bold} #msg dl a:link { color:#fc3; } #msg dl a:active { color:#ff0; } #msg dl a:visited { color:#cc6; } h3 { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; font-weight: bold; } #msg pre { overflow: auto; background: #ffc; border: 1px #fc0 solid; padding: 6px; } #msg ul, pre { overflow: auto; } #patch { width: 100%; } #patch h4 {font-family: verdana,arial,helvetica,sans-serif;font-size:10pt;padding:8px;background:#369;color:#fff;margin:0;} #patch .propset h4, #patch .binary h4 {margin:0;} #patch pre {padding:0;line-height:1.2em;margin:0;} #patch .diff {width:100%;background:#eee;padding: 0 0 10px 0;overflow:auto;} #patch .propset .diff, #patch .binary .diff {padding:10px 0;} #patch span {display:block;padding:0 10px;} #patch .modfile, #patch .addfile, #patch .delfile, #patch .propset, #patch .binary, #patch .copfile {border:1px solid #ccc;margin:10px 0;} #patch ins {background:#dfd;text-decoration:none;display:block;padding:0 10px;} #patch del {background:#fdd;text-decoration:none;display:block;padding:0 10px;} #patch .lines, .info {color:#888;background:#fff;} --></style> <title>[1405] sqlalchemy/tags/rel_0_1_7/: 0.1.7</title> </head> <body> <div id="msg"> <dl> <dt>Revision</dt> <dd>1405</dd> <dt>Author</dt> <dd>zzzeek</dd> <dt>Date</dt> <dd>2006-05-05 12:26:02 -0500 (Fri, 05 May 2006)</dd> </dl> <h3>Log Message</h3> <pre>0.1.7</pre> <h3>Added Paths</h3> <ul> <li>sqlalchemy/tags/rel_0_1_7/</li> </ul> </div> <div id="patch"> <h3>Diff</h3> <a id="sqlalchemytagsrel_0_1_7fromrev1404sqlalchemytrunk"></a> <div class="copfile"><h4>Copied: sqlalchemy/tags/rel_0_1_7 (from rev 1404, sqlalchemy/trunk) ( => )</h4> <pre class="diff"><span> <span class="info"> </span></span></pre> </div> </div> </body> </html> |
From: <co...@sq...> - 2006-05-05 17:23:12
|
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head><style type="text/css"><!-- #msg dl { border: 1px #006 solid; background: #369; padding: 6px; color: #fff; } #msg dt { float: left; width: 6em; font-weight: bold; } #msg dt:after { content:':';} #msg dl, #msg dt, #msg ul, #msg li { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; } #msg dl a { font-weight: bold} #msg dl a:link { color:#fc3; } #msg dl a:active { color:#ff0; } #msg dl a:visited { color:#cc6; } h3 { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; font-weight: bold; } #msg pre { overflow: auto; background: #ffc; border: 1px #fc0 solid; padding: 6px; } #msg ul, pre { overflow: auto; } #patch { width: 100%; } #patch h4 {font-family: verdana,arial,helvetica,sans-serif;font-size:10pt;padding:8px;background:#369;color:#fff;margin:0;} #patch .propset h4, #patch .binary h4 {margin:0;} #patch pre {padding:0;line-height:1.2em;margin:0;} #patch .diff {width:100%;background:#eee;padding: 0 0 10px 0;overflow:auto;} #patch .propset .diff, #patch .binary .diff {padding:10px 0;} #patch span {display:block;padding:0 10px;} #patch .modfile, #patch .addfile, #patch .delfile, #patch .propset, #patch .binary, #patch .copfile {border:1px solid #ccc;margin:10px 0;} #patch ins {background:#dfd;text-decoration:none;display:block;padding:0 10px;} #patch del {background:#fdd;text-decoration:none;display:block;padding:0 10px;} #patch .lines, .info {color:#888;background:#fff;} --></style> <title>[1404] sqlalchemy/trunk/lib/sqlalchemy: fixed a _get_criterion mismatch, cleaned up types + updated types doc</title> </head> <body> <div id="msg"> <dl> <dt>Revision</dt> <dd>1404</dd> <dt>Author</dt> <dd>zzzeek</dd> <dt>Date</dt> <dd>2006-05-05 12:23:00 -0500 (Fri, 05 May 2006)</dd> </dl> <h3>Log Message</h3> <pre>fixed a _get_criterion mismatch, cleaned up types + updated types doc</pre> <h3>Modified Paths</h3> <ul> <li><a href="#sqlalchemytrunkdocbuildcontentdocument_basemyt">sqlalchemy/trunk/doc/build/content/document_base.myt</a></li> <li><a href="#sqlalchemytrunkdocbuildcontenttypestxt">sqlalchemy/trunk/doc/build/content/types.txt</a></li> <li><a href="#sqlalchemytrunklibsqlalchemymappingmapperpy">sqlalchemy/trunk/lib/sqlalchemy/mapping/mapper.py</a></li> <li><a href="#sqlalchemytrunklibsqlalchemymappingquerypy">sqlalchemy/trunk/lib/sqlalchemy/mapping/query.py</a></li> <li><a href="#sqlalchemytrunklibsqlalchemytypespy">sqlalchemy/trunk/lib/sqlalchemy/types.py</a></li> </ul> </div> <div id="patch"> <h3>Diff</h3> <a id="sqlalchemytrunkdocbuildcontentdocument_basemyt"></a> <div class="modfile"><h4>Modified: sqlalchemy/trunk/doc/build/content/document_base.myt (1403 => 1404)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/trunk/doc/build/content/document_base.myt 2006-05-05 16:25:43 UTC (rev 1403) +++ sqlalchemy/trunk/doc/build/content/document_base.myt 2006-05-05 17:23:00 UTC (rev 1404) </span><span class="lines">@@ -24,7 +24,7 @@ </span><span class="cx"> onepage='documentation' </span><span class="cx"> index='index' </span><span class="cx"> title='SQLAlchemy Documentation' </span><del>- version = '0.1.6' </del><ins>+ version = '0.1.7' </ins><span class="cx"> </%attr> </span><span class="cx"> </span><span class="cx"> <%method title> </span></span></pre></div> <a id="sqlalchemytrunkdocbuildcontenttypestxt"></a> <div class="modfile"><h4>Modified: sqlalchemy/trunk/doc/build/content/types.txt (1403 => 1404)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/trunk/doc/build/content/types.txt 2006-05-05 16:25:43 UTC (rev 1403) +++ sqlalchemy/trunk/doc/build/content/types.txt 2006-05-05 17:23:00 UTC (rev 1404) </span><span class="lines">@@ -23,6 +23,7 @@ </span><span class="cx"> class Float(Numeric): </span><span class="cx"> def __init__(self, precision=10) </span><span class="cx"> </span><ins>+ # DateTime, Date, and Time work with Python datetime objects </ins><span class="cx"> class DateTime(TypeEngine) </span><span class="cx"> </span><span class="cx"> class Date(TypeEngine) </span><span class="lines">@@ -38,11 +39,11 @@ </span><span class="cx"> # as bind params, raw bytes to unicode as </span><span class="cx"> # rowset values, using the unicode encoding </span><span class="cx"> # setting on the engine (defaults to 'utf-8') </span><del>- class Unicode(String) </del><ins>+ class Unicode(TypeDecorator) </ins><span class="cx"> </span><span class="cx"> # uses the pickle protocol to serialize data </span><span class="cx"> # in/out of Binary columns </span><del>- class PickleType(Binary) </del><ins>+ class PickleType(TypeDecorator) </ins><span class="cx"> </span><span class="cx"> More specific subclasses of these types are available, which various database engines may choose to implement specifically, allowing finer grained control over types: </span><span class="cx"> </span><span class="lines">@@ -76,53 +77,25 @@ </span><span class="cx"> </span><span class="cx"> ### Creating your Own Types {@name=custom} </span><span class="cx"> </span><del>-User-defined types can be created, to support either database-specific types, or customized pre-processing of query parameters as well as post-processing of result set data. You can make your own classes to perform these operations. They are specified by subclassing the desired type class: </del><ins>+User-defined types can be created, to support either database-specific types, or customized pre-processing of query parameters as well as post-processing of result set data. You can make your own classes to perform these operations. To augment the behavior of a `TypeEngine` type, such as `String`, the `TypeDecorator` class is used: </ins><span class="cx"> </span><span class="cx"> {python title="Basic Example"} </span><span class="cx"> import sqlalchemy.types as types </span><span class="cx"> </span><del>- class MyType(types.String): </del><ins>+ class MyType(types.TypeDecorator): </ins><span class="cx"> """basic type that decorates String, prefixes values with "PREFIX:" on </span><span class="cx"> the way in and strips it off on the way out.""" </span><ins>+ impl = types.String </ins><span class="cx"> def convert_bind_param(self, value, engine): </span><span class="cx"> return "PREFIX:" + value </span><span class="cx"> def convert_result_value(self, value, engine): </span><span class="cx"> return value[7:] </span><ins>+ +The `Unicode` and `PickleType` classes are instances of `TypeDecorator` already and can be subclassed directly. </ins><span class="cx"> </span><del>-A common desire is for a "pickle" type, which overrides a Binary object to provide pickling behavior: </del><ins>+To build a type object from scratch, which will not have a corresponding database-specific implementation, subclass `TypeEngine`: </ins><span class="cx"> </span><del>- {python title="Pickle Type"} - import cPickle - - class PickleType(Binary): - def __init__(self, protocol=pickle.HIGHEST_PROTOCOL): - """allows the pickle protocol to be specified""" - self.protocol = protocol - def convert_result_value(self, value, engine): - if value is None: - return None - buf = Binary.convert_result_value(self, value, engine) - return pickle.loads(str(buf)) - def convert_bind_param(self, value, engine): - if value is None: - return None - return Binary.convert_bind_param(self, pickle.dumps(value, self.protocol), engine) - def get_constructor_args(self): - return {} - -Which can be used like: - </del><span class="cx"> {python} </span><del>- mytable = Table('mytable', engine, - Column('id', Integer, primary_key=True), - Column('data', PickleType())) - - my_object = MyObject() - mytable.insert().execute(data=my_object) - -Another example, which illustrates a fully defined datatype. This just overrides the base type class TypeEngine: - - {python} </del><span class="cx"> import sqlalchemy.types as types </span><span class="cx"> </span><span class="cx"> class MyType(types.TypeEngine): </span><span class="lines">@@ -134,7 +107,4 @@ </span><span class="cx"> return value </span><span class="cx"> def convert_result_value(self, value, engine): </span><span class="cx"> return value </span><del>- def adapt_args(self): - """allows for the adaptation of this TypeEngine object into a new kind of type depending on its arguments.""" - return self </del><span class="cx"> </span></span></pre></div> <a id="sqlalchemytrunklibsqlalchemymappingmapperpy"></a> <div class="modfile"><h4>Modified: sqlalchemy/trunk/lib/sqlalchemy/mapping/mapper.py (1403 => 1404)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/trunk/lib/sqlalchemy/mapping/mapper.py 2006-05-05 16:25:43 UTC (rev 1403) +++ sqlalchemy/trunk/lib/sqlalchemy/mapping/mapper.py 2006-05-05 17:23:00 UTC (rev 1404) </span><span class="lines">@@ -450,6 +450,22 @@ </span><span class="cx"> self._options[optkey] = mapper </span><span class="cx"> return mapper </span><span class="cx"> </span><ins>+ def _get_criterion(self, key, value): + """used by select_by to match a key/value pair against + local properties, column names, or a matching property in this mapper's + list of relations.""" + if self.props.has_key(key): + return self.props[key].columns[0] == value + elif self.table.c.has_key(key): + return self.table.c[key] == value + else: + for prop in self.props.values(): + c = prop.get_criterion(key, value) + if c is not None: + return c + else: + return None + </ins><span class="cx"> def __getattr__(self, key): </span><span class="cx"> if (key.startswith('select_by_') or key.startswith('get_by_')): </span><span class="cx"> return getattr(self.query, key) </span></span></pre></div> <a id="sqlalchemytrunklibsqlalchemymappingquerypy"></a> <div class="modfile"><h4>Modified: sqlalchemy/trunk/lib/sqlalchemy/mapping/query.py (1403 => 1404)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/trunk/lib/sqlalchemy/mapping/query.py 2006-05-05 16:25:43 UTC (rev 1403) +++ sqlalchemy/trunk/lib/sqlalchemy/mapping/query.py 2006-05-05 17:23:00 UTC (rev 1404) </span><span class="lines">@@ -177,7 +177,7 @@ </span><span class="cx"> for key, value in params.iteritems(): </span><span class="cx"> if value is False: </span><span class="cx"> continue </span><del>- c = self._get_criterion(key, value) </del><ins>+ c = self.mapper._get_criterion(key, value) </ins><span class="cx"> if c is None: </span><span class="cx"> raise InvalidRequestError("Cant find criterion for property '"+ key + "'") </span><span class="cx"> if clause is None: </span><span class="lines">@@ -261,18 +261,3 @@ </span><span class="cx"> value.setup(key, statement, **kwargs) </span><span class="cx"> return statement </span><span class="cx"> </span><del>- def _get_criterion(self, key, value): - """used by select_by to match a key/value pair against - local properties, column names, or a matching property in this mapper's - list of relations.""" - if self.props.has_key(key): - return self.props[key].columns[0] == value - elif self.table.c.has_key(key): - return self.table.c[key] == value - else: - for prop in self.props.values(): - c = prop.get_criterion(key, value) - if c is not None: - return c - else: - return None </del></span></pre></div> <a id="sqlalchemytrunklibsqlalchemytypespy"></a> <div class="modfile"><h4>Modified: sqlalchemy/trunk/lib/sqlalchemy/types.py (1403 => 1404)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/trunk/lib/sqlalchemy/types.py 2006-05-05 16:25:43 UTC (rev 1403) +++ sqlalchemy/trunk/lib/sqlalchemy/types.py 2006-05-05 17:23:00 UTC (rev 1404) </span><span class="lines">@@ -67,7 +67,9 @@ </span><span class="cx"> def convert_result_value(self, value, engine): </span><span class="cx"> return self.impl.convert_result_value(value, engine) </span><span class="cx"> def copy(self): </span><del>- raise NotImplementedError() </del><ins>+ instance = self.__class__.__new__(self.__class__) + instance.__dict__.update(self.__dict__) + return instance </ins><span class="cx"> </span><span class="cx"> def to_instance(typeobj): </span><span class="cx"> if typeobj is None: </span><span class="lines">@@ -132,8 +134,6 @@ </span><span class="cx"> return value.decode(engine.encoding) </span><span class="cx"> else: </span><span class="cx"> return value </span><del>- def copy(self): - return Unicode(self.impl.length) </del><span class="cx"> </span><span class="cx"> class Integer(TypeEngine): </span><span class="cx"> """integer datatype""" </span><span class="lines">@@ -194,8 +194,6 @@ </span><span class="cx"> if value is None: </span><span class="cx"> return None </span><span class="cx"> return self.impl.convert_bind_param(pickle.dumps(value, self.protocol), engine) </span><del>- def copy(self): - return PickleType(self.protocol) </del><span class="cx"> </span><span class="cx"> class Boolean(TypeEngine): </span><span class="cx"> pass </span></span></pre> </div> </div> </body> </html> |
From: <co...@sq...> - 2006-05-05 16:25:54
|
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head><style type="text/css"><!-- #msg dl { border: 1px #006 solid; background: #369; padding: 6px; color: #fff; } #msg dt { float: left; width: 6em; font-weight: bold; } #msg dt:after { content:':';} #msg dl, #msg dt, #msg ul, #msg li { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; } #msg dl a { font-weight: bold} #msg dl a:link { color:#fc3; } #msg dl a:active { color:#ff0; } #msg dl a:visited { color:#cc6; } h3 { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; font-weight: bold; } #msg pre { overflow: auto; background: #ffc; border: 1px #fc0 solid; padding: 6px; } #msg ul, pre { overflow: auto; } #patch { width: 100%; } #patch h4 {font-family: verdana,arial,helvetica,sans-serif;font-size:10pt;padding:8px;background:#369;color:#fff;margin:0;} #patch .propset h4, #patch .binary h4 {margin:0;} #patch pre {padding:0;line-height:1.2em;margin:0;} #patch .diff {width:100%;background:#eee;padding: 0 0 10px 0;overflow:auto;} #patch .propset .diff, #patch .binary .diff {padding:10px 0;} #patch span {display:block;padding:0 10px;} #patch .modfile, #patch .addfile, #patch .delfile, #patch .propset, #patch .binary, #patch .copfile {border:1px solid #ccc;margin:10px 0;} #patch ins {background:#dfd;text-decoration:none;display:block;padding:0 10px;} #patch del {background:#fdd;text-decoration:none;display:block;padding:0 10px;} #patch .lines, .info {color:#888;background:#fff;} --></style> <title>[1403] sqlalchemy/branches/schema/lib/sqlalchemy/orm: futher import repair...</title> </head> <body> <div id="msg"> <dl> <dt>Revision</dt> <dd>1403</dd> <dt>Author</dt> <dd>zzzeek</dd> <dt>Date</dt> <dd>2006-05-05 11:25:43 -0500 (Fri, 05 May 2006)</dd> </dl> <h3>Log Message</h3> <pre>futher import repair...</pre> <h3>Modified Paths</h3> <ul> <li><a href="#sqlalchemybranchesschemalibsqlalchemyorm__init__py">sqlalchemy/branches/schema/lib/sqlalchemy/orm/__init__.py</a></li> <li><a href="#sqlalchemybranchesschemalibsqlalchemyormpropertiespy">sqlalchemy/branches/schema/lib/sqlalchemy/orm/properties.py</a></li> </ul> </div> <div id="patch"> <h3>Diff</h3> <a id="sqlalchemybranchesschemalibsqlalchemyorm__init__py"></a> <div class="modfile"><h4>Modified: sqlalchemy/branches/schema/lib/sqlalchemy/orm/__init__.py (1402 => 1403)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/schema/lib/sqlalchemy/orm/__init__.py 2006-05-05 16:17:03 UTC (rev 1402) +++ sqlalchemy/branches/schema/lib/sqlalchemy/orm/__init__.py 2006-05-05 16:25:43 UTC (rev 1403) </span><span class="lines">@@ -18,7 +18,7 @@ </span><span class="cx"> </span><span class="cx"> __all__ = ['relation', 'backref', 'eagerload', 'lazyload', 'noload', 'deferred', 'defer', 'undefer', </span><span class="cx"> 'mapper', 'clear_mappers', 'sql', 'extension', 'class_mapper', 'object_mapper', 'MapperExtension', </span><del>- 'cascade_mappers', 'polymorphic_union', 'current_session', 'create_session' </del><ins>+ 'cascade_mappers', 'polymorphic_union', 'current_session', 'create_session', 'class_mapper', 'object_mapper' </ins><span class="cx"> ] </span><span class="cx"> </span><span class="cx"> def relation(*args, **kwargs): </span></span></pre></div> <a id="sqlalchemybranchesschemalibsqlalchemyormpropertiespy"></a> <div class="modfile"><h4>Modified: sqlalchemy/branches/schema/lib/sqlalchemy/orm/properties.py (1402 => 1403)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/schema/lib/sqlalchemy/orm/properties.py 2006-05-05 16:17:03 UTC (rev 1402) +++ sqlalchemy/branches/schema/lib/sqlalchemy/orm/properties.py 2006-05-05 16:25:43 UTC (rev 1403) </span><span class="lines">@@ -67,7 +67,7 @@ </span><span class="cx"> sessionlib.global_attributes.register_attribute(parent.class_, key, uselist=False, callable_=lambda i:self.setup_loader(i)) </span><span class="cx"> def setup_loader(self, instance): </span><span class="cx"> if not self.parent.is_assigned(instance): </span><del>- return object_mapper(instance).props[self.key].setup_loader(instance) </del><ins>+ return mapper.object_mapper(instance).props[self.key].setup_loader(instance) </ins><span class="cx"> def lazyload(): </span><span class="cx"> session = sessionlib.object_session(instance) </span><span class="cx"> connection = session.connection(self.parent) </span><span class="lines">@@ -181,13 +181,13 @@ </span><span class="cx"> def do_init(self, key, parent): </span><span class="cx"> import sqlalchemy.orm </span><span class="cx"> if isinstance(self.argument, type): </span><del>- self.mapper = sqlalchemy.orm.class_mapper(self.argument) </del><ins>+ self.mapper = mapper.class_mapper(self.argument) </ins><span class="cx"> else: </span><span class="cx"> self.mapper = self.argument </span><span class="cx"> </span><span class="cx"> if self.association is not None: </span><span class="cx"> if isinstance(self.association, type): </span><del>- self.association = sqlalchemy.orm.class_mapper(self.association) </del><ins>+ self.association = mapper.class_mapper(self.association) </ins><span class="cx"> </span><span class="cx"> self.target = self.mapper.select_table </span><span class="cx"> self.key = key </span><span class="lines">@@ -363,7 +363,7 @@ </span><span class="cx"> </span><span class="cx"> def setup_loader(self, instance): </span><span class="cx"> if not self.parent.is_assigned(instance): </span><del>- return object_mapper(instance).props[self.key].setup_loader(instance) </del><ins>+ return mapper.object_mapper(instance).props[self.key].setup_loader(instance) </ins><span class="cx"> def lazyload(): </span><span class="cx"> params = {} </span><span class="cx"> allparams = True </span></span></pre> </div> </div> </body> </html> |
From: <co...@sq...> - 2006-05-05 16:17:14
|
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head><style type="text/css"><!-- #msg dl { border: 1px #006 solid; background: #369; padding: 6px; color: #fff; } #msg dt { float: left; width: 6em; font-weight: bold; } #msg dt:after { content:':';} #msg dl, #msg dt, #msg ul, #msg li { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; } #msg dl a { font-weight: bold} #msg dl a:link { color:#fc3; } #msg dl a:active { color:#ff0; } #msg dl a:visited { color:#cc6; } h3 { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; font-weight: bold; } #msg pre { overflow: auto; background: #ffc; border: 1px #fc0 solid; padding: 6px; } #msg ul, pre { overflow: auto; } #patch { width: 100%; } #patch h4 {font-family: verdana,arial,helvetica,sans-serif;font-size:10pt;padding:8px;background:#369;color:#fff;margin:0;} #patch .propset h4, #patch .binary h4 {margin:0;} #patch pre {padding:0;line-height:1.2em;margin:0;} #patch .diff {width:100%;background:#eee;padding: 0 0 10px 0;overflow:auto;} #patch .propset .diff, #patch .binary .diff {padding:10px 0;} #patch span {display:block;padding:0 10px;} #patch .modfile, #patch .addfile, #patch .delfile, #patch .propset, #patch .binary, #patch .copfile {border:1px solid #ccc;margin:10px 0;} #patch ins {background:#dfd;text-decoration:none;display:block;padding:0 10px;} #patch del {background:#fdd;text-decoration:none;display:block;padding:0 10px;} #patch .lines, .info {color:#888;background:#fff;} --></style> <title>[1402] sqlalchemy/branches/schema/lib/sqlalchemy/orm/__init__.py: more tweak</title> </head> <body> <div id="msg"> <dl> <dt>Revision</dt> <dd>1402</dd> <dt>Author</dt> <dd>zzzeek</dd> <dt>Date</dt> <dd>2006-05-05 11:17:03 -0500 (Fri, 05 May 2006)</dd> </dl> <h3>Log Message</h3> <pre>more tweak</pre> <h3>Modified Paths</h3> <ul> <li><a href="#sqlalchemybranchesschemalibsqlalchemyorm__init__py">sqlalchemy/branches/schema/lib/sqlalchemy/orm/__init__.py</a></li> </ul> </div> <div id="patch"> <h3>Diff</h3> <a id="sqlalchemybranchesschemalibsqlalchemyorm__init__py"></a> <div class="modfile"><h4>Modified: sqlalchemy/branches/schema/lib/sqlalchemy/orm/__init__.py (1401 => 1402)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/schema/lib/sqlalchemy/orm/__init__.py 2006-05-05 16:15:08 UTC (rev 1401) +++ sqlalchemy/branches/schema/lib/sqlalchemy/orm/__init__.py 2006-05-05 16:17:03 UTC (rev 1402) </span><span class="lines">@@ -8,17 +8,13 @@ </span><span class="cx"> the mapper package provides object-relational functionality, building upon the schema and sql </span><span class="cx"> packages and tying operations to class properties and constructors. </span><span class="cx"> """ </span><del>-import sqlalchemy.sql as sql -import sqlalchemy.schema as schema -import sqlalchemy.engine as engine -import sqlalchemy.util as util -import sqlalchemy.exceptions as exceptions </del><ins>+from sqlalchemy import sql, schema, engine, util, exceptions </ins><span class="cx"> from mapper import * </span><span class="cx"> from mapper import mapper_registry </span><span class="cx"> from util import polymorphic_union </span><span class="cx"> import properties </span><del>-from sqlalchemy.orm.session import current_session -from sqlalchemy.orm.session import Session as create_session </del><ins>+from session import current_session +from session import Session as create_session </ins><span class="cx"> </span><span class="cx"> __all__ = ['relation', 'backref', 'eagerload', 'lazyload', 'noload', 'deferred', 'defer', 'undefer', </span><span class="cx"> 'mapper', 'clear_mappers', 'sql', 'extension', 'class_mapper', 'object_mapper', 'MapperExtension', </span></span></pre> </div> </div> </body> </html> |
From: <co...@sq...> - 2006-05-05 16:15:26
|
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head><style type="text/css"><!-- #msg dl { border: 1px #006 solid; background: #369; padding: 6px; color: #fff; } #msg dt { float: left; width: 6em; font-weight: bold; } #msg dt:after { content:':';} #msg dl, #msg dt, #msg ul, #msg li { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; } #msg dl a { font-weight: bold} #msg dl a:link { color:#fc3; } #msg dl a:active { color:#ff0; } #msg dl a:visited { color:#cc6; } h3 { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; font-weight: bold; } #msg pre { overflow: auto; background: #ffc; border: 1px #fc0 solid; padding: 6px; } #msg ul, pre { overflow: auto; } #patch { width: 100%; } #patch h4 {font-family: verdana,arial,helvetica,sans-serif;font-size:10pt;padding:8px;background:#369;color:#fff;margin:0;} #patch .propset h4, #patch .binary h4 {margin:0;} #patch pre {padding:0;line-height:1.2em;margin:0;} #patch .diff {width:100%;background:#eee;padding: 0 0 10px 0;overflow:auto;} #patch .propset .diff, #patch .binary .diff {padding:10px 0;} #patch span {display:block;padding:0 10px;} #patch .modfile, #patch .addfile, #patch .delfile, #patch .propset, #patch .binary, #patch .copfile {border:1px solid #ccc;margin:10px 0;} #patch ins {background:#dfd;text-decoration:none;display:block;padding:0 10px;} #patch del {background:#fdd;text-decoration:none;display:block;padding:0 10px;} #patch .lines, .info {color:#888;background:#fff;} --></style> <title>[1401] sqlalchemy/branches/schema/test: cleanup of imports, added default timeout to pool, cleaned up pool arguments</title> </head> <body> <div id="msg"> <dl> <dt>Revision</dt> <dd>1401</dd> <dt>Author</dt> <dd>zzzeek</dd> <dt>Date</dt> <dd>2006-05-05 11:15:08 -0500 (Fri, 05 May 2006)</dd> </dl> <h3>Log Message</h3> <pre>cleanup of imports, added default timeout to pool, cleaned up pool arguments</pre> <h3>Modified Paths</h3> <ul> <li><a href="#sqlalchemybranchesschemadocbuildcontentdbenginetxt">sqlalchemy/branches/schema/doc/build/content/dbengine.txt</a></li> <li><a href="#sqlalchemybranchesschemalibsqlalchemy__init__py">sqlalchemy/branches/schema/lib/sqlalchemy/__init__.py</a></li> <li><a href="#sqlalchemybranchesschemalibsqlalchemydatabasespostgrespy">sqlalchemy/branches/schema/lib/sqlalchemy/databases/postgres.py</a></li> <li><a href="#sqlalchemybranchesschemalibsqlalchemyenginestrategiespy">sqlalchemy/branches/schema/lib/sqlalchemy/engine/strategies.py</a></li> <li><a href="#sqlalchemybranchesschemalibsqlalchemyorm__init__py">sqlalchemy/branches/schema/lib/sqlalchemy/orm/__init__.py</a></li> <li><a href="#sqlalchemybranchesschemalibsqlalchemyormmapperpy">sqlalchemy/branches/schema/lib/sqlalchemy/orm/mapper.py</a></li> <li><a href="#sqlalchemybranchesschemalibsqlalchemyormpropertiespy">sqlalchemy/branches/schema/lib/sqlalchemy/orm/properties.py</a></li> <li><a href="#sqlalchemybranchesschemalibsqlalchemyormquerypy">sqlalchemy/branches/schema/lib/sqlalchemy/orm/query.py</a></li> <li><a href="#sqlalchemybranchesschemalibsqlalchemyormsessionpy">sqlalchemy/branches/schema/lib/sqlalchemy/orm/session.py</a></li> <li><a href="#sqlalchemybranchesschemalibsqlalchemypoolpy">sqlalchemy/branches/schema/lib/sqlalchemy/pool.py</a></li> <li><a href="#sqlalchemybranchesschematestmapperpy">sqlalchemy/branches/schema/test/mapper.py</a></li> <li><a href="#sqlalchemybranchesschematestpoolpy">sqlalchemy/branches/schema/test/pool.py</a></li> </ul> </div> <div id="patch"> <h3>Diff</h3> <a id="sqlalchemybranchesschemadocbuildcontentdbenginetxt"></a> <div class="modfile"><h4>Modified: sqlalchemy/branches/schema/doc/build/content/dbengine.txt (1400 => 1401)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/schema/doc/build/content/dbengine.txt 2006-05-05 15:05:00 UTC (rev 1400) +++ sqlalchemy/branches/schema/doc/build/content/dbengine.txt 2006-05-05 16:15:08 UTC (rev 1401) </span><span class="lines">@@ -65,6 +65,7 @@ </span><span class="cx"> </span><span class="cx"> * pool_size=5 : the number of connections to keep open inside the connection pool. This is only used with `QueuePool`. </span><span class="cx"> * max_overflow=10 : the number of connections to allow in "overflow", that is connections that can be opened above and beyond the initial five. this is only used with `QueuePool`. </span><ins>+* pool_timeout=30 : number of seconds to wait before giving up on getting a connection from the pool. This is only used with `QueuePool`. </ins><span class="cx"> * echo=False : if True, the Engine will log all statements as well as a repr() of their parameter lists to the engines logger, which defaults to sys.stdout. A SQLEngine instances' "echo" data member can be modified at any time to turn logging on and off. If set to the string 'debug', result rows will be printed to the standard output as well. </span><span class="cx"> * logger=None : a file-like object where logging output can be sent, if echo is set to True. This defaults to sys.stdout. </span><span class="cx"> * module=None : used by Oracle and Postgres, this is a reference to a DBAPI2 module to be used instead of the engine's default module. For Postgres, the default is psycopg2, or psycopg1 if 2 cannot be found. For Oracle, its cx_Oracle. </span></span></pre></div> <a id="sqlalchemybranchesschemalibsqlalchemy__init__py"></a> <div class="modfile"><h4>Modified: sqlalchemy/branches/schema/lib/sqlalchemy/__init__.py (1400 => 1401)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/schema/lib/sqlalchemy/__init__.py 2006-05-05 15:05:00 UTC (rev 1400) +++ sqlalchemy/branches/schema/lib/sqlalchemy/__init__.py 2006-05-05 16:15:08 UTC (rev 1401) </span><span class="lines">@@ -7,18 +7,11 @@ </span><span class="cx"> from types import * </span><span class="cx"> from sql import * </span><span class="cx"> from schema import * </span><del>-from exceptions import * -from engine import * -import sqlalchemy.sql -import sqlalchemy.orm as orm </del><span class="cx"> from sqlalchemy.orm import * </span><del>-import sqlalchemy.ext.proxy </del><span class="cx"> </span><del>-from sqlalchemy.orm.session import Session, current_session </del><ins>+from sqlalchemy.engine import create_engine +from sqlalchemy.schema import default_metadata </ins><span class="cx"> </span><del>-create_engine = sqlalchemy.engine.create_engine -create_session = sqlalchemy.orm.session.Session - </del><span class="cx"> def global_connect(*args, **kwargs): </span><del>- sqlalchemy.schema.default_metadata.connect(*args, **kwargs) </del><ins>+ default_metadata.connect(*args, **kwargs) </ins><span class="cx"> </span><span class="cx">\ No newline at end of file </span></span></pre></div> <a id="sqlalchemybranchesschemalibsqlalchemydatabasespostgrespy"></a> <div class="modfile"><h4>Modified: sqlalchemy/branches/schema/lib/sqlalchemy/databases/postgres.py (1400 => 1401)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/schema/lib/sqlalchemy/databases/postgres.py 2006-05-05 15:05:00 UTC (rev 1400) +++ sqlalchemy/branches/schema/lib/sqlalchemy/databases/postgres.py 2006-05-05 16:15:08 UTC (rev 1401) </span><span class="lines">@@ -9,11 +9,11 @@ </span><span class="cx"> import sqlalchemy.util as util </span><span class="cx"> import sqlalchemy.sql as sql </span><span class="cx"> import sqlalchemy.engine as engine </span><ins>+import sqlalchemy.engine.default as default </ins><span class="cx"> import sqlalchemy.schema as schema </span><span class="cx"> import sqlalchemy.ansisql as ansisql </span><span class="cx"> import sqlalchemy.types as sqltypes </span><del>-from sqlalchemy.exceptions import * -from sqlalchemy import * </del><ins>+import sqlalchemy.exceptions as exceptions </ins><span class="cx"> import information_schema as ischema </span><span class="cx"> </span><span class="cx"> try: </span><span class="lines">@@ -202,7 +202,7 @@ </span><span class="cx"> self.use_oids = use_oids </span><span class="cx"> if module is None: </span><span class="cx"> if psycopg is None: </span><del>- raise ArgumentError("Couldnt locate psycopg1 or psycopg2: specify postgres module argument") </del><ins>+ raise exceptions.ArgumentError("Couldnt locate psycopg1 or psycopg2: specify postgres module argument") </ins><span class="cx"> self.module = psycopg </span><span class="cx"> else: </span><span class="cx"> self.module = module </span><span class="lines">@@ -250,7 +250,7 @@ </span><span class="cx"> </span><span class="cx"> def last_inserted_ids(self): </span><span class="cx"> if self.context.last_inserted_ids is None: </span><del>- raise InvalidRequestError("no INSERT executed, or cant use cursor.lastrowid without Postgres OIDs enabled") </del><ins>+ raise exceptions.InvalidRequestError("no INSERT executed, or cant use cursor.lastrowid without Postgres OIDs enabled") </ins><span class="cx"> else: </span><span class="cx"> return self.context.last_inserted_ids </span><span class="cx"> </span><span class="lines">@@ -330,7 +330,7 @@ </span><span class="cx"> </span><span class="cx"> def get_column_specification(self, column, override_pk=False, **kwargs): </span><span class="cx"> colspec = column.name </span><del>- if column.primary_key and isinstance(column.type, types.Integer) and (column.default is None or (isinstance(column.default, schema.Sequence) and column.default.optional)): </del><ins>+ if column.primary_key and isinstance(column.type, sqltypes.Integer) and (column.default is None or (isinstance(column.default, schema.Sequence) and column.default.optional)): </ins><span class="cx"> colspec += " SERIAL" </span><span class="cx"> else: </span><span class="cx"> colspec += " " + column.type.engine_impl(self.engine).get_col_spec() </span><span class="lines">@@ -364,7 +364,7 @@ </span><span class="cx"> if isinstance(column.default, schema.PassiveDefault): </span><span class="cx"> c = self.proxy("select %s" % column.default.arg) </span><span class="cx"> return c.fetchone()[0] </span><del>- elif isinstance(column.type, types.Integer) and (column.default is None or (isinstance(column.default, schema.Sequence) and column.default.optional)): </del><ins>+ elif isinstance(column.type, sqltypes.Integer) and (column.default is None or (isinstance(column.default, schema.Sequence) and column.default.optional)): </ins><span class="cx"> sch = column.table.schema </span><span class="cx"> if sch is not None: </span><span class="cx"> exc = "select nextval('%s.%s_%s_seq')" % (sch, column.table.name, column.name) </span></span></pre></div> <a id="sqlalchemybranchesschemalibsqlalchemyenginestrategiespy"></a> <div class="modfile"><h4>Modified: sqlalchemy/branches/schema/lib/sqlalchemy/engine/strategies.py (1400 => 1401)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/schema/lib/sqlalchemy/engine/strategies.py 2006-05-05 15:05:00 UTC (rev 1400) +++ sqlalchemy/branches/schema/lib/sqlalchemy/engine/strategies.py 2006-05-05 16:15:08 UTC (rev 1401) </span><span class="lines">@@ -31,10 +31,10 @@ </span><span class="cx"> </span><span class="cx"> dialect = module.dialect(**kwargs) </span><span class="cx"> </span><del>- poolargs = kwargs.copy() - poolargs['echo'] = poolargs.get('echo_pool', False) - poolargs['pool_size'] = poolargs.get('pool_size', False) - poolargs['max_overflow'] = poolargs.get('max_overflow', False) </del><ins>+ poolargs = {} + for key in (('echo', 'echo_pool'), ('pool_size', 'pool_size'), ('max_overflow', 'max_overflow'), ('poolclass', 'poolclass'), ('pool_timeout','timeout')): + if kwargs.has_key(key[0]): + poolargs[key[1]] = kwargs[key[0]] </ins><span class="cx"> poolclass = getattr(module, 'poolclass', None) </span><span class="cx"> if poolclass is not None: </span><span class="cx"> poolargs.setdefault('poolclass', poolclass) </span><span class="lines">@@ -53,10 +53,10 @@ </span><span class="cx"> </span><span class="cx"> dialect = module.dialect(**kwargs) </span><span class="cx"> </span><del>- poolargs = kwargs.copy() - poolargs['echo'] = poolargs.get('echo_pool', False) - poolargs['pool_size'] = poolargs.get('pool_size', False) - poolargs['max_overflow'] = poolargs.get('max_overflow', False) </del><ins>+ poolargs = {} + for key in (('echo', 'echo_pool'), ('pool_size', 'pool_size'), ('max_overflow', 'max_overflow'), ('poolclass', 'poolclass'), ('pool_timeout','timeout')): + if kwargs.has_key(key[0]): + poolargs[key[1]] = kwargs[key[0]] </ins><span class="cx"> poolclass = getattr(module, 'poolclass', None) </span><span class="cx"> if poolclass is not None: </span><span class="cx"> poolargs.setdefault('poolclass', poolclass) </span></span></pre></div> <a id="sqlalchemybranchesschemalibsqlalchemyorm__init__py"></a> <div class="modfile"><h4>Modified: sqlalchemy/branches/schema/lib/sqlalchemy/orm/__init__.py (1400 => 1401)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/schema/lib/sqlalchemy/orm/__init__.py 2006-05-05 15:05:00 UTC (rev 1400) +++ sqlalchemy/branches/schema/lib/sqlalchemy/orm/__init__.py 2006-05-05 16:15:08 UTC (rev 1401) </span><span class="lines">@@ -12,38 +12,41 @@ </span><span class="cx"> import sqlalchemy.schema as schema </span><span class="cx"> import sqlalchemy.engine as engine </span><span class="cx"> import sqlalchemy.util as util </span><del>-from exceptions import * </del><ins>+import sqlalchemy.exceptions as exceptions </ins><span class="cx"> from mapper import * </span><ins>+from mapper import mapper_registry </ins><span class="cx"> from util import polymorphic_union </span><del>-from properties import * </del><ins>+import properties +from sqlalchemy.orm.session import current_session +from sqlalchemy.orm.session import Session as create_session </ins><span class="cx"> </span><span class="cx"> __all__ = ['relation', 'backref', 'eagerload', 'lazyload', 'noload', 'deferred', 'defer', 'undefer', </span><span class="cx"> 'mapper', 'clear_mappers', 'sql', 'extension', 'class_mapper', 'object_mapper', 'MapperExtension', </span><del>- 'cascade_mappers', 'polymorphic_union' </del><ins>+ 'cascade_mappers', 'polymorphic_union', 'current_session', 'create_session' </ins><span class="cx"> ] </span><span class="cx"> </span><span class="cx"> def relation(*args, **kwargs): </span><span class="cx"> """provides a relationship of a primary Mapper to a secondary Mapper, which corresponds </span><span class="cx"> to a parent-child or associative table relationship.""" </span><span class="cx"> if len(args) > 1 and isinstance(args[0], type): </span><del>- raise ArgumentError("relation(class, table, **kwargs) is deprecated. Please use relation(class, **kwargs) or relation(mapper, **kwargs).") </del><ins>+ raise exceptions.ArgumentError("relation(class, table, **kwargs) is deprecated. Please use relation(class, **kwargs) or relation(mapper, **kwargs).") </ins><span class="cx"> return _relation_loader(*args, **kwargs) </span><span class="cx"> </span><span class="cx"> def _relation_loader(mapper, secondary=None, primaryjoin=None, secondaryjoin=None, lazy=True, **kwargs): </span><span class="cx"> if lazy: </span><del>- return LazyLoader(mapper, secondary, primaryjoin, secondaryjoin, **kwargs) </del><ins>+ return properties.LazyLoader(mapper, secondary, primaryjoin, secondaryjoin, **kwargs) </ins><span class="cx"> elif lazy is None: </span><del>- return PropertyLoader(mapper, secondary, primaryjoin, secondaryjoin, **kwargs) </del><ins>+ return properties.PropertyLoader(mapper, secondary, primaryjoin, secondaryjoin, **kwargs) </ins><span class="cx"> else: </span><del>- return EagerLoader(mapper, secondary, primaryjoin, secondaryjoin, **kwargs) </del><ins>+ return properties.EagerLoader(mapper, secondary, primaryjoin, secondaryjoin, **kwargs) </ins><span class="cx"> </span><span class="cx"> def backref(name, **kwargs): </span><del>- return BackRef(name, **kwargs) </del><ins>+ return properties.BackRef(name, **kwargs) </ins><span class="cx"> </span><span class="cx"> def deferred(*columns, **kwargs): </span><span class="cx"> """returns a DeferredColumnProperty, which indicates this object attributes should only be loaded </span><span class="cx"> from its corresponding table column when first accessed.""" </span><del>- return DeferredColumnProperty(*columns, **kwargs) </del><ins>+ return properties.DeferredColumnProperty(*columns, **kwargs) </ins><span class="cx"> </span><span class="cx"> def mapper(class_, table=None, *args, **params): </span><span class="cx"> """returns a new or already cached Mapper object.""" </span><span class="lines">@@ -70,26 +73,26 @@ </span><span class="cx"> def eagerload(name, **kwargs): </span><span class="cx"> """returns a MapperOption that will convert the property of the given name </span><span class="cx"> into an eager load. Used with mapper.options()""" </span><del>- return EagerLazyOption(name, toeager=True, **kwargs) </del><ins>+ return properties.EagerLazyOption(name, toeager=True, **kwargs) </ins><span class="cx"> </span><span class="cx"> def lazyload(name, **kwargs): </span><span class="cx"> """returns a MapperOption that will convert the property of the given name </span><span class="cx"> into a lazy load. Used with mapper.options()""" </span><del>- return EagerLazyOption(name, toeager=False, **kwargs) </del><ins>+ return properties.EagerLazyOption(name, toeager=False, **kwargs) </ins><span class="cx"> </span><span class="cx"> def noload(name, **kwargs): </span><span class="cx"> """returns a MapperOption that will convert the property of the given name </span><span class="cx"> into a non-load. Used with mapper.options()""" </span><del>- return EagerLazyOption(name, toeager=None, **kwargs) </del><ins>+ return properties.EagerLazyOption(name, toeager=None, **kwargs) </ins><span class="cx"> </span><span class="cx"> def defer(name, **kwargs): </span><span class="cx"> """returns a MapperOption that will convert the column property of the given </span><span class="cx"> name into a deferred load. Used with mapper.options()""" </span><del>- return DeferredOption(name, defer=True) </del><ins>+ return properties.DeferredOption(name, defer=True) </ins><span class="cx"> def undefer(name, **kwargs): </span><span class="cx"> """returns a MapperOption that will convert the column property of the given </span><span class="cx"> name into a non-deferred (regular column) load. Used with mapper.options.""" </span><del>- return DeferredOption(name, defer=False) </del><ins>+ return properties.DeferredOption(name, defer=False) </ins><span class="cx"> </span><span class="cx"> </span><span class="cx"> </span></span></pre></div> <a id="sqlalchemybranchesschemalibsqlalchemyormmapperpy"></a> <div class="modfile"><h4>Modified: sqlalchemy/branches/schema/lib/sqlalchemy/orm/mapper.py (1400 => 1401)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/schema/lib/sqlalchemy/orm/mapper.py 2006-05-05 15:05:00 UTC (rev 1400) +++ sqlalchemy/branches/schema/lib/sqlalchemy/orm/mapper.py 2006-05-05 16:15:08 UTC (rev 1401) </span><span class="lines">@@ -4,20 +4,16 @@ </span><span class="cx"> # This module is part of SQLAlchemy and is released under </span><span class="cx"> # the MIT License: http://www.opensource.org/licenses/mit-license.php </span><span class="cx"> </span><del>- -import sqlalchemy.sql as sql -import sqlalchemy.schema as schema -import sqlalchemy.util as util </del><ins>+from sqlalchemy import sql, schema, util, exceptions +from sqlalchemy import sql_util as sqlutil </ins><span class="cx"> import util as mapperutil </span><del>-import sqlalchemy.sql_util as sqlutil </del><span class="cx"> import sync </span><del>-from sqlalchemy.exceptions import * </del><span class="cx"> import query as querylib </span><span class="cx"> import session as sessionlib </span><del>-import sys -import weakref -import sets </del><ins>+import sys, weakref, sets </ins><span class="cx"> </span><ins>+__all__ = ['Mapper', 'MapperExtension', 'class_mapper', 'object_mapper'] + </ins><span class="cx"> # a dictionary mapping classes to their primary mappers </span><span class="cx"> mapper_registry = weakref.WeakKeyDictionary() </span><span class="cx"> </span><span class="lines">@@ -85,7 +81,7 @@ </span><span class="cx"> self.polymorphic_map = polymorphic_map </span><span class="cx"> </span><span class="cx"> if not issubclass(class_, object): </span><del>- raise ArgumentError("Class '%s' is not a new-style class" % class_.__name__) </del><ins>+ raise exceptions.ArgumentError("Class '%s' is not a new-style class" % class_.__name__) </ins><span class="cx"> </span><span class="cx"> # set up various Selectable units: </span><span class="cx"> </span><span class="lines">@@ -105,7 +101,7 @@ </span><span class="cx"> # the configured properties on the mapper are not matched against the alias </span><span class="cx"> # we make, theres workarounds but it starts to get really crazy (its crazy enough </span><span class="cx"> # the SQL that gets generated) so just require an alias </span><del>- raise ArgumentError("Mapping against a Select object requires that it has a name. Use an alias to give it a name, i.e. s = select(...).alias('myselect')") </del><ins>+ raise exceptions.ArgumentError("Mapping against a Select object requires that it has a name. Use an alias to give it a name, i.e. s = select(...).alias('myselect')") </ins><span class="cx"> </span><span class="cx"> self.local_table = local_table </span><span class="cx"> </span><span class="lines">@@ -113,7 +109,7 @@ </span><span class="cx"> if isinstance(inherits, type): </span><span class="cx"> inherits = class_mapper(inherits) </span><span class="cx"> if self.class_.__mro__[1] != inherits.class_: </span><del>- raise ArgumentError("Class '%s' does not inherit from '%s'" % (self.class_.__name__, inherits.class_.__name__)) </del><ins>+ raise exceptions.ArgumentError("Class '%s' does not inherit from '%s'" % (self.class_.__name__, inherits.class_.__name__)) </ins><span class="cx"> # inherit_condition is optional. </span><span class="cx"> if not local_table is inherits.local_table: </span><span class="cx"> if concrete: </span><span class="lines">@@ -180,7 +176,7 @@ </span><span class="cx"> except KeyError: </span><span class="cx"> l = self.pks_by_table.setdefault(t, util.HashSet(ordered=True)) </span><span class="cx"> if not len(t.primary_key): </span><del>- raise ArgumentError("Table " + t.name + " has no primary key columns. Specify primary_key argument to mapper.") </del><ins>+ raise exceptions.ArgumentError("Table " + t.name + " has no primary key columns. Specify primary_key argument to mapper.") </ins><span class="cx"> for k in t.primary_key: </span><span class="cx"> l.append(k) </span><span class="cx"> </span><span class="lines">@@ -223,7 +219,7 @@ </span><span class="cx"> #prop.columns.append(column) </span><span class="cx"> else: </span><span class="cx"> if not allow_column_override: </span><del>- raise ArgumentError("WARNING: column '%s' not being added due to property '%s'. Specify 'allow_column_override=True' to mapper() to ignore this condition." % (column.key, repr(prop))) </del><ins>+ raise exceptions.ArgumentError("WARNING: column '%s' not being added due to property '%s'. Specify 'allow_column_override=True' to mapper() to ignore this condition." % (column.key, repr(prop))) </ins><span class="cx"> else: </span><span class="cx"> continue </span><span class="cx"> </span><span class="lines">@@ -236,7 +232,7 @@ </span><span class="cx"> sessionlib.global_attributes.reset_class_managed(self.class_) </span><span class="cx"> self._init_class() </span><span class="cx"> elif not non_primary: </span><del>- raise ArgumentError("Class '%s' already has a primary mapper defined. Use is_primary=True to assign a new primary mapper to the class, or use non_primary=True to create a non primary Mapper" % self.class_) </del><ins>+ raise exceptions.ArgumentError("Class '%s' already has a primary mapper defined. Use is_primary=True to assign a new primary mapper to the class, or use non_primary=True to create a non primary Mapper" % self.class_) </ins><span class="cx"> </span><span class="cx"> for key in self.polymorphic_map.keys(): </span><span class="cx"> if isinstance(self.polymorphic_map[key], type): </span><span class="lines">@@ -351,14 +347,14 @@ </span><span class="cx"> try: </span><span class="cx"> prop = self.select_table._get_col_by_original(prop) </span><span class="cx"> except KeyError: </span><del>- raise ArgumentError("Column '%s' is not represented in mapper's table" % prop._label) </del><ins>+ raise exceptions.ArgumentError("Column '%s' is not represented in mapper's table" % prop._label) </ins><span class="cx"> self.columns[key] = prop </span><span class="cx"> prop = ColumnProperty(prop) </span><span class="cx"> elif isinstance(prop, list) and sql.is_column(prop[0]): </span><span class="cx"> try: </span><span class="cx"> prop = [self.select_table._get_col_by_original(p) for p in prop] </span><span class="cx"> except KeyError, e: </span><del>- raise ArgumentError("Column '%s' is not represented in mapper's table" % e.args[0]) </del><ins>+ raise exceptions.ArgumentError("Column '%s' is not represented in mapper's table" % e.args[0]) </ins><span class="cx"> self.columns[key] = prop[0] </span><span class="cx"> prop = ColumnProperty(*prop) </span><span class="cx"> self.props[key] = prop </span><span class="lines">@@ -526,11 +522,11 @@ </span><span class="cx"> prop = self.props[column.key] </span><span class="cx"> if not raiseerror: </span><span class="cx"> return None </span><del>- raise InvalidRequestError("Column '%s.%s' is not available, due to conflicting property '%s':%s" % (column.table.name, column.name, column.key, repr(prop))) </del><ins>+ raise exceptions.InvalidRequestError("Column '%s.%s' is not available, due to conflicting property '%s':%s" % (column.table.name, column.name, column.key, repr(prop))) </ins><span class="cx"> except KeyError: </span><span class="cx"> if not raiseerror: </span><span class="cx"> return None </span><del>- raise InvalidRequestError("No column %s.%s is configured on mapper %s..." % (column.table.name, column.name, str(self))) </del><ins>+ raise exceptions.InvalidRequestError("No column %s.%s is configured on mapper %s..." % (column.table.name, column.name, str(self))) </ins><span class="cx"> return prop[0] </span><span class="cx"> </span><span class="cx"> def _getattrbycolumn(self, obj, column, raiseerror=True): </span><span class="lines">@@ -1060,7 +1056,7 @@ </span><span class="cx"> return mapper_registry[ClassKey(object.__class__, getattr(object, '_entity_name', entity_name))] </span><span class="cx"> except KeyError: </span><span class="cx"> if raiseerror: </span><del>- raise InvalidRequestError("Class '%s' entity name '%s' has no mapper associated with it" % (object.__class__.__name__, getattr(object, '_entity_name', None))) </del><ins>+ raise exceptions.InvalidRequestError("Class '%s' entity name '%s' has no mapper associated with it" % (object.__class__.__name__, getattr(object, '_entity_name', None))) </ins><span class="cx"> else: </span><span class="cx"> return None </span><span class="cx"> </span><span class="lines">@@ -1069,4 +1065,4 @@ </span><span class="cx"> try: </span><span class="cx"> return mapper_registry[ClassKey(class_, entity_name)] </span><span class="cx"> except (KeyError, AttributeError): </span><del>- raise InvalidRequestError("Class '%s' entity name '%s' has no mapper associated with it" % (class_.__name__, entity_name)) </del><ins>+ raise exceptions.InvalidRequestError("Class '%s' entity name '%s' has no mapper associated with it" % (class_.__name__, entity_name)) </ins></span></pre></div> <a id="sqlalchemybranchesschemalibsqlalchemyormpropertiespy"></a> <div class="modfile"><h4>Modified: sqlalchemy/branches/schema/lib/sqlalchemy/orm/properties.py (1400 => 1401)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/schema/lib/sqlalchemy/orm/properties.py 2006-05-05 15:05:00 UTC (rev 1400) +++ sqlalchemy/branches/schema/lib/sqlalchemy/orm/properties.py 2006-05-05 16:15:08 UTC (rev 1401) </span><span class="lines">@@ -4,25 +4,19 @@ </span><span class="cx"> # This module is part of SQLAlchemy and is released under </span><span class="cx"> # the MIT License: http://www.opensource.org/licenses/mit-license.php </span><span class="cx"> </span><del>-"""defines a set of MapperProperty objects, including basic column properties as </del><ins>+"""defines a set of mapper.MapperProperty objects, including basic column properties as </ins><span class="cx"> well as relationships. also defines some MapperOptions that can be used with the </span><span class="cx"> properties.""" </span><span class="cx"> </span><del>-from mapper import * -import sqlalchemy.sql as sql -import sqlalchemy.schema as schema -import sqlalchemy.engine as engine -import sqlalchemy.util as util -import sqlalchemy.attributes as attributes </del><ins>+from sqlalchemy import sql, schema, util, attributes, exceptions </ins><span class="cx"> import sync </span><span class="cx"> import mapper </span><span class="cx"> import session as sessionlib </span><span class="cx"> import dependency </span><span class="cx"> import util as mapperutil </span><del>-from sqlalchemy.exceptions import * </del><span class="cx"> import sets, random </span><span class="cx"> </span><del>-class ColumnProperty(MapperProperty): </del><ins>+class ColumnProperty(mapper.MapperProperty): </ins><span class="cx"> """describes an object attribute that corresponds to a table column.""" </span><span class="cx"> def __init__(self, *columns): </span><span class="cx"> """the list of columns describes a single object property. if there </span><span class="lines">@@ -114,7 +108,7 @@ </span><span class="cx"> </span><span class="cx"> mapper.ColumnProperty = ColumnProperty </span><span class="cx"> </span><del>-class PropertyLoader(MapperProperty): </del><ins>+class PropertyLoader(mapper.MapperProperty): </ins><span class="cx"> ONETOMANY = 0 </span><span class="cx"> MANYTOONE = 1 </span><span class="cx"> MANYTOMANY = 2 </span><span class="lines">@@ -200,7 +194,7 @@ </span><span class="cx"> self.parent = parent </span><span class="cx"> </span><span class="cx"> if self.secondaryjoin is not None and self.secondary is None: </span><del>- raise ArgumentError("Property '" + self.key + "' specified with secondary join condition but no secondary argument") </del><ins>+ raise exceptions.ArgumentError("Property '" + self.key + "' specified with secondary join condition but no secondary argument") </ins><span class="cx"> # if join conditions were not specified, figure them out based on foreign keys </span><span class="cx"> if self.secondary is not None: </span><span class="cx"> if self.secondaryjoin is None: </span><span class="lines">@@ -246,7 +240,7 @@ </span><span class="cx"> if self.backref is not None: </span><span class="cx"> self.backref.compile(self) </span><span class="cx"> elif not sessionlib.global_attributes.is_class_managed(parent.class_, key): </span><del>- raise ArgumentError("Attempting to assign a new relation '%s' to a non-primary mapper on class '%s'. New relations can only be added to the primary mapper, i.e. the very first mapper created for class '%s' " % (key, parent.class_.__name__, parent.class_.__name__)) </del><ins>+ raise exceptions.ArgumentError("Attempting to assign a new relation '%s' to a non-primary mapper on class '%s'. New relations can only be added to the primary mapper, i.e. the very first mapper created for class '%s' " % (key, parent.class_.__name__, parent.class_.__name__)) </ins><span class="cx"> </span><span class="cx"> self.do_init_subclass(key, parent) </span><span class="cx"> </span><span class="lines">@@ -270,7 +264,7 @@ </span><span class="cx"> elif self.foreigntable == self.parent.unjoined_table: </span><span class="cx"> return sync.MANYTOONE </span><span class="cx"> else: </span><del>- raise ArgumentError("Cant determine relation direction") </del><ins>+ raise exceptions.ArgumentError("Cant determine relation direction") </ins><span class="cx"> </span><span class="cx"> def _find_dependent(self): </span><span class="cx"> """searches through the primary join condition to determine which side </span><span class="lines">@@ -284,18 +278,18 @@ </span><span class="cx"> return </span><span class="cx"> if isinstance(binary.left, schema.Column) and binary.left.primary_key: </span><span class="cx"> if dependent[0] is binary.left.table: </span><del>- raise ArgumentError("bidirectional dependency not supported...specify foreignkey") </del><ins>+ raise exceptions.ArgumentError("bidirectional dependency not supported...specify foreignkey") </ins><span class="cx"> dependent[0] = binary.right.table </span><span class="cx"> self.foreignkey= binary.right </span><span class="cx"> elif isinstance(binary.right, schema.Column) and binary.right.primary_key: </span><span class="cx"> if dependent[0] is binary.right.table: </span><del>- raise ArgumentError("bidirectional dependency not supported...specify foreignkey") </del><ins>+ raise exceptions.ArgumentError("bidirectional dependency not supported...specify foreignkey") </ins><span class="cx"> dependent[0] = binary.left.table </span><span class="cx"> self.foreignkey = binary.left </span><span class="cx"> visitor = BinaryVisitor(foo) </span><span class="cx"> self.primaryjoin.accept_visitor(visitor) </span><span class="cx"> if dependent[0] is None: </span><del>- raise ArgumentError("cant determine primary foreign key in the join relationship....specify foreignkey=<column> or foreignkey=[<columns>]") </del><ins>+ raise exceptions.ArgumentError("cant determine primary foreign key in the join relationship....specify foreignkey=<column> or foreignkey=[<columns>]") </ins><span class="cx"> else: </span><span class="cx"> self.foreigntable = dependent[0] </span><span class="cx"> </span><span class="lines">@@ -622,7 +616,7 @@ </span><span class="cx"> row = self._row_decorator(row) </span><span class="cx"> return self.mapper._instance(session, row, imap, result_list) </span><span class="cx"> </span><del>-class GenericOption(MapperOption): </del><ins>+class GenericOption(mapper.MapperOption): </ins><span class="cx"> """a mapper option that can handle dotted property names, </span><span class="cx"> descending down through the relations of a mapper until it </span><span class="cx"> reaches the target.""" </span></span></pre></div> <a id="sqlalchemybranchesschemalibsqlalchemyormquerypy"></a> <div class="modfile"><h4>Modified: sqlalchemy/branches/schema/lib/sqlalchemy/orm/query.py (1400 => 1401)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/schema/lib/sqlalchemy/orm/query.py 2006-05-05 15:05:00 UTC (rev 1400) +++ sqlalchemy/branches/schema/lib/sqlalchemy/orm/query.py 2006-05-05 16:15:08 UTC (rev 1401) </span><span class="lines">@@ -6,11 +6,8 @@ </span><span class="cx"> </span><span class="cx"> </span><span class="cx"> import session as sessionlib </span><del>-import sqlalchemy.sql as sql -import sqlalchemy.util as util -from sqlalchemy.exceptions import * </del><ins>+from sqlalchemy import sql, util, exceptions </ins><span class="cx"> import mapper </span><del>-from sqlalchemy.exceptions import * </del><span class="cx"> </span><span class="cx"> class Query(object): </span><span class="cx"> """encapsulates the object-fetching operations provided by Mappers.""" </span><span class="lines">@@ -100,7 +97,7 @@ </span><span class="cx"> ret = self.select_whereclause(self._by_clause(*args, **params), limit=2) </span><span class="cx"> if len(ret) == 1: </span><span class="cx"> return ret[0] </span><del>- raise InvalidRequestError('Multiple rows returned for selectone_by') </del><ins>+ raise exceptions.InvalidRequestError('Multiple rows returned for selectone_by') </ins><span class="cx"> </span><span class="cx"> def count_by(self, *args, **params): </span><span class="cx"> """returns the count of instances based on the given clauses and key/value criterion. </span><span class="lines">@@ -122,7 +119,7 @@ </span><span class="cx"> ret = list(self.select(*args, **params)[0:2]) </span><span class="cx"> if len(ret) == 1: </span><span class="cx"> return ret[0] </span><del>- raise InvalidRequestError('Multiple rows returned for selectone') </del><ins>+ raise exceptions.InvalidRequestError('Multiple rows returned for selectone') </ins><span class="cx"> </span><span class="cx"> def select(self, arg=None, **kwargs): </span><span class="cx"> """selects instances of the object from the database. </span><span class="lines">@@ -199,7 +196,7 @@ </span><span class="cx"> continue </span><span class="cx"> c = self._get_criterion(self.mapper, key, value) </span><span class="cx"> if c is None: </span><del>- raise InvalidRequestError("Cant find criterion for property '"+ key + "'") </del><ins>+ raise exceptions.InvalidRequestError("Cant find criterion for property '"+ key + "'") </ins><span class="cx"> if clause is None: </span><span class="cx"> clause = c </span><span class="cx"> else: </span></span></pre></div> <a id="sqlalchemybranchesschemalibsqlalchemyormsessionpy"></a> <div class="modfile"><h4>Modified: sqlalchemy/branches/schema/lib/sqlalchemy/orm/session.py (1400 => 1401)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/schema/lib/sqlalchemy/orm/session.py 2006-05-05 15:05:00 UTC (rev 1400) +++ sqlalchemy/branches/schema/lib/sqlalchemy/orm/session.py 2006-05-05 16:15:08 UTC (rev 1401) </span><span class="lines">@@ -4,12 +4,10 @@ </span><span class="cx"> # This module is part of SQLAlchemy and is released under </span><span class="cx"> # the MIT License: http://www.opensource.org/licenses/mit-license.php </span><span class="cx"> </span><del>-from sqlalchemy import util -from sqlalchemy.exceptions import * </del><ins>+from sqlalchemy import util, exceptions, sql </ins><span class="cx"> import unitofwork, query </span><span class="cx"> import weakref </span><span class="cx"> import sqlalchemy </span><del>-import sqlalchemy.sql as sql </del><span class="cx"> </span><span class="cx"> class SessionTransaction(object): </span><span class="cx"> def __init__(self, session, parent=None, autoflush=True): </span><span class="lines">@@ -26,7 +24,7 @@ </span><span class="cx"> return SessionTransaction(self.session, self) </span><span class="cx"> def add(self, connection_or_engine): </span><span class="cx"> if self.connections.has_key(connection_or_engine.engine): </span><del>- raise InvalidRequestError("Session already has a Connection associated for the given Connection's Engine") </del><ins>+ raise exceptions.InvalidRequestError("Session already has a Connection associated for the given Connection's Engine") </ins><span class="cx"> return self.get_or_add(connection_or_engine) </span><span class="cx"> def get_or_add(self, connection_or_engine): </span><span class="cx"> # we reference the 'engine' attribute on the given object, which in the case of </span><span class="lines">@@ -222,10 +220,10 @@ </span><span class="cx"> </span><span class="cx"> def begin(self, *obj): </span><span class="cx"> """deprecated""" </span><del>- raise InvalidRequestError("Session.begin() is deprecated. use install_mod('legacy_session') to enable the old behavior") </del><ins>+ raise exceptions.InvalidRequestError("Session.begin() is deprecated. use install_mod('legacy_session') to enable the old behavior") </ins><span class="cx"> def commit(self, *obj): </span><span class="cx"> """deprecated""" </span><del>- raise InvalidRequestError("Session.commit() is deprecated. use install_mod('legacy_session') to enable the old behavior") </del><ins>+ raise exceptions.InvalidRequestError("Session.commit() is deprecated. use install_mod('legacy_session') to enable the old behavior") </ins><span class="cx"> </span><span class="cx"> def flush(self, objects=None): </span><span class="cx"> """flushes all the object modifications present in this session to the database. 'objects' </span><span class="lines">@@ -314,7 +312,7 @@ </span><span class="cx"> ident = mapper.identity(object) </span><span class="cx"> for k in ident: </span><span class="cx"> if k is None: </span><del>- raise InvalidRequestError("Instance '%s' does not have a full set of identity values, and does not represent a saved entity in the database. Use the add() method to add unsaved instances to this Session." % repr(obj)) </del><ins>+ raise exceptions.InvalidRequestError("Instance '%s' does not have a full set of identity values, and does not represent a saved entity in the database. Use the add() method to add unsaved instances to this Session." % repr(obj)) </ins><span class="cx"> key = mapper.identity_key(ident) </span><span class="cx"> u = self.uow </span><span class="cx"> if u.identity_map.has_key(key): </span><span class="lines">@@ -330,7 +328,7 @@ </span><span class="cx"> def _save_impl(self, object, **kwargs): </span><span class="cx"> if hasattr(object, '_instance_key'): </span><span class="cx"> if not self.uow.has_key(object._instance_key): </span><del>- raise InvalidRequestError("Instance '%s' is already persistent in a different Session" % repr(object)) </del><ins>+ raise exceptions.InvalidRequestError("Instance '%s' is already persistent in a different Session" % repr(object)) </ins><span class="cx"> else: </span><span class="cx"> entity_name = kwargs.get('entity_name', None) </span><span class="cx"> if entity_name is not None: </span><span class="lines">@@ -342,7 +340,7 @@ </span><span class="cx"> if self._is_attached(object) and object not in self.deleted: </span><span class="cx"> return </span><span class="cx"> if not hasattr(object, '_instance_key'): </span><del>- raise InvalidRequestError("Instance '%s' is not persisted" % repr(object)) </del><ins>+ raise exceptions.InvalidRequestError("Instance '%s' is not persisted" % repr(object)) </ins><span class="cx"> if global_attributes.is_modified(object): </span><span class="cx"> self._register_dirty(object) </span><span class="cx"> else: </span><span class="lines">@@ -366,7 +364,7 @@ </span><span class="cx"> if getattr(obj, '_sa_session_id', None) != self.hash_key: </span><span class="cx"> old = getattr(obj, '_sa_session_id', None) </span><span class="cx"> if old is not None: </span><del>- raise InvalidRequestError("Object '%s' is already attached to session '%s'" % (repr(obj), old)) </del><ins>+ raise exceptions.InvalidRequestError("Object '%s' is already attached to session '%s'" % (repr(obj), old)) </ins><span class="cx"> </span><span class="cx"> # auto-removal from the old session is disabled. but if we decide to </span><span class="cx"> # turn it back on, do it as below: gingerly since _sessions is a WeakValueDict </span><span class="lines">@@ -384,7 +382,7 @@ </span><span class="cx"> </span><span class="cx"> def _unattach(self, obj): </span><span class="cx"> if not self._is_attached(obj): #getattr(obj, '_sa_session_id', None) != self.hash_key: </span><del>- raise InvalidRequestError("Object '%s' is not attached to this Session" % repr(obj)) </del><ins>+ raise exceptions.InvalidRequestError("Object '%s' is not attached to this Session" % repr(obj)) </ins><span class="cx"> del obj._sa_session_id </span><span class="cx"> </span><span class="cx"> def _is_attached(self, obj): </span><span class="lines">@@ -416,9 +414,6 @@ </span><span class="cx"> def get_row_key(row, class_, primary_key, entity_name=None): </span><span class="cx"> return Session.get_row_key(row, class_, primary_key, entity_name) </span><span class="cx"> </span><del>-def mapper(*args, **params): - return sqlalchemy.orm.mapper(*args, **params) - </del><span class="cx"> def object_mapper(obj, **kwargs): </span><span class="cx"> return sqlalchemy.orm.object_mapper(obj, **kwargs) </span><span class="cx"> </span><span class="lines">@@ -448,9 +443,9 @@ </span><span class="cx"> s = current_session(obj) </span><span class="cx"> if s is None: </span><span class="cx"> if obj is None: </span><del>- raise InvalidRequestError("No global-level Session context is established. Use 'import sqlalchemy.mods.threadlocal' to establish a default thread-local context.") </del><ins>+ raise exceptions.InvalidRequestError("No global-level Session context is established. Use 'import sqlalchemy.mods.threadlocal' to establish a default thread-local context.") </ins><span class="cx"> else: </span><del>- raise InvalidRequestError("No Session context is established for class '%s', and no global-level Session context is established. Use 'import sqlalchemy.mods.threadlocal' to establish a default thread-local context." % (obj.__class__)) </del><ins>+ raise exceptions.InvalidRequestError("No Session context is established for class '%s', and no global-level Session context is established. Use 'import sqlalchemy.mods.threadlocal' to establish a default thread-local context." % (obj.__class__)) </ins><span class="cx"> return s </span><span class="cx"> </span><span class="cx"> def _default_session(obj=None): </span></span></pre></div> <a id="sqlalchemybranchesschemalibsqlalchemypoolpy"></a> <div class="modfile"><h4>Modified: sqlalchemy/branches/schema/lib/sqlalchemy/pool.py (1400 => 1401)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/schema/lib/sqlalchemy/pool.py 2006-05-05 15:05:00 UTC (rev 1400) +++ sqlalchemy/branches/schema/lib/sqlalchemy/pool.py 2006-05-05 16:15:08 UTC (rev 1401) </span><span class="lines">@@ -199,12 +199,13 @@ </span><span class="cx"> </span><span class="cx"> class QueuePool(Pool): </span><span class="cx"> """uses Queue.Queue to maintain a fixed-size list of connections.""" </span><del>- def __init__(self, creator, pool_size = 5, max_overflow = 10, **params): </del><ins>+ def __init__(self, creator, pool_size = 5, max_overflow = 10, timeout=30, **params): </ins><span class="cx"> Pool.__init__(self, **params) </span><span class="cx"> self._creator = creator </span><span class="cx"> self._pool = Queue.Queue(pool_size) </span><span class="cx"> self._overflow = 0 - pool_size </span><span class="cx"> self._max_overflow = max_overflow </span><ins>+ self._timeout = timeout </ins><span class="cx"> </span><span class="cx"> def do_return_conn(self, conn): </span><span class="cx"> try: </span><span class="lines">@@ -218,7 +219,7 @@ </span><span class="cx"> </span><span class="cx"> def do_get(self): </span><span class="cx"> try: </span><del>- return self._pool.get(self._max_overflow > -1 and self._overflow >= self._max_overflow) </del><ins>+ return self._pool.get(self._max_overflow > -1 and self._overflow >= self._max_overflow, self._timeout) </ins><span class="cx"> except Queue.Empty: </span><span class="cx"> self._overflow += 1 </span><span class="cx"> return self._creator() </span></span></pre></div> <a id="sqlalchemybranchesschematestmapperpy"></a> <div class="modfile"><h4>Modified: sqlalchemy/branches/schema/test/mapper.py (1400 => 1401)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/schema/test/mapper.py 2006-05-05 15:05:00 UTC (rev 1400) +++ sqlalchemy/branches/schema/test/mapper.py 2006-05-05 16:15:08 UTC (rev 1401) </span><span class="lines">@@ -2,8 +2,8 @@ </span><span class="cx"> import testbase </span><span class="cx"> import unittest, sys, os </span><span class="cx"> from sqlalchemy import * </span><ins>+import sqlalchemy.exceptions as exceptions </ins><span class="cx"> </span><del>- </del><span class="cx"> from tables import * </span><span class="cx"> import tables </span><span class="cx"> </span><span class="lines">@@ -255,7 +255,7 @@ </span><span class="cx"> 'user_name' : relation(mapper(Address, addresses)), </span><span class="cx"> }) </span><span class="cx"> self.assert_(False, "should have raised ArgumentError") </span><del>- except ArgumentError, e: </del><ins>+ except exceptions.ArgumentError, e: </ins><span class="cx"> self.assert_(True) </span><span class="cx"> </span><span class="cx"> clear_mappers() </span></span></pre></div> <a id="sqlalchemybranchesschematestpoolpy"></a> <div class="modfile"><h4>Modified: sqlalchemy/branches/schema/test/pool.py (1400 => 1401)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/schema/test/pool.py 2006-05-05 15:05:00 UTC (rev 1400) +++ sqlalchemy/branches/schema/test/pool.py 2006-05-05 16:15:08 UTC (rev 1401) </span><span class="lines">@@ -1,5 +1,5 @@ </span><span class="cx"> from testbase import PersistTest </span><del>-import unittest, sys, os </del><ins>+import unittest, sys, os, time </ins><span class="cx"> </span><span class="cx"> from pysqlite2 import dbapi2 as sqlite </span><span class="cx"> import sqlalchemy.pool as pool </span><span class="lines">@@ -90,6 +90,15 @@ </span><span class="cx"> c2 = None </span><span class="cx"> self.assert_(status(p) == (3, 2, 0, 1)) </span><span class="cx"> </span><ins>+ def test_timeout(self): + p = pool.QueuePool(creator = lambda: sqlite.connect('foo.db'), pool_size = 3, max_overflow = 0, use_threadlocal = False, echo = False, timeout=2) + c1 = p.get() + c2 = p.get() + c3 = p.get() + now = time.time() + c4 = p.get() + assert int(time.time() - now) == 2 + </ins><span class="cx"> def testthreadlocal_del(self): </span><span class="cx"> self._do_testthreadlocal(useclose=False) </span><span class="cx"> </span></span></pre> </div> </div> </body> </html> |
From: <co...@sq...> - 2006-05-05 15:05:14
|
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head><style type="text/css"><!-- #msg dl { border: 1px #006 solid; background: #369; padding: 6px; color: #fff; } #msg dt { float: left; width: 6em; font-weight: bold; } #msg dt:after { content:':';} #msg dl, #msg dt, #msg ul, #msg li { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; } #msg dl a { font-weight: bold} #msg dl a:link { color:#fc3; } #msg dl a:active { color:#ff0; } #msg dl a:visited { color:#cc6; } h3 { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; font-weight: bold; } #msg pre { overflow: auto; background: #ffc; border: 1px #fc0 solid; padding: 6px; } #msg ul, pre { overflow: auto; } #patch { width: 100%; } #patch h4 {font-family: verdana,arial,helvetica,sans-serif;font-size:10pt;padding:8px;background:#369;color:#fff;margin:0;} #patch .propset h4, #patch .binary h4 {margin:0;} #patch pre {padding:0;line-height:1.2em;margin:0;} #patch .diff {width:100%;background:#eee;padding: 0 0 10px 0;overflow:auto;} #patch .propset .diff, #patch .binary .diff {padding:10px 0;} #patch span {display:block;padding:0 10px;} #patch .modfile, #patch .addfile, #patch .delfile, #patch .propset, #patch .binary, #patch .copfile {border:1px solid #ccc;margin:10px 0;} #patch ins {background:#dfd;text-decoration:none;display:block;padding:0 10px;} #patch del {background:#fdd;text-decoration:none;display:block;padding:0 10px;} #patch .lines, .info {color:#888;background:#fff;} --></style> <title>[1400] sqlalchemy/branches/schema/lib/sqlalchemy/orm/query.py: fix to the from_obj fix</title> </head> <body> <div id="msg"> <dl> <dt>Revision</dt> <dd>1400</dd> <dt>Author</dt> <dd>zzzeek</dd> <dt>Date</dt> <dd>2006-05-05 10:05:00 -0500 (Fri, 05 May 2006)</dd> </dl> <h3>Log Message</h3> <pre>fix to the from_obj fix</pre> <h3>Modified Paths</h3> <ul> <li><a href="#sqlalchemybranchesschemalibsqlalchemyormquerypy">sqlalchemy/branches/schema/lib/sqlalchemy/orm/query.py</a></li> </ul> </div> <div id="patch"> <h3>Diff</h3> <a id="sqlalchemybranchesschemalibsqlalchemyormquerypy"></a> <div class="modfile"><h4>Modified: sqlalchemy/branches/schema/lib/sqlalchemy/orm/query.py (1399 => 1400)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/schema/lib/sqlalchemy/orm/query.py 2006-05-05 01:49:22 UTC (rev 1399) +++ sqlalchemy/branches/schema/lib/sqlalchemy/orm/query.py 2006-05-05 15:05:00 UTC (rev 1400) </span><span class="lines">@@ -245,7 +245,7 @@ </span><span class="cx"> </span><span class="cx"> def _compile(self, whereclause = None, **kwargs): </span><span class="cx"> order_by = kwargs.pop('order_by', False) </span><del>- from_obj = kwargs.pop('from_obj', False) </del><ins>+ from_obj = kwargs.pop('from_obj', []) </ins><span class="cx"> if order_by is False: </span><span class="cx"> order_by = self.order_by </span><span class="cx"> if order_by is False: </span></span></pre> </div> </div> </body> </html> |
From: <co...@sq...> - 2006-05-05 07:29:13
|
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head><style type="text/css"><!-- #msg dl { border: 1px #006 solid; background: #369; padding: 6px; color: #fff; } #msg dt { float: left; width: 6em; font-weight: bold; } #msg dt:after { content:':';} #msg dl, #msg dt, #msg ul, #msg li { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; } #msg dl a { font-weight: bold} #msg dl a:link { color:#fc3; } #msg dl a:active { color:#ff0; } #msg dl a:visited { color:#cc6; } h3 { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; font-weight: bold; } #msg pre { overflow: auto; background: #ffc; border: 1px #fc0 solid; padding: 6px; } #msg ul, pre { overflow: auto; } #patch { width: 100%; } #patch h4 {font-family: verdana,arial,helvetica,sans-serif;font-size:10pt;padding:8px;background:#369;color:#fff;margin:0;} #patch .propset h4, #patch .binary h4 {margin:0;} #patch pre {padding:0;line-height:1.2em;margin:0;} #patch .diff {width:100%;background:#eee;padding: 0 0 10px 0;overflow:auto;} #patch .propset .diff, #patch .binary .diff {padding:10px 0;} #patch span {display:block;padding:0 10px;} #patch .modfile, #patch .addfile, #patch .delfile, #patch .propset, #patch .binary, #patch .copfile {border:1px solid #ccc;margin:10px 0;} #patch ins {background:#dfd;text-decoration:none;display:block;padding:0 10px;} #patch del {background:#fdd;text-decoration:none;display:block;padding:0 10px;} #patch .lines, .info {color:#888;background:#fff;} --></style> <title>[1399] sqlalchemy/branches/schema/doc/build/content: edits</title> </head> <body> <div id="msg"> <dl> <dt>Revision</dt> <dd>1399</dd> <dt>Author</dt> <dd>zzzeek</dd> <dt>Date</dt> <dd>2006-05-04 20:49:22 -0500 (Thu, 04 May 2006)</dd> </dl> <h3>Log Message</h3> <pre>edits</pre> <h3>Modified Paths</h3> <ul> <li><a href="#sqlalchemybranchesschemadocbuildcontentadv_datamappingtxt">sqlalchemy/branches/schema/doc/build/content/adv_datamapping.txt</a></li> <li><a href="#sqlalchemybranchesschemadocbuildcontentdatamappingtxt">sqlalchemy/branches/schema/doc/build/content/datamapping.txt</a></li> </ul> </div> <div id="patch"> <h3>Diff</h3> <a id="sqlalchemybranchesschemadocbuildcontentadv_datamappingtxt"></a> <div class="modfile"><h4>Modified: sqlalchemy/branches/schema/doc/build/content/adv_datamapping.txt (1398 => 1399)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/schema/doc/build/content/adv_datamapping.txt 2006-05-05 01:20:15 UTC (rev 1398) +++ sqlalchemy/branches/schema/doc/build/content/adv_datamapping.txt 2006-05-05 01:49:22 UTC (rev 1399) </span><span class="lines">@@ -42,28 +42,8 @@ </span><span class="cx"> </span><span class="cx"> ### More On Mapper Properties {@name=properties} </span><span class="cx"> </span><del>-#### Overriding Properties {@name=overriding} </del><ins>+#### Overriding Column Names {@name=colname} </ins><span class="cx"> </span><del>-A common request is the ability to create custom class properties that override the behavior of setting/getting an attribute. Currently, the easiest way to do this in SQLAlchemy is how it would be done in any Python program; define your attribute with a different name, such as "_attribute", and use a property to get/set its value. The mapper just needs to be told of the special name: - - {python} - class MyClass(object): - def _set_email(self, email): - self._email = email - def _get_email(self, email): - return self._email - email = property(_get_email, _set_email) - - mapper(MyClass, mytable, properties = { - # map the '_email' attribute to the "email" column - # on the table - '_email': mytable.c.email - }) - -In a later release, SQLAlchemy will also allow _get_email and _set_email to be attached directly to the "email" property created by the mapper, and will also allow this association to occur via decorators. - -### Overriding Column Names {@name=colname} - </del><span class="cx"> When mappers are constructed, by default the column names in the Table metadata are used as the names of attributes on the mapped class. This can be customzed within the properties by stating the key/column combinations explicitly: </span><span class="cx"> </span><span class="cx"> {python} </span><span class="lines">@@ -83,7 +63,26 @@ </span><span class="cx"> } </span><span class="cx"> ) </span><span class="cx"> </span><ins>+#### Overriding Properties {@name=overriding} </ins><span class="cx"> </span><ins>+A common request is the ability to create custom class properties that override the behavior of setting/getting an attribute. Currently, the easiest way to do this in SQLAlchemy is how it would be done in any Python program; define your attribute with a different name, such as "_attribute", and use a property to get/set its value. The mapper just needs to be told of the special name: + + {python} + class MyClass(object): + def _set_email(self, email): + self._email = email + def _get_email(self, email): + return self._email + email = property(_get_email, _set_email) + + mapper(MyClass, mytable, properties = { + # map the '_email' attribute to the "email" column + # on the table + '_email': mytable.c.email + }) + +In a later release, SQLAlchemy will also allow `_get_email` and `_set_email` to be attached directly to the "email" property created by the mapper, and will also allow this association to occur via decorators. + </ins><span class="cx"> #### Custom Join Conditions {@name=customjoin} </span><span class="cx"> </span><span class="cx"> When creating relations on a mapper, most examples so far have illustrated the mapper and relationship joining up based on the foreign keys of the tables they represent. in fact, this "automatic" inspection can be completely circumvented using the `primaryjoin` and `secondaryjoin` arguments to `relation`, as in this example which creates a User object which has a relationship to all of its Addresses which are in Boston: </span><span class="lines">@@ -108,71 +107,95 @@ </span><span class="cx"> pass </span><span class="cx"> class Keyword(object): </span><span class="cx"> pass </span><del>- Keyword.mapper = mapper(Keyword, keywords) - User.mapper = mapper(User, users_table, properties={ - 'keywords':relation(Keyword.mapper, - primaryjoin=users_table.c.user_id==userkeywords.c.user_id, - secondaryjoin=userkeywords.c.keyword_id==keywords.c.keyword_id </del><ins>+ mapper(Keyword, keywords_table) + mapper(User, users_table, properties={ + 'keywords':relation(Keyword, secondary=userkeywords_table + primaryjoin=users_table.c.user_id==userkeywords_table.c.user_id, + secondaryjoin=userkeywords_table.c.keyword_id==keywords_table.c.keyword_id </ins><span class="cx"> ) </span><span class="cx"> }) </span><span class="cx"> </span><del>-### Lazy/Eager Joins Multiple Times to One Table {@name=multiplejoin} </del><ins>+#### Lazy/Eager Joins Multiple Times to One Table {@name=multiplejoin} </ins><span class="cx"> </span><del>-The previous example leads in to the idea of joining against the same table multiple times. Below is a User object that has lists of its Boston and New York addresses, both lazily loaded when they are first accessed: </del><ins>+The previous example leads in to the idea of joining against the same table multiple times. Below is a User object that has lists of its Boston and New York addresses: </ins><span class="cx"> </span><span class="cx"> {python} </span><del>- User.mapper = mapper(User, users_table, properties={ - 'boston_addreses' : relation(Address.mapper, primaryjoin= </del><ins>+ mapper(User, users_table, properties={ + 'boston_addreses' : relation(Address, primaryjoin= </ins><span class="cx"> and_(users_table.c.user_id==Address.c.user_id, </span><span class="cx"> Addresses.c.city=='Boston')), </span><del>- 'newyork_addresses' : relation(Address.mapper, primaryjoin= </del><ins>+ 'newyork_addresses' : relation(Address, primaryjoin= </ins><span class="cx"> and_(users_table.c.user_id==Address.c.user_id, </span><span class="cx"> Addresses.c.city=='New York')), </span><span class="cx"> }) </span><del>- -A complication arises with the above pattern if you want the relations to be eager loaded. Since there will be two separate joins to the addresses table during an eager load, an alias needs to be used to separate them. You can create an alias of the addresses table to separate them, but then you are in effect creating a brand new mapper for each property, unrelated to the main Address mapper, which can create problems with commit operations. So an additional argument `use_alias` can be used with an eager relationship to specify the alias to be used just within the eager query: </del><span class="cx"> </span><ins>+Both lazy and eager loading support multiple joins equally well. + +#### Deferred Column Loading {@name=deferred} + +This feature allows particular columns of a table to not be loaded by default, instead being loaded later on when first referenced. It is essentailly "column-level lazy loading". This feature is useful when one wants to avoid loading a large text or binary field into memory when its not needed. Individual columns can be lazy loaded by themselves or placed into groups that lazy-load together. + </ins><span class="cx"> {python} </span><del>- User.mapper = mapper(User, users_table, properties={ - 'boston_addreses' : relation(Address.mapper, primaryjoin= - and_(User.c.user_id==Address.c.user_id, - Addresses.c.city=='Boston'), lazy=False, use_alias=True), - 'newyork_addresses' : relation(Address.mapper, primaryjoin= - and_(User.c.user_id==Address.c.user_id, - Addresses.c.city=='New York'), lazy=False, use_alias=True), </del><ins>+ book_excerpts = Table('books', db, + Column('book_id', Integer, primary_key=True), + Column('title', String(200), nullable=False), + Column('summary', String(2000)), + Column('excerpt', String), + Column('photo', Binary) + ) + + class Book(object): + pass + + # define a mapper that will load each of 'excerpt' and 'photo' in + # separate, individual-row SELECT statements when each attribute + # is first referenced on the individual object instance + mapper(Book, book_excerpts, properties = { + 'excerpt' : deferred(book_excerpts.c.excerpt), + 'photo' : deferred(book_excerpts.c.photo) </ins><span class="cx"> }) </span><del>- - {sql}u = User.mapper.select() - SELECT users.user_id AS users_user_id, users.user_name AS users_user_name, - users.password AS users_password, - addresses_EF45.address_id AS addresses_EF45_address_id, addresses_EF45.user_id AS addresses_EF45_user_id, - addresses_EF45.street AS addresses_EF45_street, addresses_EF45.city AS addresses_EF45_city, - addresses_EF45.state AS addresses_EF45_state, addresses_EF45.zip AS addresses_EF45_zip, - addresses_63C5.address_id AS addresses_63C5_address_id, addresses_63C5.user_id AS addresses_63C5_user_id, - addresses_63C5.street AS addresses_63C5_street, addresses_63C5.city AS addresses_63C5_city, - addresses_63C5.state AS addresses_63C5_state, addresses_63C5.zip AS addresses_63C5_zip - FROM users - LEFT OUTER JOIN addresses AS addresses_EF45 ON users.user_id = addresses_EF45.user_id - AND addresses_EF45.city = :addresses_city - LEFT OUTER JOIN addresses AS addresses_63C5 ON users.user_id = addresses_63C5.user_id - AND addresses_63C5.city = :addresses_city_1 - ORDER BY users.oid, addresses_EF45.oid, addresses_63C5.oid - {'addresses_city_1': 'New York', 'addresses_city': 'Boston'} </del><span class="cx"> </span><ins>+Deferred columns can be placed into groups so that they load together: + + {python} + book_excerpts = Table('books', db, + Column('book_id', Integer, primary_key=True), + Column('title', String(200), nullable=False), + Column('summary', String(2000)), + Column('excerpt', String), + Column('photo1', Binary), + Column('photo2', Binary), + Column('photo3', Binary) + ) + + class Book(object): + pass + + # define a mapper with a 'photos' deferred group. when one photo is referenced, + # all three photos will be loaded in one SELECT statement. The 'excerpt' will + # be loaded separately when it is first referenced. + mapper(Book, book_excerpts, properties = { + 'excerpt' : deferred(book_excerpts.c.excerpt), + 'photo1' : deferred(book_excerpts.c.photo1, group='photos'), + 'photo2' : deferred(book_excerpts.c.photo2, group='photos'), + 'photo3' : deferred(book_excerpts.c.photo3, group='photos') + }) + </ins><span class="cx"> #### Relation Options {@name=relationoptions} </span><span class="cx"> </span><span class="cx"> Keyword options to the `relation` function include: </span><span class="cx"> </span><del>-* lazy=(True|False|None) - specifies how the related items should be loaded. a value of True indicates they should be loaded when the property is first accessed. A value of False indicates they should be loaded by joining against the parent object query, so parent and child are loaded in one round trip. A value of None indicates the related items are not loaded by the mapper in any case; the application will manually insert items into the list in some other way. A relationship with lazy=None is still important; items added to the list or removed will cause the appropriate updates and deletes upon commit(). </del><ins>+* lazy=(True|False|None) - specifies how the related items should be loaded. a value of True indicates they should be loaded when the property is first accessed. A value of False indicates they should be loaded by joining against the parent object query, so parent and child are loaded in one round trip. A value of None indicates the related items are not loaded by the mapper in any case; the application will manually insert items into the list in some other way. A relationship with lazy=None is still important; items added to the list or removed will cause the appropriate updates and deletes upon flush(). Future capabilities for lazy might also include "lazy='extra'", which would allow lazy loading of child elements one at a time, for very large collections. +* cascade - a string list of **cascade rules** which determines how persistence operations should be "cascaded" from parent to child. For a description of cascade rules, see [datamapping_relations_cycle](rel:datamapping_relations_lifecycle) and [unitofwork_cascade](rel:unitofwork_cascade). </ins><span class="cx"> * secondary - for a many-to-many relationship, specifies the intermediary table. </span><span class="cx"> * primaryjoin - a ClauseElement that will be used as the primary join of this child object against the parent object, or in a many-to-many relationship the join of the primary object to the association table. By default, this value is computed based on the foreign key relationships of the parent and child tables (or association table). </span><span class="cx"> * secondaryjoin - a ClauseElement that will be used as the join of an association table to the child object. By default, this value is computed based on the foreign key relationships of the association and child tables. </span><del>-* foreignkey - specifies which column in this relationship is "foreign", i.e. which column refers to the parent object. This value is automatically determined in all cases, based on the primary and secondary join conditions, except in the case of a self-referential mapper, where it is needed to indicate the child object's reference back to it's parent. -* uselist - a boolean that indicates if this property should be loaded as a list or a scalar. In most cases, this value is determined based on the type and direction of the relationship - one to many forms a list, one to one forms a scalar, many to many is a list. If a scalar is desired where normally a list would be present, set uselist to False. -* private - indicates if these child objects are "private" to the parent; removed items will also be deleted, and if the parent item is deleted, all child objects are deleted as well. See the example in [datamapping_relations_private](rel:datamapping_relations_private). -* backreference - indicates the name of a property to be placed on the related mapper's class that will handle this relationship in the other direction, including synchronizing the object attributes on both sides of the relation. See the example in [datamapping_relations_backreferences](rel:datamapping_relations_backreferences). </del><ins>+* foreignkey - specifies which column in this relationship is "foreign", i.e. which column refers to the parent object. This value is automatically determined in most cases based on the primary and secondary join conditions, except in the case of a self-referential mapper, where it is needed to indicate the child object's reference back to it's parent, or in the case where the join conditions do not represent any primary key columns to properly represent the direction of the relationship. +* uselist - a boolean that indicates if this property should be loaded as a list or a scalar. In most cases, this value is determined based on the type and direction of the relationship - one to many forms a list, many to one forms a scalar, many to many is a list. If a scalar is desired where normally a list would be present, such as a bi-directional one-to-one relationship, set uselist to False. +* private - setting `private=True` is the equivalent of setting `cascade="all, delete-orphan"`, and indicates the lifecycle of child objects should be contained within that of the parent. See the example in [datamapping_relations_cycle](rel:datamapping_relations_lifecycle). +* backref - indicates the name of a property to be placed on the related mapper's class that will handle this relationship in the other direction, including synchronizing the object attributes on both sides of the relation. Can also point to a `backref()` construct for more configurability. See [datamapping_relations_backreferences](rel:datamapping_relations_backreferences). </ins><span class="cx"> * order_by - indicates the ordering that should be applied when loading these items. See the section [advdatamapping_orderby](rel:advdatamapping_orderby) for details. </span><del>-* association - When specifying a many to many relationship with an association object, this keyword should reference the mapper of the target object of the association. See the example in [datamapping_association](rel:datamapping_association). </del><ins>+* association - When specifying a many to many relationship with an association object, this keyword should reference the mapper or class of the target object of the association. See the example in [datamapping_association](rel:datamapping_association). </ins><span class="cx"> * post_update - this indicates that the relationship should be handled by a second UPDATE statement after an INSERT, or before a DELETE. using this flag essentially means the relationship will not incur any "dependency" between parent and child item, as the particular foreign key relationship between them is handled by a second statement. use this flag when a particular mapping arrangement will incur two rows that are dependent on each other, such as a table that has a one-to-many relationship to a set of child rows, and also has a column that references a single child row within that list (i.e. both tables contain a foreign key to each other). If a flush() operation returns an error that a "cyclical dependency" was detected, this is a cue that you might want to use post_update. </span><span class="cx"> </span><span class="cx"> ### Controlling Ordering {@name=orderby} </span><span class="lines">@@ -254,57 +277,7 @@ </span><span class="cx"> </span><span class="cx"> The main WHERE clause as well as the limiting clauses are coerced into a subquery; this subquery represents the desired result of objects. A containing query, which handles the eager relationships, is joined against the subquery to produce the result. </span><span class="cx"> </span><del>- -### Deferred Column Loading {@name=deferred} - -This feature allows particular columns of a table to not be loaded by default, instead being loaded later on when first referenced. It is essentailly "column-level lazy loading". This feature is useful when one wants to avoid loading a large text or binary field into memory when its not needed. Individual columns can be lazy loaded by themselves or placed into groups that lazy-load together. - - {python} - book_excerpts = Table('books', db, - Column('book_id', Integer, primary_key=True), - Column('title', String(200), nullable=False), - Column('summary', String(2000)), - Column('excerpt', String), - Column('photo', Binary) - ) - - class Book(object): - pass - - # define a mapper that will load each of 'excerpt' and 'photo' in - # separate, individual-row SELECT statements when each attribute - # is first referenced on the individual object instance - mapper(Book, book_excerpts, properties = { - 'excerpt' : deferred(book_excerpts.c.excerpt), - 'photo' : deferred(book_excerpts.c.photo) - }) - -Deferred columns can be placed into groups so that they load together: - - {python} - book_excerpts = Table('books', db, - Column('book_id', Integer, primary_key=True), - Column('title', String(200), nullable=False), - Column('summary', String(2000)), - Column('excerpt', String), - Column('photo1', Binary), - Column('photo2', Binary), - Column('photo3', Binary) - ) - - class Book(object): - pass - - # define a mapper with a 'photos' deferred group. when one photo is referenced, - # all three photos will be loaded in one SELECT statement. The 'excerpt' will - # be loaded separately when it is first referenced. - mapper(Book, book_excerpts, properties = { - 'excerpt' : deferred(book_excerpts.c.excerpt), - 'photo1' : deferred(book_excerpts.c.photo1, group='photos'), - 'photo2' : deferred(book_excerpts.c.photo2, group='photos'), - 'photo3' : deferred(book_excerpts.c.photo3, group='photos') - }) - </del><ins>+ </ins><span class="cx"> ### More on Mapper Options {@name=options} </span><span class="cx"> </span><span class="cx"> The `options` method on the `Query` object, first introduced in [datamapping_relations_options](rel:datamapping_relations_options), produces a new `Query` object by creating a copy of the underlying `Mapper` and placing modified properties on it. The `options` method is also directly available off the `Mapper` object itself, so that the newly copied `Mapper` can be dealt with directly. The `options` method takes a variable number of `MapperOption` objects which know how to change specific things about the mapper. The five available options are `eagerload`, `lazyload`, `noload`, `deferred` and `extension`. </span><span class="lines">@@ -592,7 +565,7 @@ </span><span class="cx"> TreeNode.mapper = mapper(TreeNode, trees, properties={ </span><span class="cx"> 'children' : relation( </span><span class="cx"> TreeNode, </span><del>- private=True </del><ins>+ cascade="all, delete-orphan" </ins><span class="cx"> ), </span><span class="cx"> } </span><span class="cx"> ) </span><span class="lines">@@ -602,7 +575,7 @@ </span><span class="cx"> </span><span class="cx"> mymapper.add_property('children', relation( </span><span class="cx"> mymapper, </span><del>- private=True </del><ins>+ cascade="all, delete-orphan" </ins><span class="cx"> )) </span><span class="cx"> </span><span class="cx"> This kind of mapper goes through a lot of extra effort when saving and deleting items, to determine the correct dependency graph of nodes within the tree. </span><span class="lines">@@ -627,7 +600,7 @@ </span><span class="cx"> 'children' : relation( </span><span class="cx"> TreeNode, </span><span class="cx"> primaryjoin=trees.c.parent_node_id==trees.c.node_id </span><del>- private=True </del><ins>+ cascade="all, delete-orphan" </ins><span class="cx"> ), </span><span class="cx"> 'root' : relation( </span><span class="cx"> TreeNode, </span></span></pre></div> <a id="sqlalchemybranchesschemadocbuildcontentdatamappingtxt"></a> <div class="modfile"><h4>Modified: sqlalchemy/branches/schema/doc/build/content/datamapping.txt (1398 => 1399)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/schema/doc/build/content/datamapping.txt 2006-05-05 01:20:15 UTC (rev 1398) +++ sqlalchemy/branches/schema/doc/build/content/datamapping.txt 2006-05-05 01:49:22 UTC (rev 1399) </span><span class="lines">@@ -287,15 +287,15 @@ </span><span class="cx"> </span><span class="cx"> Note that when creating a relation with the `relation()` function, the target can either be a class, in which case the primary mapper for that class is used as the target, or a `Mapper` instance itself, as returned by the `mapper()` function. </span><span class="cx"> </span><del>-#### Useful Feature: Private Relations {@name=private} </del><ins>+#### Useful Feature: Lifecycle Relations {@name=lifecycle} </ins><span class="cx"> </span><del>-So our one address that was removed from the list, was updated to have a user_id of `None`, and a new address object was inserted to correspond to the new Address added to the User. But now, theres a mailing address with no user_id floating around in the database of no use to anyone. How can we avoid this ? This is acheived by using the `private=True` parameter of `relation`: </del><ins>+So our one address that was removed from the list, was updated to have a user_id of `None`, and a new address object was inserted to correspond to the new Address added to the User. But now, theres a mailing address with no user_id floating around in the database of no use to anyone. How can we avoid this ? This is acheived by using the `cascade` parameter of `relation`: </ins><span class="cx"> </span><span class="cx"> {python} </span><span class="cx"> clear_mappers() # clear mappers from the previous example </span><span class="cx"> mapper(Address, addresses_table) </span><span class="cx"> mapper(User, users_table, properties = { </span><del>- 'addresses' : relation(Address, private=True) </del><ins>+ 'addresses' : relation(Address, cascade="all, delete-orphan") </ins><span class="cx"> } </span><span class="cx"> ) </span><span class="cx"> </span><span class="lines">@@ -309,9 +309,9 @@ </span><span class="cx"> DELETE FROM addresses WHERE addresses.address_id = :address_id </span><span class="cx"> [{'address_id': 2}] </span><span class="cx"> </span><del>-In this case, with the private flag set, the element that was removed from the addresses list was also removed from the database. The "private" flag indicates that the Address object is a *lifecycle object* of User. </del><ins>+In this case, with `delete-orphan` set, the element that was removed from the addresses list was also removed from the database. The `delete-orphan` cascade rule indicates that the lifecycle of an `Address` object bounded by that of the `User`. </ins><span class="cx"> </span><del>-`private` is also a synonym for a more configurable set of rules called **cascade rules**. Cascading is described in [session_cascade](rel:session_cascade). </del><ins>+Cascading is described fully in [unitofwork_cascade](rel:unitofwork_cascade). </ins><span class="cx"> </span><span class="cx"> #### Useful Feature: Backreferences {@name=backreferences} </span><span class="cx"> </span><span class="lines">@@ -347,7 +347,7 @@ </span><span class="cx"> </span><span class="cx"> mapper(User, users, properties = { </span><span class="cx"> 'addresses' : relation(Address, </span><del>- backref=backref('user', lazy=False, private=True) </del><ins>+ backref=backref('user', lazy=False, cascade="all, delete-orphan") </ins><span class="cx"> ) </span><span class="cx"> } </span><span class="cx"> ) </span><span class="lines">@@ -359,7 +359,7 @@ </span><span class="cx"> {python} </span><span class="cx"> # define a mapper </span><span class="cx"> mapper(User, users_table, properties = { </span><del>- 'addresses' : relation(mapper(Address, addresses_table), private=True) </del><ins>+ 'addresses' : relation(mapper(Address, addresses_table), cascade="all,delete-orphan") </ins><span class="cx"> }) </span><span class="cx"> </span><span class="cx"> # select users where username is 'jane', get the first element of the list </span><span class="lines">@@ -517,7 +517,7 @@ </span><span class="cx"> mapper(UserPrefs, prefs_table) </span><span class="cx"> </span><span class="cx"> mapper(User, users_table, properties = dict( </span><del>- preferences = relation(UserPrefs, lazy=False, private=True), </del><ins>+ preferences = relation(UserPrefs, lazy=False, cascade="all, delete-orphan"), </ins><span class="cx"> )) </span><span class="cx"> </span><span class="cx"> # select </span></span></pre> </div> </div> </body> </html> |
From: <co...@sq...> - 2006-05-05 01:20:23
|
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head><style type="text/css"><!-- #msg dl { border: 1px #006 solid; background: #369; padding: 6px; color: #fff; } #msg dt { float: left; width: 6em; font-weight: bold; } #msg dt:after { content:':';} #msg dl, #msg dt, #msg ul, #msg li { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; } #msg dl a { font-weight: bold} #msg dl a:link { color:#fc3; } #msg dl a:active { color:#ff0; } #msg dl a:visited { color:#cc6; } h3 { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; font-weight: bold; } #msg pre { overflow: auto; background: #ffc; border: 1px #fc0 solid; padding: 6px; } #msg ul, pre { overflow: auto; } #patch { width: 100%; } #patch h4 {font-family: verdana,arial,helvetica,sans-serif;font-size:10pt;padding:8px;background:#369;color:#fff;margin:0;} #patch .propset h4, #patch .binary h4 {margin:0;} #patch pre {padding:0;line-height:1.2em;margin:0;} #patch .diff {width:100%;background:#eee;padding: 0 0 10px 0;overflow:auto;} #patch .propset .diff, #patch .binary .diff {padding:10px 0;} #patch span {display:block;padding:0 10px;} #patch .modfile, #patch .addfile, #patch .delfile, #patch .propset, #patch .binary, #patch .copfile {border:1px solid #ccc;margin:10px 0;} #patch ins {background:#dfd;text-decoration:none;display:block;padding:0 10px;} #patch del {background:#fdd;text-decoration:none;display:block;padding:0 10px;} #patch .lines, .info {color:#888;background:#fff;} --></style> <title>[1398] sqlalchemy/branches/schema/lib/sqlalchemy/orm/query.py: added from_obj parametrer</title> </head> <body> <div id="msg"> <dl> <dt>Revision</dt> <dd>1398</dd> <dt>Author</dt> <dd>zzzeek</dd> <dt>Date</dt> <dd>2006-05-04 20:20:15 -0500 (Thu, 04 May 2006)</dd> </dl> <h3>Log Message</h3> <pre>added from_obj parametrer</pre> <h3>Modified Paths</h3> <ul> <li><a href="#sqlalchemybranchesschemalibsqlalchemyormquerypy">sqlalchemy/branches/schema/lib/sqlalchemy/orm/query.py</a></li> </ul> </div> <div id="patch"> <h3>Diff</h3> <a id="sqlalchemybranchesschemalibsqlalchemyormquerypy"></a> <div class="modfile"><h4>Modified: sqlalchemy/branches/schema/lib/sqlalchemy/orm/query.py (1397 => 1398)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/schema/lib/sqlalchemy/orm/query.py 2006-05-05 01:18:55 UTC (rev 1397) +++ sqlalchemy/branches/schema/lib/sqlalchemy/orm/query.py 2006-05-05 01:20:15 UTC (rev 1398) </span><span class="lines">@@ -245,6 +245,7 @@ </span><span class="cx"> </span><span class="cx"> def _compile(self, whereclause = None, **kwargs): </span><span class="cx"> order_by = kwargs.pop('order_by', False) </span><ins>+ from_obj = kwargs.pop('from_obj', False) </ins><span class="cx"> if order_by is False: </span><span class="cx"> order_by = self.order_by </span><span class="cx"> if order_by is False: </span><span class="lines">@@ -252,7 +253,8 @@ </span><span class="cx"> order_by = self.table.default_order_by() </span><span class="cx"> </span><span class="cx"> if self._should_nest(**kwargs): </span><del>- s2 = sql.select(self.table.primary_key, whereclause, use_labels=True, from_obj=[self.table], **kwargs) </del><ins>+ from_obj.append(self.table) + s2 = sql.select(self.table.primary_key, whereclause, use_labels=True, from_obj=from_obj, **kwargs) </ins><span class="cx"> # raise "ok first thing", str(s2) </span><span class="cx"> if not kwargs.get('distinct', False) and order_by: </span><span class="cx"> s2.order_by(*util.to_list(order_by)) </span><span class="lines">@@ -265,7 +267,8 @@ </span><span class="cx"> if order_by: </span><span class="cx"> statement.order_by(*util.to_list(order_by)) </span><span class="cx"> else: </span><del>- statement = sql.select([], whereclause, from_obj=[self.table], use_labels=True, **kwargs) </del><ins>+ from_obj.append(self.table) + statement = sql.select([], whereclause, from_obj=from_obj, use_labels=True, **kwargs) </ins><span class="cx"> if order_by: </span><span class="cx"> statement.order_by(*util.to_list(order_by)) </span><span class="cx"> # for a DISTINCT query, you need the columns explicitly specified in order </span></span></pre> </div> </div> </body> </html> |
From: <co...@sq...> - 2006-05-05 01:19:05
|
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head><style type="text/css"><!-- #msg dl { border: 1px #006 solid; background: #369; padding: 6px; color: #fff; } #msg dt { float: left; width: 6em; font-weight: bold; } #msg dt:after { content:':';} #msg dl, #msg dt, #msg ul, #msg li { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; } #msg dl a { font-weight: bold} #msg dl a:link { color:#fc3; } #msg dl a:active { color:#ff0; } #msg dl a:visited { color:#cc6; } h3 { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; font-weight: bold; } #msg pre { overflow: auto; background: #ffc; border: 1px #fc0 solid; padding: 6px; } #msg ul, pre { overflow: auto; } #patch { width: 100%; } #patch h4 {font-family: verdana,arial,helvetica,sans-serif;font-size:10pt;padding:8px;background:#369;color:#fff;margin:0;} #patch .propset h4, #patch .binary h4 {margin:0;} #patch pre {padding:0;line-height:1.2em;margin:0;} #patch .diff {width:100%;background:#eee;padding: 0 0 10px 0;overflow:auto;} #patch .propset .diff, #patch .binary .diff {padding:10px 0;} #patch span {display:block;padding:0 10px;} #patch .modfile, #patch .addfile, #patch .delfile, #patch .propset, #patch .binary, #patch .copfile {border:1px solid #ccc;margin:10px 0;} #patch ins {background:#dfd;text-decoration:none;display:block;padding:0 10px;} #patch del {background:#fdd;text-decoration:none;display:block;padding:0 10px;} #patch .lines, .info {color:#888;background:#fff;} --></style> <title>[1397] sqlalchemy/trunk/lib/sqlalchemy/mapping/query.py: added from_obj option to select()</title> </head> <body> <div id="msg"> <dl> <dt>Revision</dt> <dd>1397</dd> <dt>Author</dt> <dd>zzzeek</dd> <dt>Date</dt> <dd>2006-05-04 20:18:55 -0500 (Thu, 04 May 2006)</dd> </dl> <h3>Log Message</h3> <pre>added from_obj option to select()</pre> <h3>Modified Paths</h3> <ul> <li><a href="#sqlalchemytrunklibsqlalchemymappingquerypy">sqlalchemy/trunk/lib/sqlalchemy/mapping/query.py</a></li> </ul> </div> <div id="patch"> <h3>Diff</h3> <a id="sqlalchemytrunklibsqlalchemymappingquerypy"></a> <div class="modfile"><h4>Modified: sqlalchemy/trunk/lib/sqlalchemy/mapping/query.py (1396 => 1397)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/trunk/lib/sqlalchemy/mapping/query.py 2006-05-05 00:58:13 UTC (rev 1396) +++ sqlalchemy/trunk/lib/sqlalchemy/mapping/query.py 2006-05-05 01:18:55 UTC (rev 1397) </span><span class="lines">@@ -223,14 +223,16 @@ </span><span class="cx"> </span><span class="cx"> def _compile(self, whereclause = None, **kwargs): </span><span class="cx"> order_by = kwargs.pop('order_by', False) </span><ins>+ from_obj = kwargs.pop('from_obj', []) </ins><span class="cx"> if order_by is False: </span><span class="cx"> order_by = self.order_by </span><span class="cx"> if order_by is False: </span><span class="cx"> if self.table.default_order_by() is not None: </span><span class="cx"> order_by = self.table.default_order_by() </span><del>- </del><ins>+ </ins><span class="cx"> if self._should_nest(**kwargs): </span><del>- s2 = sql.select(self.table.primary_key, whereclause, use_labels=True, from_obj=[self.table], **kwargs) </del><ins>+ from_obj.append(self.table) + s2 = sql.select(self.table.primary_key, whereclause, use_labels=True, from_obj=from_obj, **kwargs) </ins><span class="cx"> # raise "ok first thing", str(s2) </span><span class="cx"> if not kwargs.get('distinct', False) and order_by: </span><span class="cx"> s2.order_by(*util.to_list(order_by)) </span><span class="lines">@@ -243,7 +245,8 @@ </span><span class="cx"> if order_by: </span><span class="cx"> statement.order_by(*util.to_list(order_by)) </span><span class="cx"> else: </span><del>- statement = sql.select([], whereclause, from_obj=[self.table], use_labels=True, **kwargs) </del><ins>+ from_obj.append(self.table) + statement = sql.select([], whereclause, from_obj=from_obj, use_labels=True, **kwargs) </ins><span class="cx"> if order_by: </span><span class="cx"> statement.order_by(*util.to_list(order_by)) </span><span class="cx"> # for a DISTINCT query, you need the columns explicitly specified in order </span></span></pre> </div> </div> </body> </html> |
From: <co...@sq...> - 2006-05-05 00:58:24
|
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head><style type="text/css"><!-- #msg dl { border: 1px #006 solid; background: #369; padding: 6px; color: #fff; } #msg dt { float: left; width: 6em; font-weight: bold; } #msg dt:after { content:':';} #msg dl, #msg dt, #msg ul, #msg li { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; } #msg dl a { font-weight: bold} #msg dl a:link { color:#fc3; } #msg dl a:active { color:#ff0; } #msg dl a:visited { color:#cc6; } h3 { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; font-weight: bold; } #msg pre { overflow: auto; background: #ffc; border: 1px #fc0 solid; padding: 6px; } #msg ul, pre { overflow: auto; } #patch { width: 100%; } #patch h4 {font-family: verdana,arial,helvetica,sans-serif;font-size:10pt;padding:8px;background:#369;color:#fff;margin:0;} #patch .propset h4, #patch .binary h4 {margin:0;} #patch pre {padding:0;line-height:1.2em;margin:0;} #patch .diff {width:100%;background:#eee;padding: 0 0 10px 0;overflow:auto;} #patch .propset .diff, #patch .binary .diff {padding:10px 0;} #patch span {display:block;padding:0 10px;} #patch .modfile, #patch .addfile, #patch .delfile, #patch .propset, #patch .binary, #patch .copfile {border:1px solid #ccc;margin:10px 0;} #patch ins {background:#dfd;text-decoration:none;display:block;padding:0 10px;} #patch del {background:#fdd;text-decoration:none;display:block;padding:0 10px;} #patch .lines, .info {color:#888;background:#fff;} --></style> <title>[1396] sqlalchemy/branches/schema/test: merging more things from 0.1.7.....</title> </head> <body> <div id="msg"> <dl> <dt>Revision</dt> <dd>1396</dd> <dt>Author</dt> <dd>zzzeek</dd> <dt>Date</dt> <dd>2006-05-04 19:58:13 -0500 (Thu, 04 May 2006)</dd> </dl> <h3>Log Message</h3> <pre>merging more things from 0.1.7.....</pre> <h3>Modified Paths</h3> <ul> <li><a href="#sqlalchemybranchesschemalibsqlalchemyattributespy">sqlalchemy/branches/schema/lib/sqlalchemy/attributes.py</a></li> <li><a href="#sqlalchemybranchesschemalibsqlalchemyormmapperpy">sqlalchemy/branches/schema/lib/sqlalchemy/orm/mapper.py</a></li> <li><a href="#sqlalchemybranchesschemalibsqlalchemysqlpy">sqlalchemy/branches/schema/lib/sqlalchemy/sql.py</a></li> <li><a href="#sqlalchemybranchesschematestlazytest1py">sqlalchemy/branches/schema/test/lazytest1.py</a></li> <li><a href="#sqlalchemybranchesschematestselectpy">sqlalchemy/branches/schema/test/select.py</a></li> </ul> <h3>Added Paths</h3> <ul> <li><a href="#sqlalchemybranchesschematestmasscreate2py">sqlalchemy/branches/schema/test/masscreate2.py</a></li> </ul> </div> <div id="patch"> <h3>Diff</h3> <a id="sqlalchemybranchesschemalibsqlalchemyattributespy"></a> <div class="modfile"><h4>Modified: sqlalchemy/branches/schema/lib/sqlalchemy/attributes.py (1395 => 1396)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/schema/lib/sqlalchemy/attributes.py 2006-05-05 00:49:49 UTC (rev 1395) +++ sqlalchemy/branches/schema/lib/sqlalchemy/attributes.py 2006-05-05 00:58:13 UTC (rev 1396) </span><span class="lines">@@ -80,8 +80,12 @@ </span><span class="cx"> ManagedAttribute objects associated with the instance via this dictionary.""" </span><span class="cx"> def __init__(self, obj, key): </span><span class="cx"> self.__obj = weakref.ref(obj) </span><del>- #self.obj = obj </del><span class="cx"> self.key = key </span><ins>+ def __getstate__(self): + return {'key':self.key, 'obj':self.obj} + def __setstate__(self, d): + self.key = d['key'] + self.__obj = weakref.ref(d['obj']) </ins><span class="cx"> obj = property(lambda s:s.__obj()) </span><span class="cx"> def value_changed(self, *args, **kwargs): </span><span class="cx"> self.obj._managed_value_changed = True </span><span class="lines">@@ -522,26 +526,6 @@ </span><span class="cx"> will be passed along to newly created ManagedAttribute.""" </span><span class="cx"> if not hasattr(class_, '_attribute_manager'): </span><span class="cx"> class_._attribute_manager = self </span><del>- class_._managed_attributes = ObjectAttributeGateway() </del><span class="cx"> typecallable = getattr(class_, key, None) </span><span class="cx"> setattr(class_, key, self.create_prop(class_, key, uselist, callable_, typecallable=typecallable, **kwargs)) </span><span class="cx"> </span><del>-managed_attributes = weakref.WeakKeyDictionary() - -class ObjectAttributeGateway(object): - """handles the dictionary of ManagedAttributes for instances. this level of indirection - is to prevent circular references upon objects, as well as keeping them Pickle-compatible.""" - def __set__(self, obj, value): - managed_attributes[obj] = value - def __delete__(self, obj): - try: - del managed_attributes[obj] - except KeyError: - raise AttributeError() - def __get__(self, obj, owner): - if obj is None: - return self - try: - return managed_attributes[obj] - except KeyError: - raise AttributeError() </del><span class="cx">\ No newline at end of file </span></span></pre></div> <a id="sqlalchemybranchesschemalibsqlalchemyormmapperpy"></a> <div class="modfile"><h4>Modified: sqlalchemy/branches/schema/lib/sqlalchemy/orm/mapper.py (1395 => 1396)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/schema/lib/sqlalchemy/orm/mapper.py 2006-05-05 00:49:49 UTC (rev 1395) +++ sqlalchemy/branches/schema/lib/sqlalchemy/orm/mapper.py 2006-05-05 00:58:13 UTC (rev 1396) </span><span class="lines">@@ -143,6 +143,8 @@ </span><span class="cx"> self.effective_polymorphic_on = inherits.effective_polymorphic_on </span><span class="cx"> else: </span><span class="cx"> self.effective_polymorphic_on = self.polymorphic_on </span><ins>+ if self.order_by is False: + self.order_by = inherits.order_by </ins><span class="cx"> else: </span><span class="cx"> self._synchronizer = None </span><span class="cx"> self.inherits = None </span></span></pre></div> <a id="sqlalchemybranchesschemalibsqlalchemysqlpy"></a> <div class="modfile"><h4>Modified: sqlalchemy/branches/schema/lib/sqlalchemy/sql.py (1395 => 1396)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/schema/lib/sqlalchemy/sql.py 2006-05-05 00:49:49 UTC (rev 1395) +++ sqlalchemy/branches/schema/lib/sqlalchemy/sql.py 2006-05-05 00:58:13 UTC (rev 1396) </span><span class="lines">@@ -721,7 +721,9 @@ </span><span class="cx"> self.type = type </span><span class="cx"> def accept_visitor(self, visitor): </span><span class="cx"> visitor.visit_typeclause(self) </span><del>- </del><ins>+ def _get_from_objects(self): + return [] + </ins><span class="cx"> class TextClause(ClauseElement): </span><span class="cx"> """represents literal a SQL text fragment. public constructor is the </span><span class="cx"> text() function. </span></span></pre></div> <a id="sqlalchemybranchesschematestlazytest1py"></a> <div class="modfile"><h4>Modified: sqlalchemy/branches/schema/test/lazytest1.py (1395 => 1396)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/schema/test/lazytest1.py 2006-05-05 00:49:49 UTC (rev 1395) +++ sqlalchemy/branches/schema/test/lazytest1.py 2006-05-05 00:58:13 UTC (rev 1396) </span><span class="lines">@@ -68,6 +68,8 @@ </span><span class="cx"> class Data(object): </span><span class="cx"> pass </span><span class="cx"> </span><ins>+ session = create_session() + </ins><span class="cx"> # Create the basic mappers, with no frills or modifications </span><span class="cx"> Information.mapper = mapper(Information, info_table) </span><span class="cx"> Data.mapper = mapper(Data, data_table) </span><span class="lines">@@ -81,7 +83,7 @@ </span><span class="cx"> </span><span class="cx"> Information.mapper.add_property('rels', relation(Relation.mapper)) </span><span class="cx"> </span><del>- info = Information.mapper.get(1) </del><ins>+ info = session.query(Information).get(1) </ins><span class="cx"> assert info </span><span class="cx"> assert len(info.rels) == 2 </span><span class="cx"> assert len(info.rels[0].datas) == 3 </span></span></pre></div> <a id="sqlalchemybranchesschematestmasscreate2py"></a> <div class="addfile"><h4>Added: sqlalchemy/branches/schema/test/masscreate2.py (1395 => 1396)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/schema/test/masscreate2.py 2006-05-05 00:49:49 UTC (rev 1395) +++ sqlalchemy/branches/schema/test/masscreate2.py 2006-05-05 00:58:13 UTC (rev 1396) </span><span class="lines">@@ -0,0 +1,36 @@ </span><ins>+import gc + +import random, string + +from sqlalchemy.attributes import * + +# with this test, run top. make sure the Python process doenst grow in size arbitrarily. + +class User(object): + pass + +class Address(object): + pass + +attr_manager = AttributeManager() +attr_manager.register_attribute(User, 'id', uselist=False) +attr_manager.register_attribute(User, 'name', uselist=False) +attr_manager.register_attribute(User, 'addresses', uselist=True) +attr_manager.register_attribute(Address, 'email', uselist=False) +attr_manager.register_attribute(Address, 'user', uselist=False) + + +for i in xrange(1000): + for j in xrange(1000): + u = User() + u.name = str(random.randint(0, 100000000)) + for k in xrange(10): + a = Address() + a.email_address = str(random.randint(0, 100000000)) + u.addresses.append(a) + a.user = u + print "clearing" + #managed_attributes.clear() + gc.collect() + + </ins></span></pre></div> <a id="sqlalchemybranchesschematestselectpy"></a> <div class="modfile"><h4>Modified: sqlalchemy/branches/schema/test/select.py (1395 => 1396)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/schema/test/select.py 2006-05-05 00:49:49 UTC (rev 1395) +++ sqlalchemy/branches/schema/test/select.py 2006-05-05 00:58:13 UTC (rev 1396) </span><span class="lines">@@ -555,7 +555,8 @@ </span><span class="cx"> self.assertEqual(str(cast(tbl.c.ts, Date, engine=engine)), 'CAST(casttest.ts AS %s)' %expected_results[2]) </span><span class="cx"> self.assertEqual(str(cast(1234, TEXT, engine=engine)), 'CAST(%s AS %s)' %(literal, expected_results[3])) </span><span class="cx"> self.assertEqual(str(cast('test', String(20), engine=engine)), 'CAST(%s AS %s)' %(literal, expected_results[4])) </span><del>- </del><ins>+ sel = select([tbl, cast(tbl.c.v1, Numeric)], engine=engine) + self.assertEqual(str(sel), "SELECT casttest.id, casttest.v1, casttest.v2, casttest.ts, CAST(casttest.v1 AS NUMERIC(10, 2)) \nFROM casttest") </ins><span class="cx"> # first test with Postgres engine </span><span class="cx"> check_results(postgres.engine({}), ['NUMERIC(10, 2)', 'NUMERIC(12, 9)', 'DATE', 'TEXT', 'VARCHAR(20)'], '%(literal)s') </span><span class="cx"> </span></span></pre> </div> </div> </body> </html> |
From: <co...@sq...> - 2006-05-05 00:49:57
|
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head><style type="text/css"><!-- #msg dl { border: 1px #006 solid; background: #369; padding: 6px; color: #fff; } #msg dt { float: left; width: 6em; font-weight: bold; } #msg dt:after { content:':';} #msg dl, #msg dt, #msg ul, #msg li { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; } #msg dl a { font-weight: bold} #msg dl a:link { color:#fc3; } #msg dl a:active { color:#ff0; } #msg dl a:visited { color:#cc6; } h3 { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; font-weight: bold; } #msg pre { overflow: auto; background: #ffc; border: 1px #fc0 solid; padding: 6px; } #msg ul, pre { overflow: auto; } #patch { width: 100%; } #patch h4 {font-family: verdana,arial,helvetica,sans-serif;font-size:10pt;padding:8px;background:#369;color:#fff;margin:0;} #patch .propset h4, #patch .binary h4 {margin:0;} #patch pre {padding:0;line-height:1.2em;margin:0;} #patch .diff {width:100%;background:#eee;padding: 0 0 10px 0;overflow:auto;} #patch .propset .diff, #patch .binary .diff {padding:10px 0;} #patch span {display:block;padding:0 10px;} #patch .modfile, #patch .addfile, #patch .delfile, #patch .propset, #patch .binary, #patch .copfile {border:1px solid #ccc;margin:10px 0;} #patch ins {background:#dfd;text-decoration:none;display:block;padding:0 10px;} #patch del {background:#fdd;text-decoration:none;display:block;padding:0 10px;} #patch .lines, .info {color:#888;background:#fff;} --></style> <title>[1395] sqlalchemy/branches/schema/test: fixed parenthsis to support WHERE subquery in UPDATE</title> </head> <body> <div id="msg"> <dl> <dt>Revision</dt> <dd>1395</dd> <dt>Author</dt> <dd>zzzeek</dd> <dt>Date</dt> <dd>2006-05-04 19:49:49 -0500 (Thu, 04 May 2006)</dd> </dl> <h3>Log Message</h3> <pre>fixed parenthsis to support WHERE subquery in UPDATE</pre> <h3>Modified Paths</h3> <ul> <li><a href="#sqlalchemybranchesschemalibsqlalchemysqlpy">sqlalchemy/branches/schema/lib/sqlalchemy/sql.py</a></li> <li><a href="#sqlalchemybranchesschematestselectpy">sqlalchemy/branches/schema/test/select.py</a></li> </ul> </div> <div id="patch"> <h3>Diff</h3> <a id="sqlalchemybranchesschemalibsqlalchemysqlpy"></a> <div class="modfile"><h4>Modified: sqlalchemy/branches/schema/lib/sqlalchemy/sql.py (1394 => 1395)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/schema/lib/sqlalchemy/sql.py 2006-05-05 00:46:13 UTC (rev 1394) +++ sqlalchemy/branches/schema/lib/sqlalchemy/sql.py 2006-05-05 00:49:49 UTC (rev 1395) </span><span class="lines">@@ -899,9 +899,9 @@ </span><span class="cx"> self.operator = operator </span><span class="cx"> self.type = sqltypes.to_instance(type) </span><span class="cx"> self.parens = False </span><del>- if isinstance(self.left, BinaryClause): </del><ins>+ if isinstance(self.left, BinaryClause) or isinstance(self.left, Selectable): </ins><span class="cx"> self.left.parens = True </span><del>- if isinstance(self.right, BinaryClause): </del><ins>+ if isinstance(self.right, BinaryClause) or isinstance(self.right, Selectable): </ins><span class="cx"> self.right.parens = True </span><span class="cx"> def copy_container(self): </span><span class="cx"> return BinaryClause(self.left.copy_container(), self.right.copy_container(), self.operator) </span></span></pre></div> <a id="sqlalchemybranchesschematestselectpy"></a> <div class="modfile"><h4>Modified: sqlalchemy/branches/schema/test/select.py (1394 => 1395)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/schema/test/select.py 2006-05-05 00:46:13 UTC (rev 1394) +++ sqlalchemy/branches/schema/test/select.py 2006-05-05 00:49:49 UTC (rev 1395) </span><span class="lines">@@ -626,6 +626,11 @@ </span><span class="cx"> s = select([table2], table2.c.otherid == table1.c.myid) </span><span class="cx"> u = update(table1, table1.c.name == 'jack', values = {table1.c.name : s}) </span><span class="cx"> self.runtest(u, "UPDATE mytable SET name=(SELECT myothertable.otherid, myothertable.othername FROM myothertable WHERE myothertable.otherid = mytable.myid) WHERE mytable.name = :mytable_name") </span><ins>+ + # test a correlated WHERE clause + s = select([table2.c.othername], table2.c.otherid == 7) + u = update(table1, table1.c.name==s) + self.runtest(u, "UPDATE mytable SET myid=:myid, name=:name, description=:description WHERE mytable.name = (SELECT myothertable.othername FROM myothertable WHERE myothertable.otherid = :myothertable_otherid)") </ins><span class="cx"> </span><span class="cx"> def testdelete(self): </span><span class="cx"> self.runtest(delete(table1, table1.c.myid == 7), "DELETE FROM mytable WHERE mytable.myid = :mytable_myid") </span></span></pre> </div> </div> </body> </html> |
From: <co...@sq...> - 2006-05-05 00:46:21
|
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head><style type="text/css"><!-- #msg dl { border: 1px #006 solid; background: #369; padding: 6px; color: #fff; } #msg dt { float: left; width: 6em; font-weight: bold; } #msg dt:after { content:':';} #msg dl, #msg dt, #msg ul, #msg li { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; } #msg dl a { font-weight: bold} #msg dl a:link { color:#fc3; } #msg dl a:active { color:#ff0; } #msg dl a:visited { color:#cc6; } h3 { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; font-weight: bold; } #msg pre { overflow: auto; background: #ffc; border: 1px #fc0 solid; padding: 6px; } #msg ul, pre { overflow: auto; } #patch { width: 100%; } #patch h4 {font-family: verdana,arial,helvetica,sans-serif;font-size:10pt;padding:8px;background:#369;color:#fff;margin:0;} #patch .propset h4, #patch .binary h4 {margin:0;} #patch pre {padding:0;line-height:1.2em;margin:0;} #patch .diff {width:100%;background:#eee;padding: 0 0 10px 0;overflow:auto;} #patch .propset .diff, #patch .binary .diff {padding:10px 0;} #patch span {display:block;padding:0 10px;} #patch .modfile, #patch .addfile, #patch .delfile, #patch .propset, #patch .binary, #patch .copfile {border:1px solid #ccc;margin:10px 0;} #patch ins {background:#dfd;text-decoration:none;display:block;padding:0 10px;} #patch del {background:#fdd;text-decoration:none;display:block;padding:0 10px;} #patch .lines, .info {color:#888;background:#fff;} --></style> <title>[1394] sqlalchemy/branches/schema/lib/sqlalchemy/orm/properties.py: took out the swap() in the lazy clause...</title> </head> <body> <div id="msg"> <dl> <dt>Revision</dt> <dd>1394</dd> <dt>Author</dt> <dd>zzzeek</dd> <dt>Date</dt> <dd>2006-05-04 19:46:13 -0500 (Thu, 04 May 2006)</dd> </dl> <h3>Log Message</h3> <pre>took out the swap() in the lazy clause...</pre> <h3>Modified Paths</h3> <ul> <li><a href="#sqlalchemybranchesschemalibsqlalchemyormpropertiespy">sqlalchemy/branches/schema/lib/sqlalchemy/orm/properties.py</a></li> </ul> </div> <div id="patch"> <h3>Diff</h3> <a id="sqlalchemybranchesschemalibsqlalchemyormpropertiespy"></a> <div class="modfile"><h4>Modified: sqlalchemy/branches/schema/lib/sqlalchemy/orm/properties.py (1393 => 1394)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/schema/lib/sqlalchemy/orm/properties.py 2006-05-05 00:44:26 UTC (rev 1393) +++ sqlalchemy/branches/schema/lib/sqlalchemy/orm/properties.py 2006-05-05 00:46:13 UTC (rev 1394) </span><span class="lines">@@ -439,7 +439,6 @@ </span><span class="cx"> binary.left = binds.setdefault(binary.left, </span><span class="cx"> sql.BindParamClause(bind_label(), None, shortname = binary.left.name)) </span><span class="cx"> reverse[binary.right] = binds[col] </span><del>- binary.swap() </del><span class="cx"> </span><span class="cx"> if isinstance(binary.right, schema.Column) and isinstance(binary.left, schema.Column) and ((not circular and binary.right.table is table) or (circular and binary.left is foreignkey)): </span><span class="cx"> col = binary.right </span></span></pre> </div> </div> </body> </html> |
From: <co...@sq...> - 2006-05-05 00:44:36
|
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head><style type="text/css"><!-- #msg dl { border: 1px #006 solid; background: #369; padding: 6px; color: #fff; } #msg dt { float: left; width: 6em; font-weight: bold; } #msg dt:after { content:':';} #msg dl, #msg dt, #msg ul, #msg li { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; } #msg dl a { font-weight: bold} #msg dl a:link { color:#fc3; } #msg dl a:active { color:#ff0; } #msg dl a:visited { color:#cc6; } h3 { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; font-weight: bold; } #msg pre { overflow: auto; background: #ffc; border: 1px #fc0 solid; padding: 6px; } #msg ul, pre { overflow: auto; } #patch { width: 100%; } #patch h4 {font-family: verdana,arial,helvetica,sans-serif;font-size:10pt;padding:8px;background:#369;color:#fff;margin:0;} #patch .propset h4, #patch .binary h4 {margin:0;} #patch pre {padding:0;line-height:1.2em;margin:0;} #patch .diff {width:100%;background:#eee;padding: 0 0 10px 0;overflow:auto;} #patch .propset .diff, #patch .binary .diff {padding:10px 0;} #patch span {display:block;padding:0 10px;} #patch .modfile, #patch .addfile, #patch .delfile, #patch .propset, #patch .binary, #patch .copfile {border:1px solid #ccc;margin:10px 0;} #patch ins {background:#dfd;text-decoration:none;display:block;padding:0 10px;} #patch del {background:#fdd;text-decoration:none;display:block;padding:0 10px;} #patch .lines, .info {color:#888;background:#fff;} --></style> <title>[1393] sqlalchemy/branches/schema/lib/sqlalchemy: fixed HAVING order, fixed mapper column types propigated in UPDATE/DELETE</title> </head> <body> <div id="msg"> <dl> <dt>Revision</dt> <dd>1393</dd> <dt>Author</dt> <dd>zzzeek</dd> <dt>Date</dt> <dd>2006-05-04 19:44:26 -0500 (Thu, 04 May 2006)</dd> </dl> <h3>Log Message</h3> <pre>fixed HAVING order, fixed mapper column types propigated in UPDATE/DELETE</pre> <h3>Modified Paths</h3> <ul> <li><a href="#sqlalchemybranchesschemadocbuildcontentadv_datamappingtxt">sqlalchemy/branches/schema/doc/build/content/adv_datamapping.txt</a></li> <li><a href="#sqlalchemybranchesschemalibsqlalchemyansisqlpy">sqlalchemy/branches/schema/lib/sqlalchemy/ansisql.py</a></li> <li><a href="#sqlalchemybranchesschemalibsqlalchemyormmapperpy">sqlalchemy/branches/schema/lib/sqlalchemy/orm/mapper.py</a></li> </ul> </div> <div id="patch"> <h3>Diff</h3> <a id="sqlalchemybranchesschemadocbuildcontentadv_datamappingtxt"></a> <div class="modfile"><h4>Modified: sqlalchemy/branches/schema/doc/build/content/adv_datamapping.txt (1392 => 1393)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/schema/doc/build/content/adv_datamapping.txt 2006-05-05 00:42:42 UTC (rev 1392) +++ sqlalchemy/branches/schema/doc/build/content/adv_datamapping.txt 2006-05-05 00:44:26 UTC (rev 1393) </span><span class="lines">@@ -38,8 +38,12 @@ </span><span class="cx"> Column('user_id', INT, ForeignKey("users")), </span><span class="cx"> Column('keyword_id', INT, ForeignKey("keywords")) </span><span class="cx"> ) </span><del>-### Overriding Properties {@name=overriding} </del><span class="cx"> </span><ins>+ +### More On Mapper Properties {@name=properties} + +#### Overriding Properties {@name=overriding} + </ins><span class="cx"> A common request is the ability to create custom class properties that override the behavior of setting/getting an attribute. Currently, the easiest way to do this in SQLAlchemy is how it would be done in any Python program; define your attribute with a different name, such as "_attribute", and use a property to get/set its value. The mapper just needs to be told of the special name: </span><span class="cx"> </span><span class="cx"> {python} </span><span class="lines">@@ -58,8 +62,28 @@ </span><span class="cx"> </span><span class="cx"> In a later release, SQLAlchemy will also allow _get_email and _set_email to be attached directly to the "email" property created by the mapper, and will also allow this association to occur via decorators. </span><span class="cx"> </span><del>-### More On Relations {@name=relations} </del><ins>+### Overriding Column Names {@name=colname} </ins><span class="cx"> </span><ins>+When mappers are constructed, by default the column names in the Table metadata are used as the names of attributes on the mapped class. This can be customzed within the properties by stating the key/column combinations explicitly: + + {python} + user_mapper = mapper(User, users_table, properties={ + 'id' : users_table.c.user_id, + 'name' : users_table.c.user_name, + }) + +In the situation when column names overlap in a mapper against multiple tables, columns may be referenced together with a list: + + {python} + # join users and addresses + usersaddresses = sql.join(users_table, addresses_table, users_table.c.user_id == addresses_table.c.user_id) + m = mapper(User, usersaddresses, + properties = { + 'id' : [users_table.c.user_id, addresses_table.c.user_id], + } + ) + + </ins><span class="cx"> #### Custom Join Conditions {@name=customjoin} </span><span class="cx"> </span><span class="cx"> When creating relations on a mapper, most examples so far have illustrated the mapper and relationship joining up based on the foreign keys of the tables they represent. in fact, this "automatic" inspection can be completely circumvented using the `primaryjoin` and `secondaryjoin` arguments to `relation`, as in this example which creates a User object which has a relationship to all of its Addresses which are in Boston: </span><span class="lines">@@ -70,10 +94,10 @@ </span><span class="cx"> class Address(object): </span><span class="cx"> pass </span><span class="cx"> </span><del>- mapper(Address, addresses) - mapper(User, users, properties={ </del><ins>+ mapper(Address, addresses_table) + mapper(User, users_table, properties={ </ins><span class="cx"> 'boston_addreses' : relation(Address, primaryjoin= </span><del>- and_(users.c.user_id==Address.c.user_id, </del><ins>+ and_(users_table.c.user_id==Address.c.user_id, </ins><span class="cx"> Addresses.c.city=='Boston')) </span><span class="cx"> }) </span><span class="cx"> </span><span class="lines">@@ -85,9 +109,9 @@ </span><span class="cx"> class Keyword(object): </span><span class="cx"> pass </span><span class="cx"> Keyword.mapper = mapper(Keyword, keywords) </span><del>- User.mapper = mapper(User, users, properties={ </del><ins>+ User.mapper = mapper(User, users_table, properties={ </ins><span class="cx"> 'keywords':relation(Keyword.mapper, </span><del>- primaryjoin=users.c.user_id==userkeywords.c.user_id, </del><ins>+ primaryjoin=users_table.c.user_id==userkeywords.c.user_id, </ins><span class="cx"> secondaryjoin=userkeywords.c.keyword_id==keywords.c.keyword_id </span><span class="cx"> ) </span><span class="cx"> }) </span><span class="lines">@@ -97,19 +121,19 @@ </span><span class="cx"> The previous example leads in to the idea of joining against the same table multiple times. Below is a User object that has lists of its Boston and New York addresses, both lazily loaded when they are first accessed: </span><span class="cx"> </span><span class="cx"> {python} </span><del>- User.mapper = mapper(User, users, properties={ </del><ins>+ User.mapper = mapper(User, users_table, properties={ </ins><span class="cx"> 'boston_addreses' : relation(Address.mapper, primaryjoin= </span><del>- and_(users.c.user_id==Address.c.user_id, </del><ins>+ and_(users_table.c.user_id==Address.c.user_id, </ins><span class="cx"> Addresses.c.city=='Boston')), </span><span class="cx"> 'newyork_addresses' : relation(Address.mapper, primaryjoin= </span><del>- and_(users.c.user_id==Address.c.user_id, </del><ins>+ and_(users_table.c.user_id==Address.c.user_id, </ins><span class="cx"> Addresses.c.city=='New York')), </span><span class="cx"> }) </span><span class="cx"> </span><span class="cx"> A complication arises with the above pattern if you want the relations to be eager loaded. Since there will be two separate joins to the addresses table during an eager load, an alias needs to be used to separate them. You can create an alias of the addresses table to separate them, but then you are in effect creating a brand new mapper for each property, unrelated to the main Address mapper, which can create problems with commit operations. So an additional argument `use_alias` can be used with an eager relationship to specify the alias to be used just within the eager query: </span><span class="cx"> </span><span class="cx"> {python} </span><del>- User.mapper = mapper(User, users, properties={ </del><ins>+ User.mapper = mapper(User, users_table, properties={ </ins><span class="cx"> 'boston_addreses' : relation(Address.mapper, primaryjoin= </span><span class="cx"> and_(User.c.user_id==Address.c.user_id, </span><span class="cx"> Addresses.c.city=='Boston'), lazy=False, use_alias=True), </span><span class="lines">@@ -159,35 +183,35 @@ </span><span class="cx"> </span><span class="cx"> {python} </span><span class="cx"> # disable all ordering </span><del>- mapper = mapper(User, users, order_by=None) </del><ins>+ mapper = mapper(User, users_table, order_by=None) </ins><span class="cx"> </span><span class="cx"> # order by a column </span><del>- mapper = mapper(User, users, order_by=users.c.user_id) </del><ins>+ mapper = mapper(User, users_table, order_by=users_tableusers_table.c.user_id) </ins><span class="cx"> </span><span class="cx"> # order by multiple items </span><del>- mapper = mapper(User, users, order_by=[users.c.user_id, desc(users.c.user_name)]) </del><ins>+ mapper = mapper(User, users_table, order_by=[users_table.c.user_id, desc(users_table.c.user_name)]) </ins><span class="cx"> </span><span class="cx"> "order_by" can also be specified to an individual `select` method, overriding all other per-engine/per-mapper orderings: </span><span class="cx"> </span><span class="cx"> {python} </span><span class="cx"> # order by a column </span><del>- l = mapper.select(users.c.user_name=='fred', order_by=users.c.user_id) </del><ins>+ l = mapper.select(users_table.c.user_name=='fred', order_by=users_table.c.user_id) </ins><span class="cx"> </span><span class="cx"> # order by multiple criterion </span><del>- l = mapper.select(users.c.user_name=='fred', order_by=[users.c.user_id, desc(users.c.user_name)]) </del><ins>+ l = mapper.select(users_table.c.user_name=='fred', order_by=[users_table.c.user_id, desc(users_table.c.user_name)]) </ins><span class="cx"> </span><span class="cx"> For relations, the "order_by" property can also be specified to all forms of relation: </span><span class="cx"> </span><span class="cx"> {python} </span><span class="cx"> # order address objects by address id </span><del>- mapper = mapper(User, users, properties = { - 'addresses' : relation(mapper(Address, addresses), order_by=addresses.c.address_id) </del><ins>+ mapper = mapper(User, users_table, properties = { + 'addresses' : relation(mapper(Address, addresses_table), order_by=addresses_table.c.address_id) </ins><span class="cx"> }) </span><span class="cx"> </span><span class="cx"> # eager load with ordering - the ORDER BY clauses of parent/child will be organized properly </span><del>- mapper = mapper(User, users, properties = { - 'addresses' : relation(mapper(Address, addresses), order_by=desc(addresses.c.email_address), eager=True) - }, order_by=users.c.user_id) </del><ins>+ mapper = mapper(User, users_table, properties = { + 'addresses' : relation(mapper(Address, addresses_table), order_by=desc(addresses_table.c.email_address), eager=True) + }, order_by=users_table.c.user_id) </ins><span class="cx"> </span><span class="cx"> ### Limiting Rows {@name=limits} </span><span class="cx"> </span><span class="lines">@@ -197,7 +221,7 @@ </span><span class="cx"> class User(object): </span><span class="cx"> pass </span><span class="cx"> </span><del>- mapper(User, users) </del><ins>+ mapper(User, users_table) </ins><span class="cx"> {sql}r = session.query(User).select(limit=20, offset=10) </span><span class="cx"> SELECT users.user_id AS users_user_id, </span><span class="cx"> users.user_name AS users_user_name, users.password AS users_password </span><span class="lines">@@ -212,8 +236,8 @@ </span><span class="cx"> pass </span><span class="cx"> class Address(object): </span><span class="cx"> pass </span><del>- mapper(User, users, properties={ - 'addresses' : relation(mapper(Address, addresses), lazy=False) </del><ins>+ mapper(User, users_table, properties={ + 'addresses' : relation(mapper(Address, addresses_table), lazy=False) </ins><span class="cx"> }) </span><span class="cx"> r = session.query(User).select(User.c.user_name.like('F%'), limit=20, offset=10) </span><span class="cx"> {opensql}SELECT users.user_id AS users_user_id, users.user_name AS users_user_name, </span><span class="lines">@@ -230,27 +254,7 @@ </span><span class="cx"> </span><span class="cx"> The main WHERE clause as well as the limiting clauses are coerced into a subquery; this subquery represents the desired result of objects. A containing query, which handles the eager relationships, is joined against the subquery to produce the result. </span><span class="cx"> </span><del>-### Overriding Column Names {@name=colname} </del><span class="cx"> </span><del>-When mappers are constructed, by default the column names in the Table metadata are used as the names of attributes on the mapped class. This can be customzed within the properties by stating the key/column combinations explicitly: - - {python} - user_mapper = mapper(User, users, properties={ - 'id' : users.c.user_id, - 'name' : users.c.user_name, - }) - -In the situation when column names overlap in a mapper against multiple tables, columns may be referenced together with a list: - - {python} - # join users and addresses - usersaddresses = sql.join(users, addresses, users.c.user_id == addresses.c.user_id) - m = mapper(User, usersaddresses, - properties = { - 'id' : [users.c.user_id, addresses.c.user_id], - } - ) - </del><span class="cx"> ### Deferred Column Loading {@name=deferred} </span><span class="cx"> </span><span class="cx"> This feature allows particular columns of a table to not be loaded by default, instead being loaded later on when first referenced. It is essentailly "column-level lazy loading". This feature is useful when one wants to avoid loading a large text or binary field into memory when its not needed. Individual columns can be lazy loaded by themselves or placed into groups that lazy-load together. </span><span class="lines">@@ -314,8 +318,8 @@ </span><span class="cx"> pass </span><span class="cx"> </span><span class="cx"> # a 'lazy' relationship </span><del>- mapper(User, users, properties = { - 'addreses':relation(mapper(Address, addresses), lazy=True) </del><ins>+ mapper(User, users_table, properties = { + 'addreses':relation(mapper(Address, addresses_table), lazy=True) </ins><span class="cx"> }) </span><span class="cx"> </span><span class="cx"> # copy the mapper and convert 'addresses' to be eager </span><span class="lines">@@ -450,8 +454,8 @@ </span><span class="cx"> {python} </span><span class="cx"> AddressUser.mapper = mapper( </span><span class="cx"> AddressUser, </span><del>- addresses, inherits=User.mapper, - inherit_condition=users.c.user_id==addresses.c.user_id </del><ins>+ addresses_table, inherits=User.mapper, + inherit_condition=users_table.c.user_id==addresses_table.c.user_id </ins><span class="cx"> ) </span><span class="cx"> </span><span class="cx"> ### Mapping a Class against Multiple Tables {@name=joins} </span><span class="lines">@@ -464,7 +468,7 @@ </span><span class="cx"> pass </span><span class="cx"> </span><span class="cx"> # define a Join </span><del>- j = join(users, addresses) </del><ins>+ j = join(users_table, addresses_table) </ins><span class="cx"> </span><span class="cx"> # map to it - the identity of an AddressUser object will be </span><span class="cx"> # based on (user_id, address_id) since those are the primary keys involved </span><span class="lines">@@ -473,8 +477,8 @@ </span><span class="cx"> A second example: </span><span class="cx"> {python} </span><span class="cx"> # many-to-many join on an association table </span><del>- j = join(users, userkeywords, - users.c.user_id==userkeywords.c.user_id).join(keywords, </del><ins>+ j = join(users_table, userkeywords, + users_table.c.user_id==userkeywords.c.user_id).join(keywords, </ins><span class="cx"> userkeywords.c.keyword_id==keywords.c.keyword_id) </span><span class="cx"> </span><span class="cx"> # a class </span><span class="lines">@@ -554,7 +558,7 @@ </span><span class="cx"> </span><span class="cx"> {python} </span><span class="cx"> usermapper = mapper(User, users) </span><del>- mapper(Address, addresses, properties={ </del><ins>+ mapper(Address, addresses_table, properties={ </ins><span class="cx"> 'user':relation(User) </span><span class="cx"> }) </span><span class="cx"> </span><span class="lines">@@ -645,10 +649,10 @@ </span><span class="cx"> class User(object): </span><span class="cx"> pass </span><span class="cx"> </span><del>- User.mapper = mapper(User, users) </del><ins>+ User.mapper = mapper(User, users_table) </ins><span class="cx"> </span><span class="cx"> # select users </span><del>- c = users.select().execute() </del><ins>+ c = users_table.select().execute() </ins><span class="cx"> </span><span class="cx"> # get objects </span><span class="cx"> userlist = User.mapper.instances(c) </span><span class="lines">@@ -658,10 +662,10 @@ </span><span class="cx"> class Address(object): </span><span class="cx"> pass </span><span class="cx"> </span><del>- Address.mapper = mapper(Address, addresses) </del><ins>+ Address.mapper = mapper(Address, addresses_table) </ins><span class="cx"> </span><span class="cx"> # select users and addresses in one query </span><del>- s = select([users, addresses], users.c.user_id==addresses.c.user_id) </del><ins>+ s = select([users_table, addresses_table], users_table.c.user_id==addresses_table.c.user_id) </ins><span class="cx"> </span><span class="cx"> # execute it, and process the results with the User mapper, chained to the Address mapper </span><span class="cx"> r = User.mapper.instances(s.execute(), Address.mapper) </span><span class="lines">@@ -759,10 +763,10 @@ </span><span class="cx"> To use MapperExtension, make your own subclass of it and just send it off to a mapper: </span><span class="cx"> </span><span class="cx"> {python} </span><del>- m = mapper(User, users, extension=MyExtension()) </del><ins>+ m = mapper(User, users_table, extension=MyExtension()) </ins><span class="cx"> </span><span class="cx"> Multiple extensions will be chained together and processed in order; they are specified as a list: </span><span class="cx"> </span><span class="cx"> {python} </span><del>- m = mapper(User, users, extension=[ext1, ext2, ext3]) </del><ins>+ m = mapper(User, users_table, extension=[ext1, ext2, ext3]) </ins><span class="cx"> </span></span></pre></div> <a id="sqlalchemybranchesschemalibsqlalchemyansisqlpy"></a> <div class="modfile"><h4>Modified: sqlalchemy/branches/schema/lib/sqlalchemy/ansisql.py (1392 => 1393)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/schema/lib/sqlalchemy/ansisql.py 2006-05-05 00:42:42 UTC (rev 1392) +++ sqlalchemy/branches/schema/lib/sqlalchemy/ansisql.py 2006-05-05 00:44:26 UTC (rev 1393) </span><span class="lines">@@ -372,15 +372,15 @@ </span><span class="cx"> if group_by: </span><span class="cx"> text += " GROUP BY " + group_by </span><span class="cx"> </span><ins>+ if select.having is not None: + t = self.get_str(select.having) + if t: + text += " \nHAVING " + t + </ins><span class="cx"> order_by = self.get_str(select.order_by_clause) </span><span class="cx"> if order_by: </span><span class="cx"> text += " ORDER BY " + order_by </span><span class="cx"> </span><del>- if select.having is not None: - t = self.get_str(select.having) - if t: - text += " \nHAVING " + t - </del><span class="cx"> text += self.visit_select_postclauses(select) </span><span class="cx"> </span><span class="cx"> if select.for_update: </span></span></pre></div> <a id="sqlalchemybranchesschemalibsqlalchemyormmapperpy"></a> <div class="modfile"><h4>Modified: sqlalchemy/branches/schema/lib/sqlalchemy/orm/mapper.py (1392 => 1393)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/schema/lib/sqlalchemy/orm/mapper.py 2006-05-05 00:42:42 UTC (rev 1392) +++ sqlalchemy/branches/schema/lib/sqlalchemy/orm/mapper.py 2006-05-05 00:44:26 UTC (rev 1393) </span><span class="lines">@@ -649,9 +649,9 @@ </span><span class="cx"> if len(update): </span><span class="cx"> clause = sql.and_() </span><span class="cx"> for col in self.pks_by_table[table]: </span><del>- clause.clauses.append(col == sql.bindparam(col._label)) </del><ins>+ clause.clauses.append(col == sql.bindparam(col._label, type=col.type)) </ins><span class="cx"> if self.version_id_col is not None: </span><del>- clause.clauses.append(self.version_id_col == sql.bindparam(self.version_id_col._label)) </del><ins>+ clause.clauses.append(self.version_id_col == sql.bindparam(self.version_id_col._label, type=col.type)) </ins><span class="cx"> statement = table.update(clause) </span><span class="cx"> rows = 0 </span><span class="cx"> supports_sane_rowcount = True </span><span class="lines">@@ -727,9 +727,9 @@ </span><span class="cx"> if len(delete): </span><span class="cx"> clause = sql.and_() </span><span class="cx"> for col in self.pks_by_table[table]: </span><del>- clause.clauses.append(col == sql.bindparam(col.key)) </del><ins>+ clause.clauses.append(col == sql.bindparam(col.key, type=col.type)) </ins><span class="cx"> if self.version_id_col is not None: </span><del>- clause.clauses.append(self.version_id_col == sql.bindparam(self.version_id_col.key)) </del><ins>+ clause.clauses.append(self.version_id_col == sql.bindparam(self.version_id_col.key, type=self.version_id_col.type)) </ins><span class="cx"> statement = table.delete(clause) </span><span class="cx"> print "DELETE IS", delete </span><span class="cx"> c = connection.execute(statement, delete) </span></span></pre> </div> </div> </body> </html> |
From: <co...@sq...> - 2006-05-05 00:42:52
|
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head><style type="text/css"><!-- #msg dl { border: 1px #006 solid; background: #369; padding: 6px; color: #fff; } #msg dt { float: left; width: 6em; font-weight: bold; } #msg dt:after { content:':';} #msg dl, #msg dt, #msg ul, #msg li { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; } #msg dl a { font-weight: bold} #msg dl a:link { color:#fc3; } #msg dl a:active { color:#ff0; } #msg dl a:visited { color:#cc6; } h3 { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; font-weight: bold; } #msg pre { overflow: auto; background: #ffc; border: 1px #fc0 solid; padding: 6px; } #msg ul, pre { overflow: auto; } #patch { width: 100%; } #patch h4 {font-family: verdana,arial,helvetica,sans-serif;font-size:10pt;padding:8px;background:#369;color:#fff;margin:0;} #patch .propset h4, #patch .binary h4 {margin:0;} #patch pre {padding:0;line-height:1.2em;margin:0;} #patch .diff {width:100%;background:#eee;padding: 0 0 10px 0;overflow:auto;} #patch .propset .diff, #patch .binary .diff {padding:10px 0;} #patch span {display:block;padding:0 10px;} #patch .modfile, #patch .addfile, #patch .delfile, #patch .propset, #patch .binary, #patch .copfile {border:1px solid #ccc;margin:10px 0;} #patch ins {background:#dfd;text-decoration:none;display:block;padding:0 10px;} #patch del {background:#fdd;text-decoration:none;display:block;padding:0 10px;} #patch .lines, .info {color:#888;background:#fff;} --></style> <title>[1392] sqlalchemy/trunk: fixed HAVING/ORDER BY order, 0.1.7 prep</title> </head> <body> <div id="msg"> <dl> <dt>Revision</dt> <dd>1392</dd> <dt>Author</dt> <dd>zzzeek</dd> <dt>Date</dt> <dd>2006-05-04 19:42:42 -0500 (Thu, 04 May 2006)</dd> </dl> <h3>Log Message</h3> <pre>fixed HAVING/ORDER BY order, 0.1.7 prep</pre> <h3>Modified Paths</h3> <ul> <li><a href="#sqlalchemytrunkCHANGES">sqlalchemy/trunk/CHANGES</a></li> <li><a href="#sqlalchemytrunklibsqlalchemyansisqlpy">sqlalchemy/trunk/lib/sqlalchemy/ansisql.py</a></li> <li><a href="#sqlalchemytrunksetuppy">sqlalchemy/trunk/setup.py</a></li> </ul> </div> <div id="patch"> <h3>Diff</h3> <a id="sqlalchemytrunkCHANGES"></a> <div class="modfile"><h4>Modified: sqlalchemy/trunk/CHANGES (1391 => 1392)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/trunk/CHANGES 2006-05-04 21:36:01 UTC (rev 1391) +++ sqlalchemy/trunk/CHANGES 2006-05-05 00:42:42 UTC (rev 1392) </span><span class="lines">@@ -1,5 +1,30 @@ </span><del>-next </del><ins>+0.1.7 </ins><span class="cx"> - some fixes to topological sort algorithm </span><ins>+- added DISTINCT ON support to Postgres (just supply distinct=[col1,col2..]) +- "order_by" mapper property inherited from inheriting mapper +- fix to column type used when mapper UPDATES/DELETEs +- with convert_unicode=True, reflection was failing, has been fixed +- types types types! still werent working....have to use TypeDecorator again :( +- mysql binary type converts array output to buffer, fixes PickleType +- fixed the attributes.py memory leak once and for all +- unittests are qualified based on the databases that support each one +- fixed bug where column defaults would clobber VALUES clause of insert objects +- fixed bug where table def w/ schema name would force engine connection +- fix for parenthesis to work correctly with subqueries in INSERT/UPDATE +- HistoryArraySet gets extend() method +- fixed lazyload support for other comparison operators besides = +- lazyload fix where two comparisons in the join condition point to the +samem column +- added "construct_new" flag to mapper, will use __new__ to create instances +instead of __init__ (standard in 0.2) +- added selectresults.py to SVN, missed it last time +- tweak to allow a many-to-many relationship from a table to itself via +an association table +- small fix to "translate_row" function used by polymorphic example +- create_engine uses cgi.parse_qsl to read query string (out the window in 0.2) +- tweaks to CAST operator +- fixed function names LOCAL_TIME/LOCAL_TIMESTAMP -> LOCALTIME/LOCALTIMESTAMP +- fixed order of ORDER BY/HAVING in compile </ins><span class="cx"> </span><span class="cx"> 0.1.6 </span><span class="cx"> - support for MS-SQL added courtesy Rick Morrison, Runar Petursson </span></span></pre></div> <a id="sqlalchemytrunklibsqlalchemyansisqlpy"></a> <div class="modfile"><h4>Modified: sqlalchemy/trunk/lib/sqlalchemy/ansisql.py (1391 => 1392)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/trunk/lib/sqlalchemy/ansisql.py 2006-05-04 21:36:01 UTC (rev 1391) +++ sqlalchemy/trunk/lib/sqlalchemy/ansisql.py 2006-05-05 00:42:42 UTC (rev 1392) </span><span class="lines">@@ -371,14 +371,14 @@ </span><span class="cx"> if group_by: </span><span class="cx"> text += " GROUP BY " + group_by </span><span class="cx"> </span><del>- order_by = self.get_str(select.order_by_clause) - if order_by: - text += " ORDER BY " + order_by - </del><span class="cx"> if select.having is not None: </span><span class="cx"> t = self.get_str(select.having) </span><span class="cx"> if t: </span><span class="cx"> text += " \nHAVING " + t </span><ins>+ + order_by = self.get_str(select.order_by_clause) + if order_by: + text += " ORDER BY " + order_by </ins><span class="cx"> </span><span class="cx"> text += self.visit_select_postclauses(select) </span><span class="cx"> </span></span></pre></div> <a id="sqlalchemytrunksetuppy"></a> <div class="modfile"><h4>Modified: sqlalchemy/trunk/setup.py (1391 => 1392)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/trunk/setup.py 2006-05-04 21:36:01 UTC (rev 1391) +++ sqlalchemy/trunk/setup.py 2006-05-05 00:42:42 UTC (rev 1392) </span><span class="lines">@@ -3,7 +3,7 @@ </span><span class="cx"> from setuptools import setup, find_packages </span><span class="cx"> </span><span class="cx"> setup(name = "SQLAlchemy", </span><del>- version = "0.1.6", </del><ins>+ version = "0.1.7", </ins><span class="cx"> description = "Database Abstraction Library", </span><span class="cx"> author = "Mike Bayer", </span><span class="cx"> author_email = "mi...@zz...", </span></span></pre> </div> </div> </body> </html> |
From: <co...@sq...> - 2006-05-04 21:36:11
|
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head><style type="text/css"><!-- #msg dl { border: 1px #006 solid; background: #369; padding: 6px; color: #fff; } #msg dt { float: left; width: 6em; font-weight: bold; } #msg dt:after { content:':';} #msg dl, #msg dt, #msg ul, #msg li { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; } #msg dl a { font-weight: bold} #msg dl a:link { color:#fc3; } #msg dl a:active { color:#ff0; } #msg dl a:visited { color:#cc6; } h3 { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; font-weight: bold; } #msg pre { overflow: auto; background: #ffc; border: 1px #fc0 solid; padding: 6px; } #msg ul, pre { overflow: auto; } #patch { width: 100%; } #patch h4 {font-family: verdana,arial,helvetica,sans-serif;font-size:10pt;padding:8px;background:#369;color:#fff;margin:0;} #patch .propset h4, #patch .binary h4 {margin:0;} #patch pre {padding:0;line-height:1.2em;margin:0;} #patch .diff {width:100%;background:#eee;padding: 0 0 10px 0;overflow:auto;} #patch .propset .diff, #patch .binary .diff {padding:10px 0;} #patch span {display:block;padding:0 10px;} #patch .modfile, #patch .addfile, #patch .delfile, #patch .propset, #patch .binary, #patch .copfile {border:1px solid #ccc;margin:10px 0;} #patch ins {background:#dfd;text-decoration:none;display:block;padding:0 10px;} #patch del {background:#fdd;text-decoration:none;display:block;padding:0 10px;} #patch .lines, .info {color:#888;background:#fff;} --></style> <title>[1391] sqlalchemy/branches/schema/doc/build/content: polymorphic_ident=>polymorphic_identity</title> </head> <body> <div id="msg"> <dl> <dt>Revision</dt> <dd>1391</dd> <dt>Author</dt> <dd>zzzeek</dd> <dt>Date</dt> <dd>2006-05-04 16:36:01 -0500 (Thu, 04 May 2006)</dd> </dl> <h3>Log Message</h3> <pre>polymorphic_ident=>polymorphic_identity</pre> <h3>Modified Paths</h3> <ul> <li><a href="#sqlalchemybranchesschemadocbuildcontentadv_datamappingtxt">sqlalchemy/branches/schema/doc/build/content/adv_datamapping.txt</a></li> <li><a href="#sqlalchemybranchesschemaexamplespolymorphconcretepy">sqlalchemy/branches/schema/examples/polymorph/concrete.py</a></li> <li><a href="#sqlalchemybranchesschemaexamplespolymorphpolymorphpy">sqlalchemy/branches/schema/examples/polymorph/polymorph.py</a></li> <li><a href="#sqlalchemybranchesschemalibsqlalchemyormmapperpy">sqlalchemy/branches/schema/lib/sqlalchemy/orm/mapper.py</a></li> </ul> </div> <div id="patch"> <h3>Diff</h3> <a id="sqlalchemybranchesschemadocbuildcontentadv_datamappingtxt"></a> <div class="modfile"><h4>Modified: sqlalchemy/branches/schema/doc/build/content/adv_datamapping.txt (1390 => 1391)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/schema/doc/build/content/adv_datamapping.txt 2006-05-04 21:25:45 UTC (rev 1390) +++ sqlalchemy/branches/schema/doc/build/content/adv_datamapping.txt 2006-05-04 21:36:01 UTC (rev 1391) </span><span class="lines">@@ -411,8 +411,8 @@ </span><span class="cx"> }, 'type', 'pjoin') </span><span class="cx"> </span><span class="cx"> employee_mapper = mapper(Employee, pjoin, polymorphic_on=pjoin.c.type) </span><del>- manager_mapper = mapper(Manager, managers_table, inherits=employee_mapper, concrete=True, polymorphic_ident='manager') - engineer_mapper = mapper(Engineer, engineers_table, inherits=employee_mapper, concrete=True, polymorphic_ident='engineer') </del><ins>+ manager_mapper = mapper(Manager, managers_table, inherits=employee_mapper, concrete=True, polymorphic_identity='manager') + engineer_mapper = mapper(Engineer, engineers_table, inherits=employee_mapper, concrete=True, polymorphic_identity='engineer') </ins><span class="cx"> </span><span class="cx"> A future release of SQLALchemy might better merge the generated UNION into the mapper construction phase. </span><span class="cx"> </span><span class="lines">@@ -441,9 +441,9 @@ </span><span class="cx"> 'person':people.select(people.c.type=='person'), </span><span class="cx"> }, None, 'pjoin') </span><span class="cx"> </span><del>- person_mapper = mapper(Person, people, select_table=person_join, polymorphic_on=person_join.c.type, polymorphic_ident='person') - mapper(Engineer, engineers, inherits=person_mapper, polymorphic_ident='engineer') - mapper(Manager, managers, inherits=person_mapper, polymorphic_ident='manager') </del><ins>+ person_mapper = mapper(Person, people, select_table=person_join, polymorphic_on=person_join.c.type, polymorphic_identity='person') + mapper(Engineer, engineers, inherits=person_mapper, polymorphic_identity='engineer') + mapper(Manager, managers, inherits=person_mapper, polymorphic_identity='manager') </ins><span class="cx"> </span><span class="cx"> The join condition in an inheritance relationship can be specified explicitly, using `inherit_condition`: </span><span class="cx"> </span></span></pre></div> <a id="sqlalchemybranchesschemaexamplespolymorphconcretepy"></a> <div class="modfile"><h4>Modified: sqlalchemy/branches/schema/examples/polymorph/concrete.py (1390 => 1391)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/schema/examples/polymorph/concrete.py 2006-05-04 21:25:45 UTC (rev 1390) +++ sqlalchemy/branches/schema/examples/polymorph/concrete.py 2006-05-04 21:36:01 UTC (rev 1391) </span><span class="lines">@@ -45,8 +45,8 @@ </span><span class="cx"> }, 'type', 'pjoin') </span><span class="cx"> </span><span class="cx"> employee_mapper = mapper(Employee, pjoin, polymorphic_on=pjoin.c.type) </span><del>-manager_mapper = mapper(Manager, managers_table, inherits=employee_mapper, concrete=True, polymorphic_ident='manager') -engineer_mapper = mapper(Engineer, engineers_table, inherits=employee_mapper, concrete=True, polymorphic_ident='engineer') </del><ins>+manager_mapper = mapper(Manager, managers_table, inherits=employee_mapper, concrete=True, polymorphic_identity='manager') +engineer_mapper = mapper(Engineer, engineers_table, inherits=employee_mapper, concrete=True, polymorphic_identity='engineer') </ins><span class="cx"> </span><span class="cx"> </span><span class="cx"> session = create_session(bind_to=engine) </span></span></pre></div> <a id="sqlalchemybranchesschemaexamplespolymorphpolymorphpy"></a> <div class="modfile"><h4>Modified: sqlalchemy/branches/schema/examples/polymorph/polymorph.py (1390 => 1391)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/schema/examples/polymorph/polymorph.py 2006-05-04 21:25:45 UTC (rev 1390) +++ sqlalchemy/branches/schema/examples/polymorph/polymorph.py 2006-05-04 21:36:01 UTC (rev 1391) </span><span class="lines">@@ -63,9 +63,9 @@ </span><span class="cx"> 'person':people.select(people.c.type=='person'), </span><span class="cx"> }, None, 'pjoin') </span><span class="cx"> </span><del>-person_mapper = mapper(Person, people, select_table=person_join, polymorphic_on=person_join.c.type, polymorphic_ident='person') -mapper(Engineer, engineers, inherits=person_mapper, polymorphic_ident='engineer') -mapper(Manager, managers, inherits=person_mapper, polymorphic_ident='manager') </del><ins>+person_mapper = mapper(Person, people, select_table=person_join, polymorphic_on=person_join.c.type, polymorphic_identity='person') +mapper(Engineer, engineers, inherits=person_mapper, polymorphic_identity='engineer') +mapper(Manager, managers, inherits=person_mapper, polymorphic_identity='manager') </ins><span class="cx"> </span><span class="cx"> mapper(Company, companies, properties={ </span><span class="cx"> 'employees': relation(Person, lazy=True, private=True, backref='company') </span></span></pre></div> <a id="sqlalchemybranchesschemalibsqlalchemyormmapperpy"></a> <div class="modfile"><h4>Modified: sqlalchemy/branches/schema/lib/sqlalchemy/orm/mapper.py (1390 => 1391)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/schema/lib/sqlalchemy/orm/mapper.py 2006-05-04 21:25:45 UTC (rev 1390) +++ sqlalchemy/branches/schema/lib/sqlalchemy/orm/mapper.py 2006-05-04 21:36:01 UTC (rev 1391) </span><span class="lines">@@ -53,7 +53,7 @@ </span><span class="cx"> version_id_col = None, </span><span class="cx"> polymorphic_on=None, </span><span class="cx"> polymorphic_map=None, </span><del>- polymorphic_ident=None, </del><ins>+ polymorphic_identity=None, </ins><span class="cx"> concrete=False, </span><span class="cx"> select_table=None): </span><span class="cx"> </span><span class="lines">@@ -136,9 +136,9 @@ </span><span class="cx"> else: </span><span class="cx"> self._synchronizer = None </span><span class="cx"> self.inherits = inherits </span><del>- if polymorphic_ident is not None: - inherits.add_polymorphic_mapping(polymorphic_ident, self) - self.polymorphic_ident = polymorphic_ident </del><ins>+ if polymorphic_identity is not None: + inherits.add_polymorphic_mapping(polymorphic_identity, self) + self.polymorphic_identity = polymorphic_identity </ins><span class="cx"> if self.polymorphic_on is None: </span><span class="cx"> self.effective_polymorphic_on = inherits.effective_polymorphic_on </span><span class="cx"> else: </span><span class="lines">@@ -147,9 +147,9 @@ </span><span class="cx"> self._synchronizer = None </span><span class="cx"> self.inherits = None </span><span class="cx"> self.mapped_table = self.local_table </span><del>- if polymorphic_ident is not None: - self.add_polymorphic_mapping(polymorphic_ident, self) - self.polymorphic_ident = polymorphic_ident </del><ins>+ if polymorphic_identity is not None: + self.add_polymorphic_mapping(polymorphic_identity, self) + self.polymorphic_identity = polymorphic_identity </ins><span class="cx"> self.effective_polymorphic_on = self.polymorphic_on </span><span class="cx"> </span><span class="cx"> if select_table is not None: </span><span class="lines">@@ -608,7 +608,7 @@ </span><span class="cx"> params[col.key] = value </span><span class="cx"> elif self.effective_polymorphic_on is not None and col.original is self.effective_polymorphic_on.original: </span><span class="cx"> if isinsert: </span><del>- value = self.polymorphic_ident </del><ins>+ value = self.polymorphic_identity </ins><span class="cx"> if col.default is None or value is not None: </span><span class="cx"> params[col.key] = value </span><span class="cx"> else: </span></span></pre> </div> </div> </body> </html> |
From: <co...@sq...> - 2006-05-04 21:25:57
|
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head><style type="text/css"><!-- #msg dl { border: 1px #006 solid; background: #369; padding: 6px; color: #fff; } #msg dt { float: left; width: 6em; font-weight: bold; } #msg dt:after { content:':';} #msg dl, #msg dt, #msg ul, #msg li { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; } #msg dl a { font-weight: bold} #msg dl a:link { color:#fc3; } #msg dl a:active { color:#ff0; } #msg dl a:visited { color:#cc6; } h3 { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; font-weight: bold; } #msg pre { overflow: auto; background: #ffc; border: 1px #fc0 solid; padding: 6px; } #msg ul, pre { overflow: auto; } #patch { width: 100%; } #patch h4 {font-family: verdana,arial,helvetica,sans-serif;font-size:10pt;padding:8px;background:#369;color:#fff;margin:0;} #patch .propset h4, #patch .binary h4 {margin:0;} #patch pre {padding:0;line-height:1.2em;margin:0;} #patch .diff {width:100%;background:#eee;padding: 0 0 10px 0;overflow:auto;} #patch .propset .diff, #patch .binary .diff {padding:10px 0;} #patch span {display:block;padding:0 10px;} #patch .modfile, #patch .addfile, #patch .delfile, #patch .propset, #patch .binary, #patch .copfile {border:1px solid #ccc;margin:10px 0;} #patch ins {background:#dfd;text-decoration:none;display:block;padding:0 10px;} #patch del {background:#fdd;text-decoration:none;display:block;padding:0 10px;} #patch .lines, .info {color:#888;background:#fff;} --></style> <title>[1390] sqlalchemy/branches/schema/doc/build/content: more docs on adv.</title> </head> <body> <div id="msg"> <dl> <dt>Revision</dt> <dd>1390</dd> <dt>Author</dt> <dd>zzzeek</dd> <dt>Date</dt> <dd>2006-05-04 16:25:45 -0500 (Thu, 04 May 2006)</dd> </dl> <h3>Log Message</h3> <pre>more docs on adv. datamapping</pre> <h3>Modified Paths</h3> <ul> <li><a href="#sqlalchemybranchesschemadocbuildcontentadv_datamappingtxt">sqlalchemy/branches/schema/doc/build/content/adv_datamapping.txt</a></li> <li><a href="#sqlalchemybranchesschemaexamplespolymorphpolymorphpy">sqlalchemy/branches/schema/examples/polymorph/polymorph.py</a></li> </ul> <h3>Removed Paths</h3> <ul> <li><a href="#sqlalchemybranchesschemadocbuildcontentadv_datamappingmyt">sqlalchemy/branches/schema/doc/build/content/adv_datamapping.myt</a></li> </ul> </div> <div id="patch"> <h3>Diff</h3> <a id="sqlalchemybranchesschemadocbuildcontentadv_datamappingmyt"></a> <div class="delfile"><h4>Deleted: sqlalchemy/branches/schema/doc/build/content/adv_datamapping.myt (1389 => 1390)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/schema/doc/build/content/adv_datamapping.myt 2006-05-04 20:27:18 UTC (rev 1389) +++ sqlalchemy/branches/schema/doc/build/content/adv_datamapping.myt 2006-05-04 21:25:45 UTC (rev 1390) </span><span class="lines">@@ -1,754 +0,0 @@ </span><del>-<%flags>inherit='document_base.myt'</%flags> -<%attr>title='Advanced Data Mapping'</%attr> -<&|doclib.myt:item, name="adv_datamapping", description="Advanced Data Mapping" &> -<p>This section details all the options available to Mappers, as well as advanced patterns.</p> - -<p>To start, heres the tables we will work with again:</p> - <&|formatting.myt:code&> - from sqlalchemy import * - db = create_engine('sqlite://filename=mydb', echo=True) - - # a table to store users - users = Table('users', db, - Column('user_id', Integer, primary_key = True), - Column('user_name', String(40)), - Column('password', String(80)) - ) - - # a table that stores mailing addresses associated with a specific user - addresses = Table('addresses', db, - Column('address_id', Integer, primary_key = True), - Column('user_id', Integer, ForeignKey("users.user_id")), - Column('street', String(100)), - Column('city', String(80)), - Column('state', String(2)), - Column('zip', String(10)) - ) - - # a table that stores keywords - keywords = Table('keywords', db, - Column('keyword_id', Integer, primary_key = True), - Column('name', VARCHAR(50)) - ) - - # a table that associates keywords with users - userkeywords = Table('userkeywords', db, - Column('user_id', INT, ForeignKey("users")), - Column('keyword_id', INT, ForeignKey("keywords")) - ) - - </&> - -### Overriding Properties {@name=overriding} - -A common request is the ability to create custom class properties that override the behavior of setting/getting an attribute. Currently, the easiest way to do this in SQLAlchemy is how it would be done in any Python program; define your attribute with a different name, such as "_attribute", and use a property to get/set its value. The mapper just needs to be told of the special name: - - {python} - class MyClass(object): - def _set_email(self, email): - self._email = email - def _get_email(self, email): - return self._email - email = property(_get_email, _set_email) - - m = mapper(MyClass, mytable, properties = { - # map the '_email' attribute to the "email" column - # on the table - '_email': mytable.c.email - }) - -In a later release, SQLAlchemy will also allow _get_email and _set_email to be attached directly to the "email" property created by the mapper, and will also allow this association to occur via decorators. - -<&|doclib.myt:item, name="relations", description="More On Relations" &> - <&|doclib.myt:item, name="customjoin", description="Custom Join Conditions" &> - <p>When creating relations on a mapper, most examples so far have illustrated the mapper and relationship joining up based on the foreign keys of the tables they represent. in fact, this "automatic" inspection can be completely circumvented using the <span class="codeline">primaryjoin</span> and <span class="codeline">secondaryjoin</span> arguments to <span class="codeline">relation</span>, as in this example which creates a User object which has a relationship to all of its Addresses which are in Boston: - <&|formatting.myt:code&> - class User(object): - pass - class Address(object): - pass - Address.mapper = mapper(Address, addresses) - User.mapper = mapper(User, users, properties={ - 'boston_addreses' : relation(Address.mapper, primaryjoin= - and_(users.c.user_id==Address.c.user_id, - Addresses.c.city=='Boston')) - }) - </&> - <P>Many to many relationships can be customized by one or both of <span class="codeline">primaryjoin</span> and <span class="codeline">secondaryjoin</span>, shown below with just the default many-to-many relationship explicitly set:</p> - <&|formatting.myt:code&> - class User(object): - pass - class Keyword(object): - pass - Keyword.mapper = mapper(Keyword, keywords) - User.mapper = mapper(User, users, properties={ - 'keywords':relation(Keyword.mapper, - primaryjoin=users.c.user_id==userkeywords.c.user_id, - secondaryjoin=userkeywords.c.keyword_id==keywords.c.keyword_id - ) - }) - </&> - </&> - <&|doclib.myt:item, name="multiplejoin", description="Lazy/Eager Joins Multiple Times to One Table" &> - - <p>The previous example leads in to the idea of joining against the same table multiple times. Below is a User object that has lists of its Boston and New York addresses, both lazily loaded when they are first accessed:</p> - <&|formatting.myt:code&> - User.mapper = mapper(User, users, properties={ - 'boston_addreses' : relation(Address.mapper, primaryjoin= - and_(users.c.user_id==Address.c.user_id, - Addresses.c.city=='Boston')), - 'newyork_addresses' : relation(Address.mapper, primaryjoin= - and_(users.c.user_id==Address.c.user_id, - Addresses.c.city=='New York')), - }) - </&> - <p>A complication arises with the above pattern if you want the relations to be eager loaded. Since there will be two separate joins to the addresses table during an eager load, an alias needs to be used to separate them. You can create an alias of the addresses table to separate them, but then you are in effect creating a brand new mapper for each property, unrelated to the main Address mapper, which can create problems with commit operations. So an additional argument <span class="codeline">use_alias</span> can be used with an eager relationship to specify the alias to be used just within the eager query:</p> - <&|formatting.myt:code&> - User.mapper = mapper(User, users, properties={ - 'boston_addreses' : relation(Address.mapper, primaryjoin= - and_(User.c.user_id==Address.c.user_id, - Addresses.c.city=='Boston'), lazy=False, use_alias=True), - 'newyork_addresses' : relation(Address.mapper, primaryjoin= - and_(User.c.user_id==Address.c.user_id, - Addresses.c.city=='New York'), lazy=False, use_alias=True), - }) - - <&formatting.myt:poplink&>u = User.mapper.select() - - <&|formatting.myt:codepopper, link="sql" &> - SELECT users.user_id AS users_user_id, users.user_name AS users_user_name, - users.password AS users_password, - addresses_EF45.address_id AS addresses_EF45_address_id, addresses_EF45.user_id AS addresses_EF45_user_id, - addresses_EF45.street AS addresses_EF45_street, addresses_EF45.city AS addresses_EF45_city, - addresses_EF45.state AS addresses_EF45_state, addresses_EF45.zip AS addresses_EF45_zip, - addresses_63C5.address_id AS addresses_63C5_address_id, addresses_63C5.user_id AS addresses_63C5_user_id, - addresses_63C5.street AS addresses_63C5_street, addresses_63C5.city AS addresses_63C5_city, - addresses_63C5.state AS addresses_63C5_state, addresses_63C5.zip AS addresses_63C5_zip - FROM users - LEFT OUTER JOIN addresses AS addresses_EF45 ON users.user_id = addresses_EF45.user_id - AND addresses_EF45.city = :addresses_city - LEFT OUTER JOIN addresses AS addresses_63C5 ON users.user_id = addresses_63C5.user_id - AND addresses_63C5.city = :addresses_city_1 - ORDER BY users.oid, addresses_EF45.oid, addresses_63C5.oid - {'addresses_city_1': 'New York', 'addresses_city': 'Boston'} - </&> - </&> - </&> - - <&|doclib.myt:item, name="relationoptions", description="Relation Options" &> - Keyword options to the <span class="codeline">relation</span> function include: - <ul> - <li>lazy=(True|False|None) - specifies how the related items should be loaded. a value of True indicates they should be loaded when the property is first accessed. A value of False indicates they should be loaded by joining against the parent object query, so parent and child are loaded in one round trip. A value of None indicates the related items are not loaded by the mapper in any case; the application will manually insert items into the list in some other way. A relationship with lazy=None is still important; items added to the list or removed will cause the appropriate updates and deletes upon commit().</li> - <li>primaryjoin - a ClauseElement that will be used as the primary join of this child object against the parent object, or in a many-to-many relationship the join of the primary object to the association table. By default, this value is computed based on the foreign key relationships of the parent and child tables (or association table).</li> - <li>secondaryjoin - a ClauseElement that will be used as the join of an association table to the child object. By default, this value is computed based on the foreign key relationships of the association and child tables.</li> - <li>foreignkey - specifies which column in this relationship is "foreign", i.e. which column refers to the parent object. This value is automatically determined in all cases, based on the primary and secondary join conditions, except in the case of a self-referential mapper, where it is needed to indicate the child object's reference back to it's parent.</li> - <li>uselist - a boolean that indicates if this property should be loaded as a list or a scalar. In most cases, this value is determined based on the type and direction of the relationship - one to many forms a list, one to one forms a scalar, many to many is a list. If a scalar is desired where normally a list would be present, set uselist to False.</li> - <li>private - indicates if these child objects are "private" to the parent; removed items will also be deleted, and if the parent item is deleted, all child objects are deleted as well. See the example in <&formatting.myt:link, path="datamapping_relations_private"&>.</li> - <li>backreference - indicates the name of a property to be placed on the related mapper's class that will handle this relationship in the other direction, including synchronizing the object attributes on both sides of the relation. See the example in <&formatting.myt:link, path="datamapping_relations_backreferences"&>.</li> - <li>order_by - indicates the ordering that should be applied when loading these items. See the section <&formatting.myt:link, path="adv_datamapping_orderby" &> for details.</li> - <li>association - When specifying a many to many relationship with an association object, this keyword should reference the mapper of the target object of the association. See the example in <&formatting.myt:link, path="datamapping_association"&>.</li> - <li>post_update - this indicates that the relationship should be handled by a second UPDATE statement after an INSERT, or before a DELETE. using this flag essentially means the relationship will not incur any "dependency" between parent and child item, as the particular foreign key relationship between them is handled by a second statement. use this flag when a particular mapping arrangement will incur two rows that are dependent on each other, such as a table that has a one-to-many relationship to a set of child rows, and also has a column that references a single child row within that list (i.e. both tables contain a foreign key to each other). If a commit() operation returns an error that a "cyclical dependency" was detected, this is a cue that you might want to use post_update.</li> - </ul> - </&> - -</&> -<&|doclib.myt:item, name="orderby", description="Controlling Ordering" &> -<p>By default, mappers will not supply any ORDER BY clause when selecting rows. This can be modified in several ways.</p> - -<p>A "default ordering" can be supplied by all mappers, by enabling the "default_ordering" flag to the engine, which indicates that table primary keys or object IDs should be used as the default ordering:</p> -<&|formatting.myt:code&> - db = create_engine('postgres://username=scott&password=tiger', default_ordering=True) -</&> -<p>The "order_by" parameter can be sent to a mapper, overriding the per-engine ordering if any. A value of None means that the mapper should not use any ordering, even if the engine's default_ordering property is True. A non-None value, which can be a column, an <span class="codeline">asc</span> or <span class="codeline">desc</span> clause, or an array of either one, indicates the ORDER BY clause that should be added to all select queries:</p> -<&|formatting.myt:code&> - # disable all ordering - mapper = mapper(User, users, order_by=None) - - # order by a column - mapper = mapper(User, users, order_by=users.c.user_id) - - # order by multiple items - mapper = mapper(User, users, order_by=[users.c.user_id, desc(users.c.user_name)]) -</&> -<p>"order_by" can also be specified to an individual <span class="codeline">select</span> method, overriding all other per-engine/per-mapper orderings: -<&|formatting.myt:code&> - # order by a column - l = mapper.select(users.c.user_name=='fred', order_by=users.c.user_id) - - # order by multiple criterion - l = mapper.select(users.c.user_name=='fred', order_by=[users.c.user_id, desc(users.c.user_name)]) -</&> -<p>For relations, the "order_by" property can also be specified to all forms of relation:</p> -<&|formatting.myt:code&> - # order address objects by address id - mapper = mapper(User, users, properties = { - 'addresses' : relation(mapper(Address, addresses), order_by=addresses.c.address_id) - }) - - # eager load with ordering - the ORDER BY clauses of parent/child will be organized properly - mapper = mapper(User, users, properties = { - 'addresses' : relation(mapper(Address, addresses), order_by=desc(addresses.c.email_address), eager=True) - }, order_by=users.c.user_id) - -</&> -</&> -<&|doclib.myt:item, name="limits", description="Limiting Rows" &> -<p>You can limit rows in a regular SQL query by specifying <span class="codeline">limit</span> and <span class="codeline">offset</span>. A Mapper can handle the same concepts:</p> -<&|formatting.myt:code&> - class User(object): - pass - - m = mapper(User, users) -<&formatting.myt:poplink&>r = m.select(limit=20, offset=10) -<&|formatting.myt:codepopper, link="sql" &>SELECT users.user_id AS users_user_id, -users.user_name AS users_user_name, users.password AS users_password -FROM users ORDER BY users.oid - LIMIT 20 OFFSET 10 -{} -</&> -</&> -However, things get tricky when dealing with eager relationships, since a straight LIMIT of rows does not represent the count of items when joining against other tables to load related items as well. So here is what SQLAlchemy will do when you use limit or offset with an eager relationship: - <&|formatting.myt:code&> - class User(object): - pass - class Address(object): - pass - m = mapper(User, users, properties={ - 'addresses' : relation(mapper(Address, addresses), lazy=False) - }) - r = m.select(User.c.user_name.like('F%'), limit=20, offset=10) -<&|formatting.myt:poppedcode, link="sql" &> -SELECT users.user_id AS users_user_id, users.user_name AS users_user_name, -users.password AS users_password, addresses.address_id AS addresses_address_id, -addresses.user_id AS addresses_user_id, addresses.street AS addresses_street, -addresses.city AS addresses_city, addresses.state AS addresses_state, -addresses.zip AS addresses_zip -FROM -(SELECT users.user_id FROM users WHERE users.user_name LIKE %(users_user_name)s -ORDER BY users.oid LIMIT 20 OFFSET 10) AS rowcount, - users LEFT OUTER JOIN addresses ON users.user_id = addresses.user_id -WHERE rowcount.user_id = users.user_id ORDER BY users.oid, addresses.oid -{'users_user_name': 'F%'} - - </&> - </&> - <p>The main WHERE clause as well as the limiting clauses are coerced into a subquery; this subquery represents the desired result of objects. A containing query, which handles the eager relationships, is joined against the subquery to produce the result.</p> -</&> -<&|doclib.myt:item, name="colname", description="Overriding Column Names" &> -<p>When mappers are constructed, by default the column names in the Table metadata are used as the names of attributes on the mapped class. This can be customzed within the properties by stating the key/column combinations explicitly:</p> -<&|formatting.myt:code&> - user_mapper = mapper(User, users, properties={ - 'id' : users.c.user_id, - 'name' : users.c.user_name, - }) -</&> -<p>In the situation when column names overlap in a mapper against multiple tables, columns may be referenced together with a list: -<&|formatting.myt:code&> - # join users and addresses - usersaddresses = sql.join(users, addresses, users.c.user_id == addresses.c.user_id) - m = mapper(User, usersaddresses, - properties = { - 'id' : [users.c.user_id, addresses.c.user_id], - } - ) -</&> -</&> -<&|doclib.myt:item, name="deferred", description="Deferred Column Loading" &> -<p>This feature allows particular columns of a table to not be loaded by default, instead being loaded later on when first referenced. It is essentailly "column-level lazy loading". This feature is useful when one wants to avoid loading a large text or binary field into memory when its not needed. Individual columns can be lazy loaded by themselves or placed into groups that lazy-load together.</p> -<&|formatting.myt:code&> - book_excerpts = Table('books', db, - Column('book_id', Integer, primary_key=True), - Column('title', String(200), nullable=False), - Column('summary', String(2000)), - Column('excerpt', String), - Column('photo', Binary) - ) - - class Book(object): - pass - - # define a mapper that will load each of 'excerpt' and 'photo' in - # separate, individual-row SELECT statements when each attribute - # is first referenced on the individual object instance - book_mapper = mapper(Book, book_excerpts, properties = { - 'excerpt' : deferred(book_excerpts.c.excerpt), - 'photo' : deferred(book_excerpts.c.photo) - }) -</&> -<p>Deferred columns can be placed into groups so that they load together:</p> -<&|formatting.myt:code&> - book_excerpts = Table('books', db, - Column('book_id', Integer, primary_key=True), - Column('title', String(200), nullable=False), - Column('summary', String(2000)), - Column('excerpt', String), - Column('photo1', Binary), - Column('photo2', Binary), - Column('photo3', Binary) - ) - - class Book(object): - pass - - # define a mapper with a 'photos' deferred group. when one photo is referenced, - # all three photos will be loaded in one SELECT statement. The 'excerpt' will - # be loaded separately when it is first referenced. - book_mapper = mapper(Book, book_excerpts, properties = { - 'excerpt' : deferred(book_excerpts.c.excerpt), - 'photo1' : deferred(book_excerpts.c.photo1, group='photos'), - 'photo2' : deferred(book_excerpts.c.photo2, group='photos'), - 'photo3' : deferred(book_excerpts.c.photo3, group='photos') - }) -</&> -</&> -<&|doclib.myt:item, name="options", description="More on Mapper Options" &> - <p>The <span class="codeline">options</span> method of mapper, first introduced in <&formatting.myt:link, path="datamapping_relations_options" &>, supports the copying of a mapper into a new one, with any number of its relations replaced by new ones. The method takes a variable number of <span class="codeline">MapperOption</span> objects which know how to change specific things about the mapper. The five available options are <span class="codeline">eagerload</span>, <span class="codeline">lazyload</span>, <span class="codeline">noload</span>, <span class="codeline">deferred</span> and <span class="codeline">extension</span>.</p> - <P>An example of a mapper with a lazy load relationship, upgraded to an eager load relationship: - <&|formatting.myt:code&> - class User(object): - pass - class Address(object): - pass - - # a 'lazy' relationship - User.mapper = mapper(User, users, properties = { - 'addreses':relation(mapper(Address, addresses), lazy=True) - }) - - # copy the mapper and convert 'addresses' to be eager - eagermapper = User.mapper.options(eagerload('addresses')) - </&> - - <p>The load options also can take keyword arguments that apply to the new relationship. To take the "double" address lazy relationship from the previous section and upgrade it to eager, adding the "selectalias" keywords as well:</p> - <&|formatting.myt:code&> - m = User.mapper.options( - eagerload('boston_addresses', selectalias='boston_ad'), - eagerload('newyork_addresses', selectalias='newyork_ad') - ) - </&> - <p>The <span class="codeline">defer</span> and <span class="codeline">undefer</span> options can control the deferred loading of attributes:</p> - <&|formatting.myt:code&> - # set the 'excerpt' deferred attribute to load normally - m = book_mapper.options(undefer('excerpt')) - - # set the referenced mapper 'photos' to defer its loading of the column 'imagedata' - m = book_mapper.options(defer('photos.imagedata')) - </&> - <p>Options can also take a limited set of keyword arguments which will be applied to a new mapper. For example, to create a mapper that refreshes all objects loaded each time:</p> - <&|formatting.myt:code&> - m2 = mapper.options(always_refresh=True) - </&> - <p>Or, a mapper with different ordering:</p> - <&|formatting.myt:code&> - m2 = mapper.options(order_by=[newcol]) - </&> - -</&> - - -<&|doclib.myt:item, name="inheritance", description="Mapping a Class with Table Inheritance" &> - - <p>Table Inheritance indicates the pattern where two tables, in a parent-child relationship, are mapped to an inheritance chain of classes. If a table "employees" contains additional information about managers in the table "managers", a corresponding object inheritance pattern would have an Employee class and a Manager class. Loading a Manager object means you are joining managers to employees. For SQLAlchemy, this pattern is just a special case of a mapper that maps against a joined relationship, and is provided via the <span class="codeline">inherits</span> keyword. - <&|formatting.myt:code&> - class User(object): - """a user object.""" - pass - User.mapper = mapper(User, users) - - class AddressUser(User): - """a user object that also has the users mailing address.""" - pass - - # define a mapper for AddressUser that inherits the User.mapper, and joins on the user_id column - AddressUser.mapper = mapper( - AddressUser, - addresses, inherits=User.mapper - ) - - items = AddressUser.mapper.select() - </&> -<P>Above, the join condition is determined via the foreign keys between the users and the addresses table. To specify the join condition explicitly, use <span class="codeline">inherit_condition</span>: -<&|formatting.myt:code&> - AddressUser.mapper = mapper( - AddressUser, - addresses, inherits=User.mapper, - inherit_condition=users.c.user_id==addresses.c.user_id - ) -</&> -</&> - -<&|doclib.myt:item, name="joins", description="Mapping a Class against Multiple Tables" &> - <P>The more general case of the pattern described in "table inheritance" is a mapper that maps against more than one table. The <span class="codeline">join</span> keyword from the SQL package creates a neat selectable unit comprised of multiple tables, complete with its own composite primary key, which can be passed in to a mapper as the table.</p> - <&|formatting.myt:code&> - # a class - class AddressUser(object): - pass - - # define a Join - j = join(users, addresses) - - # map to it - the identity of an AddressUser object will be - # based on (user_id, address_id) since those are the primary keys involved - m = mapper(AddressUser, j) - </&> - - A second example: - <&|formatting.myt:code&> - # many-to-many join on an association table - j = join(users, userkeywords, - users.c.user_id==userkeywords.c.user_id).join(keywords, - userkeywords.c.keyword_id==keywords.c.keyword_id) - - # a class - class KeywordUser(object): - pass - - # map to it - the identity of a KeywordUser object will be - # (user_id, keyword_id) since those are the primary keys involved - m = mapper(KeywordUser, j) - </&> -</&> -<&|doclib.myt:item, name="selects", description="Mapping a Class against Arbitary Selects" &> -<p>Similar to mapping against a join, a plain select() object can be used with a mapper as well. Below, an example select which contains two aggregate functions and a group_by is mapped to a class:</p> - <&|formatting.myt:code&> - s = select([customers, - func.count(orders).label('order_count'), - func.max(orders.price).label('highest_order')], - customers.c.customer_id==orders.c.customer_id, - group_by=[c for c in customers.c] - ) - class Customer(object): - pass - - mapper = mapper(Customer, s) - </&> -<p>Above, the "customers" table is joined against the "orders" table to produce a full row for each customer row, the total count of related rows in the "orders" table, and the highest price in the "orders" table, grouped against the full set of columns in the "customers" table. That query is then mapped against the Customer class. New instances of Customer will contain attributes for each column in the "customers" table as well as an "order_count" and "highest_order" attribute. Updates to the Customer object will only be reflected in the "customers" table and not the "orders" table. This is because the primary keys of the "orders" table are not represented in this mapper and therefore the table is not affected by save or delete operations.</p> -</&> -<&|doclib.myt:item, name="multiple", description="Multiple Mappers for One Class" &> - <p>By now it should be apparent that the mapper defined for a class is in no way the only mapper that exists for that class. Other mappers can be created at any time; either explicitly or via the <span class="codeline">options</span> method, to provide different loading behavior.</p> - - <p>However, its not as simple as that. The mapper serves a dual purpose; one is to generate select statements and load objects from executing those statements; the other is to keep track of the defined dependencies of that object when save and delete operations occur, and to extend the attributes of the object so that they store information about their history and communicate with the unit of work system. For this reason, it is a good idea to be aware of the behavior of multiple mappers. When creating dependency relationships between objects, one should insure that only the primary mappers are used in those relationships, else deep object traversal operations will fail to load in the expected properties, and update operations will not take all the dependencies into account. </p> - - <p>Generally its as simple as, the <i>first</i> mapper that is defined for a particular class is the one that gets to define that classes' relationships to other mapped classes, and also decorates its attributes and constructors with special behavior. Any subsequent mappers created for that class will be able to load new instances, but object manipulation operations will still function via the original mapper. The special keyword <span class="codeline">is_primary</span> will override this behavior, and make any mapper the new "primary" mapper. - </p> - <&|formatting.myt:code&> - class User(object): - pass - - # mapper one - mark it as "primary", meaning this mapper will handle - # saving and class-level properties - m1 = mapper(User, users, is_primary=True) - - # mapper two - this one will also eager-load address objects in - m2 = mapper(User, users, properties={ - 'addresses' : relation(mapper(Address, addresses), lazy=False) - }) - - # get a user. this user will not have an 'addreses' property - u1 = m1.select(User.c.user_id==10) - - # get another user. this user will have an 'addreses' property. - u2 = m2.select(User.c.user_id==27) - - # make some modifications, including adding an Address object. - u1.user_name = 'jack' - u2.user_name = 'jane' - u2.addresses.append(Address('123 green street')) - - # upon commit, the User objects will be saved. - # the Address object will not, since the primary mapper for User - # does not have an 'addresses' relationship defined - objectstore.commit() - </&> -</&> -<&|doclib.myt:item, name="circular", description="Circular Mapping" &> -<p>Oftentimes it is necessary for two mappers to be related to each other. With a datamodel that consists of Users that store Addresses, you might have an Address object and want to access the "user" attribute on it, or have a User object and want to get the list of Address objects. The easiest way to do this is via the <span class="codeline">backreference</span> keyword described in <&formatting.myt:link, path="datamapping_relations_backreferences"&>. Although even when backreferences are used, it is sometimes necessary to explicitly specify the relations on both mappers pointing to each other.</p> -<p>To achieve this involves creating the first mapper by itself, then creating the second mapper referencing the first, then adding references to the first mapper to reference the second:</p> -<&|formatting.myt:code&> - class User(object): - pass - class Address(object): - pass - User.mapper = mapper(User, users) - Address.mapper = mapper(Address, addresses, properties={ - 'user':relation(User.mapper) - }) - User.mapper.add_property('addresses', relation(Address.mapper)) -</&> -<p>Note that with a circular relationship as above, you cannot declare both relationships as "eager" relationships, since that produces a circular query situation which will generate a recursion exception. So what if you want to load an Address and its User eagerly? Just make a second mapper using options: -<&|formatting.myt:code&> - eagermapper = Address.mapper.options(eagerload('user')) - s = eagermapper.select(Address.c.address_id==12) -</&> -</&> -<&|doclib.myt:item, name="recursive", description="Self Referential Mappers" &> -<p>A self-referential mapper is a mapper that is designed to operate with an <b>adjacency list</b> table. This is a table that contains one or more foreign keys back to itself, and is usually used to create hierarchical tree structures. SQLAlchemy's default model of saving items based on table dependencies is not sufficient in this case, as an adjacency list table introduces dependencies between individual rows. Fortunately, SQLAlchemy will automatically detect a self-referential mapper and do the extra lifting to make it work. </p> - <&|formatting.myt:code&> - # define a self-referential table - trees = Table('treenodes', engine, - Column('node_id', Integer, primary_key=True), - Column('parent_node_id', Integer, ForeignKey('treenodes.node_id'), nullable=True), - Column('node_name', String(50), nullable=False), - ) - - # treenode class - class TreeNode(object): - pass - - # mapper defines "children" property, pointing back to TreeNode class, - # with the mapper unspecified. it will point back to the primary - # mapper on the TreeNode class. - TreeNode.mapper = mapper(TreeNode, trees, properties={ - 'children' : relation( - TreeNode, - private=True - ), - } - ) - - # or, specify the circular relationship after establishing the original mapper: - mymapper = mapper(TreeNode, trees) - - mymapper.add_property('children', relation( - mymapper, - private=True - )) - - </&> - <p>This kind of mapper goes through a lot of extra effort when saving and deleting items, to determine the correct dependency graph of nodes within the tree.</p> - - <p>A self-referential mapper where there is more than one relationship on the table requires that all join conditions be explicitly spelled out. Below is a self-referring table that contains a "parent_node_id" column to reference parent/child relationships, and a "root_node_id" column which points child nodes back to the ultimate root node:</p> - <&|formatting.myt:code&> - # define a self-referential table with several relations - trees = Table('treenodes', engine, - Column('node_id', Integer, primary_key=True), - Column('parent_node_id', Integer, ForeignKey('treenodes.node_id'), nullable=True), - Column('root_node_id', Integer, ForeignKey('treenodes.node_id'), nullable=True), - Column('node_name', String(50), nullable=False), - ) - - # treenode class - class TreeNode(object): - pass - - # define the "children" property as well as the "root" property - TreeNode.mapper = mapper(TreeNode, trees, properties={ - 'children' : relation( - TreeNode, - primaryjoin=trees.c.parent_node_id==trees.c.node_id - private=True - ), - 'root' : relation( - TreeNode, - primaryjoin=trees.c.root_node_id=trees.c.node_id, - foreignkey=trees.c.node_id, - uselist=False - ) - } - ) - </&> -<p>The "root" property on a TreeNode is a many-to-one relationship. By default, a self-referential mapper declares relationships as one-to-many, so the extra parameter <span class="codeline">foreignkey</span>, pointing to the "many" side of a relationship, is needed to indicate a "many-to-one" self-referring relationship.</p> -<p>Both TreeNode examples above are available in functional form in the <span class="codeline">examples/adjacencytree</span> directory of the distribution.</p> -</&> -<&|doclib.myt:item, name="resultset", description="Result-Set Mapping" &> - <p>Take any result set and feed it into a mapper to produce objects. Multiple mappers can be combined to retrieve unrelated objects from the same row in one step. The <span class="codeline">instances</span> method on mapper takes a ResultProxy object, which is the result type generated from SQLEngine, and delivers object instances.</p> - <&|formatting.myt:code, title="single object"&> - class User(object): - pass - - User.mapper = mapper(User, users) - - # select users - c = users.select().execute() - - # get objects - userlist = User.mapper.instances(c) - </&> - - <&|formatting.myt:code, title="multiple objects"&> - # define a second class/mapper - class Address(object): - pass - - Address.mapper = mapper(Address, addresses) - - # select users and addresses in one query - s = select([users, addresses], users.c.user_id==addresses.c.user_id) - - # execute it, and process the results with the User mapper, chained to the Address mapper - r = User.mapper.instances(s.execute(), Address.mapper) - - # result rows are an array of objects, one for each mapper used - for entry in r: - user = r[0] - address = r[1] - </&> -</&> -<&|doclib.myt:item, name="arguments", description="Mapper Arguments" &> -<p>Other arguments not covered above include:</p> -<ul> - <li>version_id_col=None - an integer-holding Column object that will be assigned an incrementing - counter, which is added to the WHERE clause used by UPDATE and DELETE statements. The matching row - count returned by the database is compared to the expected row count, and an exception is raised if they dont match. This is a basic "optimistic concurrency" check. Without the version id column, SQLAlchemy still compares the updated rowcount.</li> - <li>always_refresh=False - this option will cause the mapper to refresh all the attributes of all objects loaded by select/get statements, regardless of if they already exist in the current session. this includes all lazy- and eager-loaded relationship attributes, and will also overwrite any changes made to attributes on the column.</li> - <li>entity_name=None - this is an optional "entity name" that will be appended to the key used to associate classes to this mapper. What this basically means is, several primary mappers can be made against the same class by using different entity names; object instances will have the entity name tagged to them, so that all operations will occur on them relative to that mapper. When instantiating new objects, use <code>_sa_entity='name'</code> to tag them to the appropriate mapper.</li> -</ul> -</&> -<&|doclib.myt:item, name="extending", description="Extending Mapper" &> -<p>Mappers can have functionality augmented or replaced at many points in its execution via the usage of the MapperExtension class. This class is just a series of "hooks" where various functionality takes place. An application can make its own MapperExtension objects, overriding only the methods it needs. - <&|formatting.myt:code&> - class MapperExtension(object): - def create_instance(self, mapper, row, imap, class_): - """called when a new object instance is about to be created from a row. - the method can choose to create the instance itself, or it can return - None to indicate normal object creation should take place. - - mapper - the mapper doing the operation - row - the result row from the database - imap - a dictionary that is storing the running set of objects collected from the - current result set - class_ - the class we are mapping. - """ - def append_result(self, mapper, row, imap, result, instance, isnew, populate_existing=False): - """called when an object instance is being appended to a result list. - - If it returns True, it is assumed that this method handled the appending itself. - - mapper - the mapper doing the operation - row - the result row from the database - imap - a dictionary that is storing the running set of objects collected from the - current result set - result - an instance of util.HistoryArraySet(), which may be an attribute on an - object if this is a related object load (lazy or eager). use result.append_nohistory(value) - to append objects to this list. - instance - the object instance to be appended to the result - isnew - indicates if this is the first time we have seen this object instance in the current result - set. if you are selecting from a join, such as an eager load, you might see the same object instance - many times in the same result set. - populate_existing - usually False, indicates if object instances that were already in the main - identity map, i.e. were loaded by a previous select(), get their attributes overwritten - """ - def before_insert(self, mapper, instance): - """called before an object instance is INSERTed into its table. - - this is a good place to set up primary key values and such that arent handled otherwise.""" - def after_insert(self, mapper, instance): - """called after an object instance has been INSERTed""" - def before_delete(self, mapper, instance): - """called before an object instance is DELETEed""" - - </&> - <p>To use MapperExtension, make your own subclass of it and just send it off to a mapper:</p> - <&|formatting.myt:code&> - mapper = mapper(User, users, extension=MyExtension()) - </&> - <p>An existing mapper can create a copy of itself using an extension via the <span class="codeline">extension</span> option: - <&|formatting.myt:code&> - extended_mapper = mapper.options(extension(MyExtension())) - </&> - -</&> -<&|doclib.myt:item, name="class", description="How Mapper Modifies Mapped Classes" &> -<p>This section is a quick summary of what's going on when you send a class to the <span class="codeline">mapper()</span> function. This material, not required to be able to use SQLAlchemy, is a little more dense and should be approached patiently!</p> - -<p>The primary changes to a class that is mapped involve attaching property objects to it which represent table columns. These property objects essentially track changes. In addition, the __init__ method of the object is decorated to track object creates.</p> -<p>Here is a quick rundown of all the changes in code form: - <&|formatting.myt:code&> - # step 1 - override __init__ to 'register_new' with the Unit of Work - oldinit = myclass.__init__ - def init(self, *args, **kwargs): - nohist = kwargs.pop('_mapper_nohistory', False) - oldinit(self, *args, **kwargs) - if not nohist: - # register_new with Unit Of Work - objectstore.uow().register_new(self) - myclass.__init__ = init - - # step 2 - set a string identifier that will - # locate the classes' primary mapper - myclass._mapper = mapper.hashkey - - # step 3 - add column accessor - myclass.c = mapper.columns - - # step 4 - attribute decorating. - # this happens mostly within the package sqlalchemy.attributes - - # this dictionary will store a series of callables - # that generate "history" containers for - # individual object attributes - myclass._class_managed_attributes = {} - - # create individual properties for each column - - # these objects know how to talk - # to the attribute package to create appropriate behavior. - # the next example examines the attributes package more closely. - myclass.column1 = SmartProperty().property('column1', uselist=False) - myclass.column2 = SmartProperty().property('column2', uselist=True) - </&> -<p>The attribute package is used when save operations occur to get a handle on modified values. In the example below, -a full round-trip attribute tracking operation is illustrated:</p> -<&|formatting.myt:code&> - import sqlalchemy.attributes as attributes - - # create an attribute manager. - # the sqlalchemy.mapping package keeps one of these around as - # 'objectstore.global_attributes' - manager = attributes.AttributeManager() - - # regular old new-style class - class MyClass(object): - pass - - # register a scalar and a list attribute - manager.register_attribute(MyClass, 'column1', uselist=False) - manager.register_attribute(MyClass, 'column2', uselist=True) - - # create/modify an object - obj = MyClass() - obj.column1 = 'this is a new value' - obj.column2.append('value 1') - obj.column2.append('value 2') - - # get history objects - col1_history = manager.get_history(obj, 'column1') - col2_history = manager.get_history(obj, 'column2') - - # whats new ? - >>> col1_history.added_items() - ['this is a new value'] - - >>> col2_history.added_items() - ['value1', 'value2'] - - # commit changes - manager.commit(obj) - - # the new values become the "unchanged" values - >>> col1_history.added_items() - [] - - >>> col1_history.unchanged_items() - ['this is a new value'] - - >>> col2_history.added_items() - [] - - >>> col2_history.unchanged_items() - ['value1', 'value2'] -</&> -<p>The above AttributeManager also includes a method <span class="codeline">value_changed</span> which is triggered whenever change events occur on the managed object attributes. The Unit of Work (objectstore) package overrides this method in order to receive change events; its essentially this:</p> -<&|formatting.myt:code&> - import sqlalchemy.attributes as attributes - class UOWAttributeManager(attributes.AttributeManager): - def value_changed(self, obj, key, value): - if hasattr(obj, '_instance_key'): - uow().register_dirty(obj) - else: - uow().register_new(obj) - - global_attributes = UOWAttributeManager() -</&> -<p>Objects that contain the attribute "_instance_key" are already registered with the Identity Map, and are assumed to have come from the database. They therefore get marked as "dirty" when changes happen. Objects without an "_instance_key" are not from the database, and get marked as "new" when changes happen, although usually this will already have occured via the object's __init__ method.</p> -</&> -</&> </del></span></pre></div> <a id="sqlalchemybranchesschemadocbuildcontentadv_datamappingtxt"></a> <div class="modfile"><h4>Modified: sqlalchemy/branches/schema/doc/build/content/adv_datamapping.txt (1389 => 1390)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/schema/doc/build/content/adv_datamapping.txt 2006-05-04 20:27:18 UTC (rev 1389) +++ sqlalchemy/branches/schema/doc/build/content/adv_datamapping.txt 2006-05-04 21:25:45 UTC (rev 1390) </span><span class="lines">@@ -119,8 +119,6 @@ </span><span class="cx"> }) </span><span class="cx"> </span><span class="cx"> {sql}u = User.mapper.select() </span><del>- - <&|formatting.myt:codepopper, link="sql" &> </del><span class="cx"> SELECT users.user_id AS users_user_id, users.user_name AS users_user_name, </span><span class="cx"> users.password AS users_password, </span><span class="cx"> addresses_EF45.address_id AS addresses_EF45_address_id, addresses_EF45.user_id AS addresses_EF45_user_id, </span><span class="lines">@@ -147,8 +145,8 @@ </span><span class="cx"> * secondaryjoin - a ClauseElement that will be used as the join of an association table to the child object. By default, this value is computed based on the foreign key relationships of the association and child tables. </span><span class="cx"> * foreignkey - specifies which column in this relationship is "foreign", i.e. which column refers to the parent object. This value is automatically determined in all cases, based on the primary and secondary join conditions, except in the case of a self-referential mapper, where it is needed to indicate the child object's reference back to it's parent. </span><span class="cx"> * uselist - a boolean that indicates if this property should be loaded as a list or a scalar. In most cases, this value is determined based on the type and direction of the relationship - one to many forms a list, one to one forms a scalar, many to many is a list. If a scalar is desired where normally a list would be present, set uselist to False. </span><del>-* private - indicates if these child objects are "private" to the parent; removed items will also be deleted, and if the parent item is deleted, all child objects are deleted as well. See the example in <&formatting.myt:link, path="datamapping_relations_private"&>. -* backreference - indicates the name of a property to be placed on the related mapper's class that will handle this relationship in the other direction, including synchronizing the object attributes on both sides of the relation. See the example in <&formatting.myt:link, path="datamapping_relations_backreferences"&>. </del><ins>+* private - indicates if these child objects are "private" to the parent; removed items will also be deleted, and if the parent item is deleted, all child objects are deleted as well. See the example in [datamapping_relations_private](rel:datamapping_relations_private). +* backreference - indicates the name of a property to be placed on the related mapper's class that will handle this relationship in the other direction, including synchronizing the object attributes on both sides of the relation. See the example in [datamapping_relations_backreferences](rel:datamapping_relations_backreferences). </ins><span class="cx"> * order_by - indicates the ordering that should be applied when loading these items. See the section [advdatamapping_orderby](rel:advdatamapping_orderby) for details. </span><span class="cx"> * association - When specifying a many to many relationship with an association object, this keyword should reference the mapper of the target object of the association. See the example in [datamapping_association](rel:datamapping_association). </span><span class="cx"> * post_update - this indicates that the relationship should be handled by a second UPDATE statement after an INSERT, or before a DELETE. using this flag essentially means the relationship will not incur any "dependency" between parent and child item, as the particular foreign key relationship between them is handled by a second statement. use this flag when a particular mapping arrangement will incur two rows that are dependent on each other, such as a table that has a one-to-many relationship to a set of child rows, and also has a column that references a single child row within that list (i.e. both tables contain a foreign key to each other). If a flush() operation returns an error that a "cyclical dependency" was detected, this is a cue that you might want to use post_update. </span><span class="lines">@@ -340,6 +338,8 @@ </span><span class="cx"> </span><span class="cx"> SQLAlchemy supports all three kinds of inheritance. Additionally, true `polymorphic` loading is supported in a straightfoward way for single table inheritance, and has some more manually-configured features that can make it happen for concrete and multiple table inheritance. </span><span class="cx"> </span><ins>+Working examples of polymorphic inheritance come with the distribution in the directory `examples/polymorphic`. + </ins><span class="cx"> Here are the classes we will use to represent an inheritance relationship: </span><span class="cx"> </span><span class="cx"> {python} </span><span class="lines">@@ -402,189 +402,209 @@ </span><span class="cx"> manager_mapper = mapper(Manager, managers_table) </span><span class="cx"> engineer_mapper = mapper(Engineer, engineers_table) </span><span class="cx"> </span><del>-With polymorphic loading, the SQL query to do the actual polymorphic load must be constructed, usually as a UNION. Additionally, each query composed into the UNION must have the same set of column names, as well as an extra column to indicate the "polymorphic identity" of each row: </del><ins>+With polymorphic loading, the SQL query to do the actual polymorphic load must be constructed, usually as a UNION. There is a helper function to create these UNIONS called `polymorphic_union`. </ins><span class="cx"> </span><span class="cx"> {python} </span><del>- colnames = ['employee_id', 'name', 'manager_data', 'engineer_info'] - def col(name, table): - try: - return table.c[name] - exce... [truncated message content] |
From: <co...@sq...> - 2006-05-04 20:27:28
|
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head><style type="text/css"><!-- #msg dl { border: 1px #006 solid; background: #369; padding: 6px; color: #fff; } #msg dt { float: left; width: 6em; font-weight: bold; } #msg dt:after { content:':';} #msg dl, #msg dt, #msg ul, #msg li { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; } #msg dl a { font-weight: bold} #msg dl a:link { color:#fc3; } #msg dl a:active { color:#ff0; } #msg dl a:visited { color:#cc6; } h3 { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; font-weight: bold; } #msg pre { overflow: auto; background: #ffc; border: 1px #fc0 solid; padding: 6px; } #msg ul, pre { overflow: auto; } #patch { width: 100%; } #patch h4 {font-family: verdana,arial,helvetica,sans-serif;font-size:10pt;padding:8px;background:#369;color:#fff;margin:0;} #patch .propset h4, #patch .binary h4 {margin:0;} #patch pre {padding:0;line-height:1.2em;margin:0;} #patch .diff {width:100%;background:#eee;padding: 0 0 10px 0;overflow:auto;} #patch .propset .diff, #patch .binary .diff {padding:10px 0;} #patch span {display:block;padding:0 10px;} #patch .modfile, #patch .addfile, #patch .delfile, #patch .propset, #patch .binary, #patch .copfile {border:1px solid #ccc;margin:10px 0;} #patch ins {background:#dfd;text-decoration:none;display:block;padding:0 10px;} #patch del {background:#fdd;text-decoration:none;display:block;padding:0 10px;} #patch .lines, .info {color:#888;background:#fff;} --></style> <title>[1389] sqlalchemy/trunk/lib/sqlalchemy/mapping/mapper.py: save_obj/delete_obj need to specify column types to binds for primary key criterion</title> </head> <body> <div id="msg"> <dl> <dt>Revision</dt> <dd>1389</dd> <dt>Author</dt> <dd>zzzeek</dd> <dt>Date</dt> <dd>2006-05-04 15:27:18 -0500 (Thu, 04 May 2006)</dd> </dl> <h3>Log Message</h3> <pre>save_obj/delete_obj need to specify column types to binds for primary key criterion</pre> <h3>Modified Paths</h3> <ul> <li><a href="#sqlalchemytrunklibsqlalchemymappingmapperpy">sqlalchemy/trunk/lib/sqlalchemy/mapping/mapper.py</a></li> </ul> </div> <div id="patch"> <h3>Diff</h3> <a id="sqlalchemytrunklibsqlalchemymappingmapperpy"></a> <div class="modfile"><h4>Modified: sqlalchemy/trunk/lib/sqlalchemy/mapping/mapper.py (1388 => 1389)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/trunk/lib/sqlalchemy/mapping/mapper.py 2006-05-04 19:18:24 UTC (rev 1388) +++ sqlalchemy/trunk/lib/sqlalchemy/mapping/mapper.py 2006-05-04 20:27:18 UTC (rev 1389) </span><span class="lines">@@ -580,9 +580,9 @@ </span><span class="cx"> if len(update): </span><span class="cx"> clause = sql.and_() </span><span class="cx"> for col in self.pks_by_table[table]: </span><del>- clause.clauses.append(col == sql.bindparam(col._label)) </del><ins>+ clause.clauses.append(col == sql.bindparam(col._label, type=col.type)) </ins><span class="cx"> if self.version_id_col is not None: </span><del>- clause.clauses.append(self.version_id_col == sql.bindparam(self.version_id_col._label)) </del><ins>+ clause.clauses.append(self.version_id_col == sql.bindparam(self.version_id_col._label, type=col.type)) </ins><span class="cx"> statement = table.update(clause) </span><span class="cx"> rows = 0 </span><span class="cx"> for rec in update: </span><span class="lines">@@ -654,9 +654,9 @@ </span><span class="cx"> if len(delete): </span><span class="cx"> clause = sql.and_() </span><span class="cx"> for col in self.pks_by_table[table]: </span><del>- clause.clauses.append(col == sql.bindparam(col.key)) </del><ins>+ clause.clauses.append(col == sql.bindparam(col.key, type=col.type)) </ins><span class="cx"> if self.version_id_col is not None: </span><del>- clause.clauses.append(self.version_id_col == sql.bindparam(self.version_id_col.key)) </del><ins>+ clause.clauses.append(self.version_id_col == sql.bindparam(self.version_id_col.key, type=self.version_id_col.type)) </ins><span class="cx"> statement = table.delete(clause) </span><span class="cx"> c = statement.execute(*delete) </span><span class="cx"> if c.supports_sane_rowcount() and c.rowcount != len(delete): </span></span></pre> </div> </div> </body> </html> |
From: <co...@sq...> - 2006-05-04 19:18:38
|
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head><style type="text/css"><!-- #msg dl { border: 1px #006 solid; background: #369; padding: 6px; color: #fff; } #msg dt { float: left; width: 6em; font-weight: bold; } #msg dt:after { content:':';} #msg dl, #msg dt, #msg ul, #msg li { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; } #msg dl a { font-weight: bold} #msg dl a:link { color:#fc3; } #msg dl a:active { color:#ff0; } #msg dl a:visited { color:#cc6; } h3 { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; font-weight: bold; } #msg pre { overflow: auto; background: #ffc; border: 1px #fc0 solid; padding: 6px; } #msg ul, pre { overflow: auto; } #patch { width: 100%; } #patch h4 {font-family: verdana,arial,helvetica,sans-serif;font-size:10pt;padding:8px;background:#369;color:#fff;margin:0;} #patch .propset h4, #patch .binary h4 {margin:0;} #patch pre {padding:0;line-height:1.2em;margin:0;} #patch .diff {width:100%;background:#eee;padding: 0 0 10px 0;overflow:auto;} #patch .propset .diff, #patch .binary .diff {padding:10px 0;} #patch span {display:block;padding:0 10px;} #patch .modfile, #patch .addfile, #patch .delfile, #patch .propset, #patch .binary, #patch .copfile {border:1px solid #ccc;margin:10px 0;} #patch ins {background:#dfd;text-decoration:none;display:block;padding:0 10px;} #patch del {background:#fdd;text-decoration:none;display:block;padding:0 10px;} #patch .lines, .info {color:#888;background:#fff;} --></style> <title>[1388] sqlalchemy/branches/schema/lib/sqlalchemy: advanced datamapping doc, doing more with polymorphic...</title> </head> <body> <div id="msg"> <dl> <dt>Revision</dt> <dd>1388</dd> <dt>Author</dt> <dd>zzzeek</dd> <dt>Date</dt> <dd>2006-05-04 14:18:24 -0500 (Thu, 04 May 2006)</dd> </dl> <h3>Log Message</h3> <pre>advanced datamapping doc, doing more with polymorphic...</pre> <h3>Modified Paths</h3> <ul> <li><a href="#sqlalchemybranchesschemaexamplespolymorphpolymorphpy">sqlalchemy/branches/schema/examples/polymorph/polymorph.py</a></li> <li><a href="#sqlalchemybranchesschemalibsqlalchemyorm__init__py">sqlalchemy/branches/schema/lib/sqlalchemy/orm/__init__.py</a></li> <li><a href="#sqlalchemybranchesschemalibsqlalchemyormmapperpy">sqlalchemy/branches/schema/lib/sqlalchemy/orm/mapper.py</a></li> <li><a href="#sqlalchemybranchesschemalibsqlalchemyormsessionpy">sqlalchemy/branches/schema/lib/sqlalchemy/orm/session.py</a></li> <li><a href="#sqlalchemybranchesschemalibsqlalchemyormutilpy">sqlalchemy/branches/schema/lib/sqlalchemy/orm/util.py</a></li> <li><a href="#sqlalchemybranchesschemalibsqlalchemyschemapy">sqlalchemy/branches/schema/lib/sqlalchemy/schema.py</a></li> </ul> <h3>Added Paths</h3> <ul> <li><a href="#sqlalchemybranchesschemadocbuildcontentadv_datamappingtxt">sqlalchemy/branches/schema/doc/build/content/adv_datamapping.txt</a></li> <li><a href="#sqlalchemybranchesschemaexamplespolymorphconcretepy">sqlalchemy/branches/schema/examples/polymorph/concrete.py</a></li> <li><a href="#sqlalchemybranchesschemaexamplespolymorphsinglepy">sqlalchemy/branches/schema/examples/polymorph/single.py</a></li> </ul> </div> <div id="patch"> <h3>Diff</h3> <a id="sqlalchemybranchesschemadocbuildcontentadv_datamappingtxt"></a> <div class="addfile"><h4>Added: sqlalchemy/branches/schema/doc/build/content/adv_datamapping.txt (1387 => 1388)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/schema/doc/build/content/adv_datamapping.txt 2006-05-04 04:38:20 UTC (rev 1387) +++ sqlalchemy/branches/schema/doc/build/content/adv_datamapping.txt 2006-05-04 19:18:24 UTC (rev 1388) </span><span class="lines">@@ -0,0 +1,814 @@ </span><ins>+Advanced Data Mapping {@name=advdatamapping} +====================== + +This section details all the options available to Mappers, as well as advanced patterns. + +To start, heres the tables we will work with again: + + {python} + from sqlalchemy import * + + metadata = MetaData() + + # a table to store users + users_table = Table('users', metadata, + Column('user_id', Integer, primary_key = True), + Column('user_name', String(40)), + Column('password', String(80)) + ) + + # a table that stores mailing addresses associated with a specific user + addresses_table = Table('addresses', metadata, + Column('address_id', Integer, primary_key = True), + Column('user_id', Integer, ForeignKey("users.user_id")), + Column('street', String(100)), + Column('city', String(80)), + Column('state', String(2)), + Column('zip', String(10)) + ) + + # a table that stores keywords + keywords_table = Table('keywords', metadata, + Column('keyword_id', Integer, primary_key = True), + Column('name', VARCHAR(50)) + ) + + # a table that associates keywords with users + userkeywords_table = Table('userkeywords', metadata, + Column('user_id', INT, ForeignKey("users")), + Column('keyword_id', INT, ForeignKey("keywords")) + ) +### Overriding Properties {@name=overriding} + +A common request is the ability to create custom class properties that override the behavior of setting/getting an attribute. Currently, the easiest way to do this in SQLAlchemy is how it would be done in any Python program; define your attribute with a different name, such as "_attribute", and use a property to get/set its value. The mapper just needs to be told of the special name: + + {python} + class MyClass(object): + def _set_email(self, email): + self._email = email + def _get_email(self, email): + return self._email + email = property(_get_email, _set_email) + + mapper(MyClass, mytable, properties = { + # map the '_email' attribute to the "email" column + # on the table + '_email': mytable.c.email + }) + +In a later release, SQLAlchemy will also allow _get_email and _set_email to be attached directly to the "email" property created by the mapper, and will also allow this association to occur via decorators. + +### More On Relations {@name=relations} + +#### Custom Join Conditions {@name=customjoin} + +When creating relations on a mapper, most examples so far have illustrated the mapper and relationship joining up based on the foreign keys of the tables they represent. in fact, this "automatic" inspection can be completely circumvented using the `primaryjoin` and `secondaryjoin` arguments to `relation`, as in this example which creates a User object which has a relationship to all of its Addresses which are in Boston: + + {python} + class User(object): + pass + class Address(object): + pass + + mapper(Address, addresses) + mapper(User, users, properties={ + 'boston_addreses' : relation(Address, primaryjoin= + and_(users.c.user_id==Address.c.user_id, + Addresses.c.city=='Boston')) + }) + +Many to many relationships can be customized by one or both of `primaryjoin` and `secondaryjoin`, shown below with just the default many-to-many relationship explicitly set: + + {python} + class User(object): + pass + class Keyword(object): + pass + Keyword.mapper = mapper(Keyword, keywords) + User.mapper = mapper(User, users, properties={ + 'keywords':relation(Keyword.mapper, + primaryjoin=users.c.user_id==userkeywords.c.user_id, + secondaryjoin=userkeywords.c.keyword_id==keywords.c.keyword_id + ) + }) + +### Lazy/Eager Joins Multiple Times to One Table {@name=multiplejoin} + +The previous example leads in to the idea of joining against the same table multiple times. Below is a User object that has lists of its Boston and New York addresses, both lazily loaded when they are first accessed: + + {python} + User.mapper = mapper(User, users, properties={ + 'boston_addreses' : relation(Address.mapper, primaryjoin= + and_(users.c.user_id==Address.c.user_id, + Addresses.c.city=='Boston')), + 'newyork_addresses' : relation(Address.mapper, primaryjoin= + and_(users.c.user_id==Address.c.user_id, + Addresses.c.city=='New York')), + }) + +A complication arises with the above pattern if you want the relations to be eager loaded. Since there will be two separate joins to the addresses table during an eager load, an alias needs to be used to separate them. You can create an alias of the addresses table to separate them, but then you are in effect creating a brand new mapper for each property, unrelated to the main Address mapper, which can create problems with commit operations. So an additional argument `use_alias` can be used with an eager relationship to specify the alias to be used just within the eager query: + + {python} + User.mapper = mapper(User, users, properties={ + 'boston_addreses' : relation(Address.mapper, primaryjoin= + and_(User.c.user_id==Address.c.user_id, + Addresses.c.city=='Boston'), lazy=False, use_alias=True), + 'newyork_addresses' : relation(Address.mapper, primaryjoin= + and_(User.c.user_id==Address.c.user_id, + Addresses.c.city=='New York'), lazy=False, use_alias=True), + }) + + {sql}u = User.mapper.select() + + <&|formatting.myt:codepopper, link="sql" &> + SELECT users.user_id AS users_user_id, users.user_name AS users_user_name, + users.password AS users_password, + addresses_EF45.address_id AS addresses_EF45_address_id, addresses_EF45.user_id AS addresses_EF45_user_id, + addresses_EF45.street AS addresses_EF45_street, addresses_EF45.city AS addresses_EF45_city, + addresses_EF45.state AS addresses_EF45_state, addresses_EF45.zip AS addresses_EF45_zip, + addresses_63C5.address_id AS addresses_63C5_address_id, addresses_63C5.user_id AS addresses_63C5_user_id, + addresses_63C5.street AS addresses_63C5_street, addresses_63C5.city AS addresses_63C5_city, + addresses_63C5.state AS addresses_63C5_state, addresses_63C5.zip AS addresses_63C5_zip + FROM users + LEFT OUTER JOIN addresses AS addresses_EF45 ON users.user_id = addresses_EF45.user_id + AND addresses_EF45.city = :addresses_city + LEFT OUTER JOIN addresses AS addresses_63C5 ON users.user_id = addresses_63C5.user_id + AND addresses_63C5.city = :addresses_city_1 + ORDER BY users.oid, addresses_EF45.oid, addresses_63C5.oid + {'addresses_city_1': 'New York', 'addresses_city': 'Boston'} + +#### Relation Options {@name=relationoptions} + +Keyword options to the `relation` function include: + +* lazy=(True|False|None) - specifies how the related items should be loaded. a value of True indicates they should be loaded when the property is first accessed. A value of False indicates they should be loaded by joining against the parent object query, so parent and child are loaded in one round trip. A value of None indicates the related items are not loaded by the mapper in any case; the application will manually insert items into the list in some other way. A relationship with lazy=None is still important; items added to the list or removed will cause the appropriate updates and deletes upon commit(). +* secondary - for a many-to-many relationship, specifies the intermediary table. +* primaryjoin - a ClauseElement that will be used as the primary join of this child object against the parent object, or in a many-to-many relationship the join of the primary object to the association table. By default, this value is computed based on the foreign key relationships of the parent and child tables (or association table). +* secondaryjoin - a ClauseElement that will be used as the join of an association table to the child object. By default, this value is computed based on the foreign key relationships of the association and child tables. +* foreignkey - specifies which column in this relationship is "foreign", i.e. which column refers to the parent object. This value is automatically determined in all cases, based on the primary and secondary join conditions, except in the case of a self-referential mapper, where it is needed to indicate the child object's reference back to it's parent. +* uselist - a boolean that indicates if this property should be loaded as a list or a scalar. In most cases, this value is determined based on the type and direction of the relationship - one to many forms a list, one to one forms a scalar, many to many is a list. If a scalar is desired where normally a list would be present, set uselist to False. +* private - indicates if these child objects are "private" to the parent; removed items will also be deleted, and if the parent item is deleted, all child objects are deleted as well. See the example in <&formatting.myt:link, path="datamapping_relations_private"&>. +* backreference - indicates the name of a property to be placed on the related mapper's class that will handle this relationship in the other direction, including synchronizing the object attributes on both sides of the relation. See the example in <&formatting.myt:link, path="datamapping_relations_backreferences"&>. +* order_by - indicates the ordering that should be applied when loading these items. See the section [advdatamapping_orderby](rel:advdatamapping_orderby) for details. +* association - When specifying a many to many relationship with an association object, this keyword should reference the mapper of the target object of the association. See the example in [datamapping_association](rel:datamapping_association). +* post_update - this indicates that the relationship should be handled by a second UPDATE statement after an INSERT, or before a DELETE. using this flag essentially means the relationship will not incur any "dependency" between parent and child item, as the particular foreign key relationship between them is handled by a second statement. use this flag when a particular mapping arrangement will incur two rows that are dependent on each other, such as a table that has a one-to-many relationship to a set of child rows, and also has a column that references a single child row within that list (i.e. both tables contain a foreign key to each other). If a flush() operation returns an error that a "cyclical dependency" was detected, this is a cue that you might want to use post_update. + +### Controlling Ordering {@name=orderby} + +By default, mappers will attempt to ORDER BY the "oid" column of a table, or the primary key column, when selecting rows. This can be modified in several ways. + +The "order_by" parameter can be sent to a mapper, overriding the per-engine ordering if any. A value of None means that the mapper should not use any ordering. A non-None value, which can be a column, an `asc` or `desc` clause, or an array of either one, indicates the ORDER BY clause that should be added to all select queries: + + {python} + # disable all ordering + mapper = mapper(User, users, order_by=None) + + # order by a column + mapper = mapper(User, users, order_by=users.c.user_id) + + # order by multiple items + mapper = mapper(User, users, order_by=[users.c.user_id, desc(users.c.user_name)]) + +"order_by" can also be specified to an individual `select` method, overriding all other per-engine/per-mapper orderings: + + {python} + # order by a column + l = mapper.select(users.c.user_name=='fred', order_by=users.c.user_id) + + # order by multiple criterion + l = mapper.select(users.c.user_name=='fred', order_by=[users.c.user_id, desc(users.c.user_name)]) + +For relations, the "order_by" property can also be specified to all forms of relation: + + {python} + # order address objects by address id + mapper = mapper(User, users, properties = { + 'addresses' : relation(mapper(Address, addresses), order_by=addresses.c.address_id) + }) + + # eager load with ordering - the ORDER BY clauses of parent/child will be organized properly + mapper = mapper(User, users, properties = { + 'addresses' : relation(mapper(Address, addresses), order_by=desc(addresses.c.email_address), eager=True) + }, order_by=users.c.user_id) + +### Limiting Rows {@name=limits} + +You can limit rows in a regular SQL query by specifying `limit` and `offset`. A Mapper can handle the same concepts: + + {python} + class User(object): + pass + + mapper(User, users) + {sql}r = session.query(User).select(limit=20, offset=10) + SELECT users.user_id AS users_user_id, + users.user_name AS users_user_name, users.password AS users_password + FROM users ORDER BY users.oid + LIMIT 20 OFFSET 10 + {} + +However, things get tricky when dealing with eager relationships, since a straight LIMIT of rows does not represent the count of items when joining against other tables to load related items as well. So here is what SQLAlchemy will do when you use limit or offset with an eager relationship: + + {python} + class User(object): + pass + class Address(object): + pass + mapper(User, users, properties={ + 'addresses' : relation(mapper(Address, addresses), lazy=False) + }) + r = session.query(User).select(User.c.user_name.like('F%'), limit=20, offset=10) + {opensql}SELECT users.user_id AS users_user_id, users.user_name AS users_user_name, + users.password AS users_password, addresses.address_id AS addresses_address_id, + addresses.user_id AS addresses_user_id, addresses.street AS addresses_street, + addresses.city AS addresses_city, addresses.state AS addresses_state, + addresses.zip AS addresses_zip + FROM + (SELECT users.user_id FROM users WHERE users.user_name LIKE %(users_user_name)s + ORDER BY users.oid LIMIT 20 OFFSET 10) AS rowcount, + users LEFT OUTER JOIN addresses ON users.user_id = addresses.user_id + WHERE rowcount.user_id = users.user_id ORDER BY users.oid, addresses.oid + {'users_user_name': 'F%'} + +The main WHERE clause as well as the limiting clauses are coerced into a subquery; this subquery represents the desired result of objects. A containing query, which handles the eager relationships, is joined against the subquery to produce the result. + +### Overriding Column Names {@name=colname} + +When mappers are constructed, by default the column names in the Table metadata are used as the names of attributes on the mapped class. This can be customzed within the properties by stating the key/column combinations explicitly: + + {python} + user_mapper = mapper(User, users, properties={ + 'id' : users.c.user_id, + 'name' : users.c.user_name, + }) + +In the situation when column names overlap in a mapper against multiple tables, columns may be referenced together with a list: + + {python} + # join users and addresses + usersaddresses = sql.join(users, addresses, users.c.user_id == addresses.c.user_id) + m = mapper(User, usersaddresses, + properties = { + 'id' : [users.c.user_id, addresses.c.user_id], + } + ) + +### Deferred Column Loading {@name=deferred} + +This feature allows particular columns of a table to not be loaded by default, instead being loaded later on when first referenced. It is essentailly "column-level lazy loading". This feature is useful when one wants to avoid loading a large text or binary field into memory when its not needed. Individual columns can be lazy loaded by themselves or placed into groups that lazy-load together. + + {python} + book_excerpts = Table('books', db, + Column('book_id', Integer, primary_key=True), + Column('title', String(200), nullable=False), + Column('summary', String(2000)), + Column('excerpt', String), + Column('photo', Binary) + ) + + class Book(object): + pass + + # define a mapper that will load each of 'excerpt' and 'photo' in + # separate, individual-row SELECT statements when each attribute + # is first referenced on the individual object instance + mapper(Book, book_excerpts, properties = { + 'excerpt' : deferred(book_excerpts.c.excerpt), + 'photo' : deferred(book_excerpts.c.photo) + }) + +Deferred columns can be placed into groups so that they load together: + + {python} + book_excerpts = Table('books', db, + Column('book_id', Integer, primary_key=True), + Column('title', String(200), nullable=False), + Column('summary', String(2000)), + Column('excerpt', String), + Column('photo1', Binary), + Column('photo2', Binary), + Column('photo3', Binary) + ) + + class Book(object): + pass + + # define a mapper with a 'photos' deferred group. when one photo is referenced, + # all three photos will be loaded in one SELECT statement. The 'excerpt' will + # be loaded separately when it is first referenced. + mapper(Book, book_excerpts, properties = { + 'excerpt' : deferred(book_excerpts.c.excerpt), + 'photo1' : deferred(book_excerpts.c.photo1, group='photos'), + 'photo2' : deferred(book_excerpts.c.photo2, group='photos'), + 'photo3' : deferred(book_excerpts.c.photo3, group='photos') + }) + +### More on Mapper Options {@name=options} + +The `options` method on the `Query` object, first introduced in [datamapping_relations_options](rel:datamapping_relations_options), produces a new `Query` object by creating a copy of the underlying `Mapper` and placing modified properties on it. The `options` method is also directly available off the `Mapper` object itself, so that the newly copied `Mapper` can be dealt with directly. The `options` method takes a variable number of `MapperOption` objects which know how to change specific things about the mapper. The five available options are `eagerload`, `lazyload`, `noload`, `deferred` and `extension`. + +An example of a mapper with a lazy load relationship, upgraded to an eager load relationship: + + {python} + class User(object): + pass + class Address(object): + pass + + # a 'lazy' relationship + mapper(User, users, properties = { + 'addreses':relation(mapper(Address, addresses), lazy=True) + }) + + # copy the mapper and convert 'addresses' to be eager + eagermapper = class_mapper(User).options(eagerload('addresses')) + +The `defer` and `undefer` options can control the deferred loading of attributes: + + {python} + # set the 'excerpt' deferred attribute to load normally + m = book_mapper.options(undefer('excerpt')) + + # set the referenced mapper 'photos' to defer its loading of the column 'imagedata' + m = book_mapper.options(defer('photos.imagedata')) + +### Mapping a Class with Table Inheritance {@name=inheritance} + +Inheritance in databases comes in three forms: *single table inheritance*, where several types of classes are stored in one table, *concrete table inheritance*, where each type of class is stored in its own table, and *multiple table inheritance*, where the parent/child classes are stored in their own tables that are joined together in a select. + +There is also a concept of `polymorphic` loading, which indicates if multiple kinds of classes can be loaded in one pass. + +SQLAlchemy supports all three kinds of inheritance. Additionally, true `polymorphic` loading is supported in a straightfoward way for single table inheritance, and has some more manually-configured features that can make it happen for concrete and multiple table inheritance. + +Here are the classes we will use to represent an inheritance relationship: + + {python} + class Employee(object): + def __init__(self, name): + self.name = name + def __repr__(self): + return self.__class__.__name__ + " " + self.name + + class Manager(Employee): + def __init__(self, name, manager_data): + self.name = name + self.manager_data = manager_data + def __repr__(self): + return self.__class__.__name__ + " " + self.name + " " + self.manager_data + + class Engineer(Employee): + def __init__(self, name, engineer_info): + self.name = name + self.engineer_info = engineer_info + def __repr__(self): + return self.__class__.__name__ + " " + self.name + " " + self.engineer_info + +Each class supports a common `name` attribute, while the `Manager` class has its own attribute `manager_data` and the `Engineer` class has its own attribute `engineer_info`. + +#### Single Table Inheritance + +This will support polymorphic loading via the `Employee` mapper. + + {python} + employees_table = Table('employees', metadata, + Column('employee_id', Integer, primary_key=True), + Column('name', String(50)), + Column('manager_data', String(50)), + Column('engineer_info', String(50)), + Column('type', String(20)) + ) + + employee_mapper = mapper(Employee, employees_table, polymorphic_on=employees_table.c.type) + manager_mapper = mapper(Manager, inherits=employee_mapper, polymorphic_identity='manager') + engineer_mapper = mapper(Engineer, inherits=employee_mapper, polymorphic_identity='engineer') + +#### Concrete Table Inheritance + +Without polymorphic loading, you just define a separate mapper for each class. + + {python} + managers_table = Table('managers', metadata, + Column('employee_id', Integer, primary_key=True), + Column('name', String(50)), + Column('manager_data', String(50)), + ) + + engineers_table = Table('engineers', metadata, + Column('employee_id', Integer, primary_key=True), + Column('name', String(50)), + Column('engineer_info', String(50)), + ) + + manager_mapper = mapper(Manager, managers_table) + engineer_mapper = mapper(Engineer, engineers_table) + +With polymorphic loading, the SQL query to do the actual polymorphic load must be constructed, usually as a UNION. Additionally, each query composed into the UNION must have the same set of column names, as well as an extra column to indicate the "polymorphic identity" of each row: + + {python} + colnames = ['employee_id', 'name', 'manager_data', 'engineer_info'] + def col(name, table): + try: + return table.c[name] + except KeyError: + return null().label(name) + + pjoin = select( + [col(name, managers_table) for name in colnames] + [column("'manager'").label('type')] + ).union_all( + select( + [col(name, engineers_table) for name in colnames] + [column("'engineer'").label('type')] + )).alias('pjoin') + + employee_mapper = mapper(Employee, pjoin, polymorphic_on=pjoin.c.type) + manager_mapper = mapper(Manager, managers_table, inherits=employee_mapper, concrete=True, polymorphic_ident='manager') + engineer_mapper = mapper(Engineer, engineers_table, inherits=employee_mapper, concrete=True, polymorphic_ident='engineer') + + + {python} +Table Inheritance indicates the pattern where two tables, in a parent-child relationship, are mapped to an inheritance chain of classes. If a table "employees" contains additional information about managers in the table "managers", a corresponding object inheritance pattern would have an Employee class and a Manager class. Loading a Manager object means you are joining managers to employees. For SQLAlchemy, this pattern is just a special case of a mapper that maps against a joined relationship, and is provided via the `inherits` keyword. + {python} + class User(object): + """a user object.""" + pass + User.mapper = mapper(User, users) + + class AddressUser(User): + """a user object that also has the users mailing address.""" + pass + + # define a mapper for AddressUser that inherits the User.mapper, and joins on the user_id column + AddressUser.mapper = mapper( + AddressUser, + addresses, inherits=User.mapper + ) + + items = AddressUser.mapper.select() + +Above, the join condition is determined via the foreign keys between the users and the addresses table. To specify the join condition explicitly, use `inherit_condition`: +{python} + AddressUser.mapper = mapper( + AddressUser, + addresses, inherits=User.mapper, + inherit_condition=users.c.user_id==addresses.c.user_id + ) +### Mapping a Class against Multiple Tables {@name=joins} + The more general case of the pattern described in "table inheritance" is a mapper that maps against more than one table. The `join` keyword from the SQL package creates a neat selectable unit comprised of multiple tables, complete with its own composite primary key, which can be passed in to a mapper as the table. + {python} + # a class + class AddressUser(object): + pass + + # define a Join + j = join(users, addresses) + + # map to it - the identity of an AddressUser object will be + # based on (user_id, address_id) since those are the primary keys involved + m = mapper(AddressUser, j) + + + A second example: + {python} + # many-to-many join on an association table + j = join(users, userkeywords, + users.c.user_id==userkeywords.c.user_id).join(keywords, + userkeywords.c.keyword_id==keywords.c.keyword_id) + + # a class + class KeywordUser(object): + pass + + # map to it - the identity of a KeywordUser object will be + # (user_id, keyword_id) since those are the primary keys involved + m = mapper(KeywordUser, j) +### Mapping a Class against Arbitary Selects {@name=selects} +Similar to mapping against a join, a plain select() object can be used with a mapper as well. Below, an example select which contains two aggregate functions and a group_by is mapped to a class: + {python} + s = select([customers, + func.count(orders).label('order_count'), + func.max(orders.price).label('highest_order')], + customers.c.customer_id==orders.c.customer_id, + group_by=[c for c in customers.c] + ) + class Customer(object): + pass + + mapper = mapper(Customer, s) + +Above, the "customers" table is joined against the "orders" table to produce a full row for each customer row, the total count of related rows in the "orders" table, and the highest price in the "orders" table, grouped against the full set of columns in the "customers" table. That query is then mapped against the Customer class. New instances of Customer will contain attributes for each column in the "customers" table as well as an "order_count" and "highest_order" attribute. Updates to the Customer object will only be reflected in the "customers" table and not the "orders" table. This is because the primary keys of the "orders" table are not represented in this mapper and therefore the table is not affected by save or delete operations. +### Multiple Mappers for One Class {@name=multiple} + By now it should be apparent that the mapper defined for a class is in no way the only mapper that exists for that class. Other mappers can be created at any time; either explicitly or via the `options` method, to provide different loading behavior. + + However, its not as simple as that. The mapper serves a dual purpose; one is to generate select statements and load objects from executing those statements; the other is to keep track of the defined dependencies of that object when save and delete operations occur, and to extend the attributes of the object so that they store information about their history and communicate with the unit of work system. For this reason, it is a good idea to be aware of the behavior of multiple mappers. When creating dependency relationships between objects, one should insure that only the primary mappers are used in those relationships, else deep object traversal operations will fail to load in the expected properties, and update operations will not take all the dependencies into account. + + Generally its as simple as, the <i>first</i> mapper that is defined for a particular class is the one that gets to define that classes' relationships to other mapped classes, and also decorates its attributes and constructors with special behavior. Any subsequent mappers created for that class will be able to load new instances, but object manipulation operations will still function via the original mapper. The special keyword `is_primary` will override this behavior, and make any mapper the new "primary" mapper. + + {python} + class User(object): + pass + + # mapper one - mark it as "primary", meaning this mapper will handle + # saving and class-level properties + m1 = mapper(User, users, is_primary=True) + + # mapper two - this one will also eager-load address objects in + m2 = mapper(User, users, properties={ + 'addresses' : relation(mapper(Address, addresses), lazy=False) + }) + + # get a user. this user will not have an 'addreses' property + u1 = m1.select(User.c.user_id==10) + + # get another user. this user will have an 'addreses' property. + u2 = m2.select(User.c.user_id==27) + + # make some modifications, including adding an Address object. + u1.user_name = 'jack' + u2.user_name = 'jane' + u2.addresses.append(Address('123 green street')) + + # upon commit, the User objects will be saved. + # the Address object will not, since the primary mapper for User + # does not have an 'addresses' relationship defined + objectstore.commit() +### Circular Mapping {@name=circular} +Oftentimes it is necessary for two mappers to be related to each other. With a datamodel that consists of Users that store Addresses, you might have an Address object and want to access the "user" attribute on it, or have a User object and want to get the list of Address objects. The easiest way to do this is via the `backreference` keyword described in <&formatting.myt:link, path="datamapping_relations_backreferences"&>. Although even when backreferences are used, it is sometimes necessary to explicitly specify the relations on both mappers pointing to each other. +To achieve this involves creating the first mapper by itself, then creating the second mapper referencing the first, then adding references to the first mapper to reference the second: +{python} + class User(object): + pass + class Address(object): + pass + User.mapper = mapper(User, users) + Address.mapper = mapper(Address, addresses, properties={ + 'user':relation(User.mapper) + }) + User.mapper.add_property('addresses', relation(Address.mapper)) + +Note that with a circular relationship as above, you cannot declare both relationships as "eager" relationships, since that produces a circular query situation which will generate a recursion exception. So what if you want to load an Address and its User eagerly? Just make a second mapper using options: +{python} + eagermapper = Address.mapper.options(eagerload('user')) + s = eagermapper.select(Address.c.address_id==12) +### Self Referential Mappers {@name=recursive} +A self-referential mapper is a mapper that is designed to operate with an <b>adjacency list</b> table. This is a table that contains one or more foreign keys back to itself, and is usually used to create hierarchical tree structures. SQLAlchemy's default model of saving items based on table dependencies is not sufficient in this case, as an adjacency list table introduces dependencies between individual rows. Fortunately, SQLAlchemy will automatically detect a self-referential mapper and do the extra lifting to make it work. + {python} + # define a self-referential table + trees = Table('treenodes', engine, + Column('node_id', Integer, primary_key=True), + Column('parent_node_id', Integer, ForeignKey('treenodes.node_id'), nullable=True), + Column('node_name', String(50), nullable=False), + ) + + # treenode class + class TreeNode(object): + pass + + # mapper defines "children" property, pointing back to TreeNode class, + # with the mapper unspecified. it will point back to the primary + # mapper on the TreeNode class. + TreeNode.mapper = mapper(TreeNode, trees, properties={ + 'children' : relation( + TreeNode, + private=True + ), + } + ) + + # or, specify the circular relationship after establishing the original mapper: + mymapper = mapper(TreeNode, trees) + + mymapper.add_property('children', relation( + mymapper, + private=True + )) + + + This kind of mapper goes through a lot of extra effort when saving and deleting items, to determine the correct dependency graph of nodes within the tree. + + A self-referential mapper where there is more than one relationship on the table requires that all join conditions be explicitly spelled out. Below is a self-referring table that contains a "parent_node_id" column to reference parent/child relationships, and a "root_node_id" column which points child nodes back to the ultimate root node: + {python} + # define a self-referential table with several relations + trees = Table('treenodes', engine, + Column('node_id', Integer, primary_key=True), + Column('parent_node_id', Integer, ForeignKey('treenodes.node_id'), nullable=True), + Column('root_node_id', Integer, ForeignKey('treenodes.node_id'), nullable=True), + Column('node_name', String(50), nullable=False), + ) + + # treenode class + class TreeNode(object): + pass + + # define the "children" property as well as the "root" property + TreeNode.mapper = mapper(TreeNode, trees, properties={ + 'children' : relation( + TreeNode, + primaryjoin=trees.c.parent_node_id==trees.c.node_id + private=True + ), + 'root' : relation( + TreeNode, + primaryjoin=trees.c.root_node_id=trees.c.node_id, + foreignkey=trees.c.node_id, + uselist=False + ) + } + ) + +The "root" property on a TreeNode is a many-to-one relationship. By default, a self-referential mapper declares relationships as one-to-many, so the extra parameter `foreignkey`, pointing to the "many" side of a relationship, is needed to indicate a "many-to-one" self-referring relationship. +Both TreeNode examples above are available in functional form in the `examples/adjacencytree` directory of the distribution. +### Result-Set Mapping {@name=resultset} + Take any result set and feed it into a mapper to produce objects. Multiple mappers can be combined to retrieve unrelated objects from the same row in one step. The `instances` method on mapper takes a ResultProxy object, which is the result type generated from SQLEngine, and delivers object instances. + <&|formatting.myt:code, title="single object"&> + class User(object): + pass + + User.mapper = mapper(User, users) + + # select users + c = users.select().execute() + + # get objects + userlist = User.mapper.instances(c) + + + <&|formatting.myt:code, title="multiple objects"&> + # define a second class/mapper + class Address(object): + pass + + Address.mapper = mapper(Address, addresses) + + # select users and addresses in one query + s = select([users, addresses], users.c.user_id==addresses.c.user_id) + + # execute it, and process the results with the User mapper, chained to the Address mapper + r = User.mapper.instances(s.execute(), Address.mapper) + + # result rows are an array of objects, one for each mapper used + for entry in r: + user = r[0] + address = r[1] +### Mapper Arguments {@name=arguments} +Other arguments not covered above include: +<ul> + <li>version_id_col=None - an integer-holding Column object that will be assigned an incrementing + counter, which is added to the WHERE clause used by UPDATE and DELETE statements. The matching row + count returned by the database is compared to the expected row count, and an exception is raised if they dont match. This is a basic "optimistic concurrency" check. Without the version id column, SQLAlchemy still compares the updated rowcount.</li> + <li>always_refresh=False - this option will cause the mapper to refresh all the attributes of all objects loaded by select/get statements, regardless of if they already exist in the current session. this includes all lazy- and eager-loaded relationship attributes, and will also overwrite any changes made to attributes on the column.</li> + <li>entity_name=None - this is an optional "entity name" that will be appended to the key used to associate classes to this mapper. What this basically means is, several primary mappers can be made against the same class by using different entity names; object instances will have the entity name tagged to them, so that all operations will occur on them relative to that mapper. When instantiating new objects, use <code>_sa_entity='name'</code> to tag them to the appropriate mapper.</li> +</ul> +### Extending Mapper {@name=extending} +Mappers can have functionality augmented or replaced at many points in its execution via the usage of the MapperExtension class. This class is just a series of "hooks" where various functionality takes place. An application can make its own MapperExtension objects, overriding only the methods it needs. + {python} + class MapperExtension(object): + def create_instance(self, mapper, row, imap, class_): + """called when a new object instance is about to be created from a row. + the method can choose to create the instance itself, or it can return + None to indicate normal object creation should take place. + + mapper - the mapper doing the operation + row - the result row from the database + imap - a dictionary that is storing the running set of objects collected from the + current result set + class_ - the class we are mapping. + """ + def append_result(self, mapper, row, imap, result, instance, isnew, populate_existing=False): + """called when an object instance is being appended to a result list. + + If it returns True, it is assumed that this method handled the appending itself. + + mapper - the mapper doing the operation + row - the result row from the database + imap - a dictionary that is storing the running set of objects collected from the + current result set + result - an instance of util.HistoryArraySet(), which may be an attribute on an + object if this is a related object load (lazy or eager). use result.append_nohistory(value) + to append objects to this list. + instance - the object instance to be appended to the result + isnew - indicates if this is the first time we have seen this object instance in the current result + set. if you are selecting from a join, such as an eager load, you might see the same object instance + many times in the same result set. + populate_existing - usually False, indicates if object instances that were already in the main + identity map, i.e. were loaded by a previous select(), get their attributes overwritten + """ + def before_insert(self, mapper, instance): + """called before an object instance is INSERTed into its table. + + this is a good place to set up primary key values and such that arent handled otherwise.""" + def after_insert(self, mapper, instance): + """called after an object instance has been INSERTed""" + def before_delete(self, mapper, instance): + """called before an object instance is DELETEed""" + + + To use MapperExtension, make your own subclass of it and just send it off to a mapper: + {python} + mapper = mapper(User, users, extension=MyExtension()) + + An existing mapper can create a copy of itself using an extension via the `extension` option: + {python} + extended_mapper = mapper.options(extension(MyExtension())) +### How Mapper Modifies Mapped Classes {@name=class} +This section is a quick summary of what's going on when you send a class to the `mapper()` function. This material, not required to be able to use SQLAlchemy, is a little more dense and should be approached patiently! + +The primary changes to a class that is mapped involve attaching property objects to it which represent table columns. These property objects essentially track changes. In addition, the __init__ method of the object is decorated to track object creates. +Here is a quick rundown of all the changes in code form: + {python} + # step 1 - override __init__ to 'register_new' with the Unit of Work + oldinit = myclass.__init__ + def init(self, *args, **kwargs): + nohist = kwargs.pop('_mapper_nohistory', False) + oldinit(self, *args, **kwargs) + if not nohist: + # register_new with Unit Of Work + objectstore.uow().register_new(self) + myclass.__init__ = init + + # step 2 - set a string identifier that will + # locate the classes' primary mapper + myclass._mapper = mapper.hashkey + + # step 3 - add column accessor + myclass.c = mapper.columns + + # step 4 - attribute decorating. + # this happens mostly within the package sqlalchemy.attributes + + # this dictionary will store a series of callables + # that generate "history" containers for + # individual object attributes + myclass._class_managed_attributes = {} + + # create individual properties for each column - + # these objects know how to talk + # to the attribute package to create appropriate behavior. + # the next example examines the attributes package more closely. + myclass.column1 = SmartProperty().property('column1', uselist=False) + myclass.column2 = SmartProperty().property('column2', uselist=True) + +The attribute package is used when save operations occur to get a handle on modified values. In the example below, +a full round-trip attribute tracking operation is illustrated: +{python} + import sqlalchemy.attributes as attributes + + # create an attribute manager. + # the sqlalchemy.mapping package keeps one of these around as + # 'objectstore.global_attributes' + manager = attributes.AttributeManager() + + # regular old new-style class + class MyClass(object): + pass + + # register a scalar and a list attribute + manager.register_attribute(MyClass, 'column1', uselist=False) + manager.register_attribute(MyClass, 'column2', uselist=True) + + # create/modify an object + obj = MyClass() + obj.column1 = 'this is a new value' + obj.column2.append('value 1') + obj.column2.append('value 2') + + # get history objects + col1_history = manager.get_history(obj, 'column1') + col2_history = manager.get_history(obj, 'column2') + + # whats new ? + >>> col1_history.added_items() + ['this is a new value'] + + >>> col2_history.added_items() + ['value1', 'value2'] + + # commit changes + manager.commit(obj) + + # the new values become the "unchanged" values + >>> col1_history.added_items() + [] + + >>> col1_history.unchanged_items() + ['this is a new value'] + + >>> col2_history.added_items() + [] + + >>> col2_history.unchanged_items() + ['value1', 'value2'] + +The above AttributeManager also includes a method `value_changed` which is triggered whenever change events occur on the managed object attributes. The Unit of Work (objectstore) package overrides this method in order to receive change events; its essentially this: +` + import sqlalchemy.attributes as attributes + class UOWAttributeManager(attributes.AttributeManager): + def value_changed(self, obj, key, value): + if hasattr(obj, '_instance_key'): + uow().register_dirty(obj) + else: + uow().register_new(obj) + + global_attributes = UOWAttributeManager() + +Objects that contain the attribute "_instance_key" are already registered with the Identity Map, and are assumed to have come from the database. They therefore get marked as "dirty" when changes happen. Objects without an "_instance_key" are not from the database, and get marked as "new" when changes happen, although usually this will already have occured via the object's __init__ method. + + </ins></span></pre></div> <a id="sqlalchemybranchesschemaexamplespolymorphconcretepy"></a> <div class="addfile"><h4>Added: sqlalchemy/branches/schema/examples/polymorph/concrete.py (1387 => 1388)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/schema/examples/polymorph/concrete.py 2006-05-04 04:38:20 UTC (rev 1387) +++ sqlalchemy/branches/schema/examples/polymorph/concrete.py 2006-05-04 19:18:24 UTC (rev 1388) </span><span class="lines">@@ -0,0 +1,65 @@ </span><ins>+from sqlalchemy import * + +metadata = MetaData() + +managers_table = Table('managers', metadata, + Column('employee_id', Integer, primary_key=True), + Column('name', String(50)), + Column('manager_data', String(40)) +) + +engineers_table = Table('engineers', metadata, + Column('employee_id', Integer, primary_key=True), + Column('name', String(50)), + Column('engineer_info', String(40)) +) + +engine = create_engine('sqlite:///', echo=True) +metadata.create_all(engine) + + +class Employee(object): + def __init__(self, name): + self.name = name + def __repr__(self): + return self.__class__.__name__ + " " + self.name + +class Manager(Employee): + def __init__(self, name, manager_data): + self.name = name + self.manager_data = manager_data + def __repr__(self): + return self.__class__.__name__ + " " + self.name + " " + self.manager_data + +class Engineer(Employee): + def __init__(self, name, engineer_info): + self.name = name + self.engineer_info = engineer_info + def __repr__(self): + return self.__class__.__name__ + " " + self.name + " " + self.engineer_info + + +pjoin = polymorphic_union({ + 'manager':managers_table, + 'engineer':engineers_table +}, 'type', 'pjoin') + +employee_mapper = mapper(Employee, pjoin, polymorphic_on=pjoin.c.type) +manager_mapper = mapper(Manager, managers_table, inherits=employee_mapper, concrete=True, polymorphic_ident='manager') +engineer_mapper = mapper(Engineer, engineers_table, inherits=employee_mapper, concrete=True, polymorphic_ident='engineer') + + +session = create_session(bind_to=engine) + +m1 = Manager("pointy haired boss", "manager1") +e1 = Engineer("wally", "engineer1") +e2 = Engineer("dilbert", "engineer2") + +session.save(m1) +session.save(e1) +session.save(e2) +session.flush() + +employees = session.query(Employee).select() +print [e for e in employees] + </ins></span></pre></div> <a id="sqlalchemybranchesschemaexamplespolymorphpolymorphpy"></a> <div class="modfile"><h4>Modified: sqlalchemy/branches/schema/examples/polymorph/polymorph.py (1387 => 1388)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/schema/examples/polymorph/polymorph.py 2006-05-04 04:38:20 UTC (rev 1387) +++ sqlalchemy/branches/schema/examples/polymorph/polymorph.py 2006-05-04 19:18:24 UTC (rev 1388) </span><span class="lines">@@ -4,7 +4,7 @@ </span><span class="cx"> # this example illustrates a polymorphic load of two classes, where each class has a very </span><span class="cx"> # different set of properties </span><span class="cx"> </span><del>-db = create_engine('sqlite://', echo=True, echo_uow=False) </del><ins>+db = create_engine('sqlite://', echo='debug', echo_uow=False) </ins><span class="cx"> </span><span class="cx"> # a table to store companies </span><span class="cx"> companies = Table('companies', db, </span><span class="lines">@@ -56,7 +56,13 @@ </span><span class="cx"> </span><span class="cx"> # create a union that represents both types of joins. we have to use </span><span class="cx"> # nulls to pad out the disparate columns. </span><del>-person_join = select( </del><ins>+person_join = polymorphic_union( + { + 'engineer':people.join(engineers), + 'manager':people.join(managers), + 'person':people, + }, None, 'pjoin') +p_person_join = select( </ins><span class="cx"> [ </span><span class="cx"> people, </span><span class="cx"> managers.c.status, </span><span class="lines">@@ -88,6 +94,7 @@ </span><span class="cx"> ) </span><span class="cx"> ).alias('pjoin') </span><span class="cx"> </span><ins>+ </ins><span class="cx"> person_mapper = mapper(Person, people, select_table=person_join, polymorphic_on=person_join.c.type, polymorphic_ident='person') </span><span class="cx"> mapper(Engineer, engineers, inherits=person_mapper, polymorphic_ident='engineer') </span><span class="cx"> mapper(Manager, managers, inherits=person_mapper, polymorphic_ident='manager') </span></span></pre></div> <a id="sqlalchemybranchesschemaexamplespolymorphsinglepy"></a> <div class="addfile"><h4>Added: sqlalchemy/branches/schema/examples/polymorph/single.py (1387 => 1388)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/schema/examples/polymorph/single.py 2006-05-04 04:38:20 UTC (rev 1387) +++ sqlalchemy/branches/schema/examples/polymorph/single.py 2006-05-04 19:18:24 UTC (rev 1388) </span><span class="lines">@@ -0,0 +1,25 @@ </span><ins>+from sqlalchemy import * + +metadata = MetaData() + +employees_table = Table('employees', metadata, + Column('employee_id', Integer, primary_key=True), + Column('name', String(50)), + Column('type', String(20)) +) + +engine = create_engine('sqlite:///') +metadata.create_all(engine) + +class Employee(object): + pass + +class Manager(Employee): + pass + +class Engineer(Employee): + pass + +employee_mapper = mapper(Employee, employees_table, polymorphic_on=employees_table.c.type) +manager_mapper = mapper(Manager, inherits=employee_mapper, polymorphic_identity='manager') +engineer_mapper = mapper(Engineer, inherits=employee_mapper, polymorphic_identity='engineer') </ins></span></pre></div> <a id="sqlalchemybranchesschemalibsqlalchemyorm__init__py"></a> <div class="modfile"><h4>Modified: sqlalchemy/branches/schema/lib/sqlalchemy/orm/__init__.py (1387 => 1388)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/schema/lib/sqlalchemy/orm/__init__.py 2006-05-04 04:38:20 UTC (rev 1387) +++ sqlalchemy/branches/schema/lib/sqlalchemy/orm/__init__.py 2006-05-04 19:18:24 UTC (rev 1388) </span><span class="lines">@@ -14,11 +14,12 @@ </span><span class="cx"> import sqlalchemy.util as util </span><span class="cx"> from exceptions import * </span><span class="cx"> from mapper import * </span><ins>+from util import polymorphic_union </ins><span class="cx"> from properties import * </span><span class="cx"> </span><span class="cx"> __all__ = ['relation', 'backref', 'eagerload', 'lazyload', 'noload', 'deferred', 'defer', 'undefer', </span><span class="cx"> 'mapper', 'clear_mappers', 'sql', 'extension', 'class_mapper', 'object_mapper', 'MapperExtension', </span><del>- 'cascade_mappers' </del><ins>+ 'cascade_mappers', 'polymorphic_union' </ins><span class="cx"> ] </span><span class="cx"> </span><span class="cx"> def relation(*args, **kwargs): </span></span></pre></div> <a id="sqlalchemybranchesschemalibsqlalchemyormmapperpy"></a> <div class="modfile"><h4>Modified: sqlalchemy/branches/schema/lib/sqlalchemy/orm/mapper.py (1387 => 1388)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/schema/lib/sqlalchemy/orm/mapper.py 2006-05-04 04:38:20 UTC (rev 1387) +++ sqlalchemy/branches/schema/lib/sqlalchemy/orm/mapper.py 2006-05-04 19:18:24 UTC (rev 1388) </span><span class="lines">@@ -54,6 +54,7 @@ </span><span class="cx"> polymorphic_on=None, </span><span class="cx"> polymorphic_map=None, </span><span class="cx"> polymorphic_ident=None, </span><ins>+ concrete=False, </ins><span class="cx"> select_table=None): </span><span class="cx"> </span><span class="cx"> ext = MapperExtension() </span><span class="lines">@@ -98,7 +99,7 @@ </span><span class="cx"> # tables - a collection of underlying Table objects pulled from mapped_table </span><span class="cx"> </span><span class="cx"> for table in (local_table, select_table): </span><del>- if table is not None and isinstance(local_table, sql.Select): </del><ins>+ if table is not None and isinstance(local_table, sql.SelectBaseMixin): </ins><span class="cx"> # some db's, noteably postgres, dont want to select from a select </span><span class="cx"> # without an alias. also if we make our own alias internally, then </span><span class="cx"> # the configured properties on the mapper are not matched against the alias </span><span class="lines">@@ -115,19 +116,23 @@ </span><span class="cx"> raise ArgumentError("Class '%s' does not inherit from '%s'" % (self.class_.__name__, inherits.class_.__name__)) </span><span class="cx"> # inherit_condition is optional. </span><span class="cx"> if not local_table is inherits.local_tab... [truncated message content] |
From: <co...@sq...> - 2006-05-04 04:38:31
|
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head><style type="text/css"><!-- #msg dl { border: 1px #006 solid; background: #369; padding: 6px; color: #fff; } #msg dt { float: left; width: 6em; font-weight: bold; } #msg dt:after { content:':';} #msg dl, #msg dt, #msg ul, #msg li { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; } #msg dl a { font-weight: bold} #msg dl a:link { color:#fc3; } #msg dl a:active { color:#ff0; } #msg dl a:visited { color:#cc6; } h3 { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; font-weight: bold; } #msg pre { overflow: auto; background: #ffc; border: 1px #fc0 solid; padding: 6px; } #msg ul, pre { overflow: auto; } #patch { width: 100%; } #patch h4 {font-family: verdana,arial,helvetica,sans-serif;font-size:10pt;padding:8px;background:#369;color:#fff;margin:0;} #patch .propset h4, #patch .binary h4 {margin:0;} #patch pre {padding:0;line-height:1.2em;margin:0;} #patch .diff {width:100%;background:#eee;padding: 0 0 10px 0;overflow:auto;} #patch .propset .diff, #patch .binary .diff {padding:10px 0;} #patch span {display:block;padding:0 10px;} #patch .modfile, #patch .addfile, #patch .delfile, #patch .propset, #patch .binary, #patch .copfile {border:1px solid #ccc;margin:10px 0;} #patch ins {background:#dfd;text-decoration:none;display:block;padding:0 10px;} #patch del {background:#fdd;text-decoration:none;display:block;padding:0 10px;} #patch .lines, .info {color:#888;background:#fff;} --></style> <title>[1387] sqlalchemy/branches/schema/doc/build/content/plugins.txt: how can we forget...</title> </head> <body> <div id="msg"> <dl> <dt>Revision</dt> <dd>1387</dd> <dt>Author</dt> <dd>zzzeek</dd> <dt>Date</dt> <dd>2006-05-03 23:38:20 -0500 (Wed, 03 May 2006)</dd> </dl> <h3>Log Message</h3> <pre>how can we forget...</pre> <h3>Modified Paths</h3> <ul> <li><a href="#sqlalchemybranchesschemadocbuildcontentpluginstxt">sqlalchemy/branches/schema/doc/build/content/plugins.txt</a></li> </ul> </div> <div id="patch"> <h3>Diff</h3> <a id="sqlalchemybranchesschemadocbuildcontentpluginstxt"></a> <div class="modfile"><h4>Modified: sqlalchemy/branches/schema/doc/build/content/plugins.txt (1386 => 1387)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/schema/doc/build/content/plugins.txt 2006-05-04 04:06:42 UTC (rev 1386) +++ sqlalchemy/branches/schema/doc/build/content/plugins.txt 2006-05-04 04:38:20 UTC (rev 1387) </span><span class="lines">@@ -68,7 +68,9 @@ </span><span class="cx"> u = User() </span><span class="cx"> </span><span class="cx"> ctx.current.flush() </span><del>- </del><ins>+ +### ActiveMapper + </ins><span class="cx"> ### ProxyEngine </span><span class="cx"> </span><span class="cx"> ### SelectResults </span></span></pre> </div> </div> </body> </html> |