sqlalchemy-commits Mailing List for SQLAlchemy (Page 372)
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-03-17 06:20:34
|
<!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>[1157] sqlalchemy/trunk/doc/build/content/tutorial.txt: edits...</title> </head> <body> <div id="msg"> <dl> <dt>Revision</dt> <dd>1157</dd> <dt>Author</dt> <dd>zzzeek</dd> <dt>Date</dt> <dd>2006-03-17 00:20:24 -0600 (Fri, 17 Mar 2006)</dd> </dl> <h3>Log Message</h3> <pre>edits...</pre> <h3>Modified Paths</h3> <ul> <li><a href="#sqlalchemytrunkdocbuildcontenttutorialtxt">sqlalchemy/trunk/doc/build/content/tutorial.txt</a></li> </ul> </div> <div id="patch"> <h3>Diff</h3> <a id="sqlalchemytrunkdocbuildcontenttutorialtxt"></a> <div class="modfile"><h4>Modified: sqlalchemy/trunk/doc/build/content/tutorial.txt (1156 => 1157)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/trunk/doc/build/content/tutorial.txt 2006-03-17 06:11:34 UTC (rev 1156) +++ sqlalchemy/trunk/doc/build/content/tutorial.txt 2006-03-17 06:20:24 UTC (rev 1157) </span><span class="lines">@@ -1,6 +1,6 @@ </span><span class="cx"> Tutorial </span><span class="cx"> ======== </span><del>-This tutorial is a "quick start" guide to SQLAlchemy. You may wish to skip it and dive into the [main manual][manual] which is more reference-oriented. </del><ins>+This tutorial provides a relatively simple walking tour through the basic concepts of SQLAlchemy. You may wish to skip it and dive into the [main manual][manual] which is more reference-oriented. </ins><span class="cx"> </span><span class="cx"> [manual]: rel:howtoread </span><span class="cx"> </span><span class="lines">@@ -19,9 +19,9 @@ </span><span class="cx"> [install setuptools]: http://peak.telecommunity.com/DevCenter/EasyInstall#installation-instructions </span><span class="cx"> [cheese]: http://cheeseshop.python.org/pypi </span><span class="cx"> </span><del>-### Installing A Database Manager {@name=dbms} </del><ins>+### Installing a Database API {@name=dbms} </ins><span class="cx"> </span><del>- SQLAlchemy is designed to operate with a [DBAPI][DBAPI] implementation built for a particular database, and includes support for the most popular databases. If you have one of the [supported database managers][supported dbms], you can proceed to the following section. Otherwise [SQLite][] is an easy to use database to get started with, requiring very little configuration. </del><ins>+SQLAlchemy is designed to operate with a [DBAPI][DBAPI] implementation built for a particular database, and includes support for the most popular databases. If you have one of the [supported DBAPI implementations][supported dbms], you can proceed to the following section. Otherwise [SQLite][] is an easy-to-use database to get started with, which works with plain files or in-memory databases. </ins><span class="cx"> </span><span class="cx"> [DBAPI]: http://www.python.org/doc/peps/pep-0249/ </span><span class="cx"> </span><span class="lines">@@ -42,12 +42,12 @@ </span><span class="cx"> </span><span class="cx"> ### Connecting to the Database </span><span class="cx"> </span><del>-First, you need to connect to the database you want to work with: </del><ins>+The first thing needed is a handle to the desired database, represented by a `SQLEngine` object. This object handles the business of managing connections and dealing with the specifics of a particular database. Below, we will make a SQLite connection to a file-based database called "tutorial.db". </ins><span class="cx"> </span><span class="cx"> >>> from sqlalchemy import * </span><span class="cx"> >>> db = create_engine('sqlite://filename=tutorial.db') </span><span class="cx"> </span><del>-Documentation on creating engines is available in [dbengine](rel:dbengine). </del><ins>+For full information on creating database engines, including those for SQLite and others, see [dbengine](rel:dbengine). </ins><span class="cx"> </span><span class="cx"> ### Creating a Table {@name=table} </span><span class="cx"> </span></span></pre> </div> </div> </body> </html> |
From: <co...@sq...> - 2006-03-17 06:11:47
|
<!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>[1156] sqlalchemy/trunk/doc/build/content/tutorial.txt: making the verbiage a little more formal...(its good stuff, just not sure what I want yet)</title> </head> <body> <div id="msg"> <dl> <dt>Revision</dt> <dd>1156</dd> <dt>Author</dt> <dd>zzzeek</dd> <dt>Date</dt> <dd>2006-03-17 00:11:34 -0600 (Fri, 17 Mar 2006)</dd> </dl> <h3>Log Message</h3> <pre>making the verbiage a little more formal...(its good stuff, just not sure what I want yet)</pre> <h3>Modified Paths</h3> <ul> <li><a href="#sqlalchemytrunkdocbuildcontenttutorialtxt">sqlalchemy/trunk/doc/build/content/tutorial.txt</a></li> </ul> </div> <div id="patch"> <h3>Diff</h3> <a id="sqlalchemytrunkdocbuildcontenttutorialtxt"></a> <div class="modfile"><h4>Modified: sqlalchemy/trunk/doc/build/content/tutorial.txt (1155 => 1156)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/trunk/doc/build/content/tutorial.txt 2006-03-17 02:51:08 UTC (rev 1155) +++ sqlalchemy/trunk/doc/build/content/tutorial.txt 2006-03-17 06:11:34 UTC (rev 1156) </span><span class="lines">@@ -1,10 +1,7 @@ </span><span class="cx"> Tutorial </span><span class="cx"> ======== </span><del>-This tutorial is a "quick start" guide to SQLAlchemy, the Python SQL Toolkit and Object Relational Mapper. -SQLAlchemy provides a lot of functionality to help manage SQL databases, but you don't need much in order to begin doing useful things with it. </del><ins>+This tutorial is a "quick start" guide to SQLAlchemy. You may wish to skip it and dive into the [main manual][manual] which is more reference-oriented. </ins><span class="cx"> </span><del>-Note that it is not neccessary to read this tutorial; you may wish to skip it and dive into the [main manual][manual] which is more reference-oriented. - </del><span class="cx"> [manual]: rel:howtoread </span><span class="cx"> </span><span class="cx"> Installation </span><span class="lines">@@ -12,7 +9,7 @@ </span><span class="cx"> </span><span class="cx"> ### Installing SQLAlchemy {@name=sqlalchemy} </span><span class="cx"> </span><del>-Installing SQLAlchemy is easy if you have [setuptools][] on your system (if you don't, [install it][install setuptools], it's easy!). Just run this from the command-line: </del><ins>+Installing SQLAlchemy from scratch is most easily achieved with [setuptools][]. ([setuptools installation][install setuptools]). Just run this from the command-line: </ins><span class="cx"> </span><span class="cx"> $ easy_install SQLAlchemy </span><span class="cx"> </span><span class="lines">@@ -24,25 +21,23 @@ </span><span class="cx"> </span><span class="cx"> ### Installing A Database Manager {@name=dbms} </span><span class="cx"> </span><del>- SQLAlchemy is designed to operate with the [DBAPI][DBAPI] built for a particular database, and includes implementations supporting the most popular ones. If you have one of the [supported database managers][supported dbms], you can proceed to the following section. Otherwise we recommend installing [SQLite][] for starters. SQLite needs very little configuration and is easy to work with. </del><ins>+ SQLAlchemy is designed to operate with a [DBAPI][DBAPI] implementation built for a particular database, and includes support for the most popular databases. If you have one of the [supported database managers][supported dbms], you can proceed to the following section. Otherwise [SQLite][] is an easy to use database to get started with, requiring very little configuration. </ins><span class="cx"> </span><span class="cx"> [DBAPI]: http://www.python.org/doc/peps/pep-0249/ </span><span class="cx"> </span><span class="cx"> To work with SQLite, you'll need: </span><span class="cx"> </span><del>- * SQLite library </del><span class="cx"> * [pysqlite][] - Python interface for SQLite </span><ins>+ * SQLite library </ins><span class="cx"> </span><del>-If you use Windows, you only have to download and install the compiled [pysqlite binary][pysqlite]. It includes the SQLite library already linked in, so you don't have to install it separately. </del><ins>+Note that the SQLite library download is not required with Windows, as the Windows Pysqlite library already includes it linked in. Pysqlite and SQLite can also be installed on Linux or FreeBSD via pre-made [packages][pysqlite packages] or [from sources][pysqlite]. </ins><span class="cx"> </span><del>-If you use Linux or FreeBSD, you may want to install pysqlite and SQLite from [packages][pysqlite packages] made for your operating system. Or you may install them [from sources][pysqlite]. - </del><span class="cx"> [supported dbms]: rel:dbengine_establishing </span><span class="cx"> [sqlite]: http://sqlite.org/ </span><span class="cx"> [pysqlite]: http://pysqlite.org/ </span><span class="cx"> [pysqlite packages]: http://initd.org/tracker/pysqlite/wiki/PysqlitePackages </span><span class="cx"> </span><del>-Get Going! {@name=getgoing} </del><ins>+Getting Started {@name=gettingstarted} </ins><span class="cx"> -------------------------- </span><span class="cx"> </span><span class="cx"> ### Connecting to the Database </span><span class="lines">@@ -52,11 +47,11 @@ </span><span class="cx"> >>> from sqlalchemy import * </span><span class="cx"> >>> db = create_engine('sqlite://filename=tutorial.db') </span><span class="cx"> </span><del>-Main documentation: [dbengine](rel:dbengine). </del><ins>+Documentation on creating engines is available in [dbengine](rel:dbengine). </ins><span class="cx"> </span><span class="cx"> ### Creating a Table {@name=table} </span><span class="cx"> </span><del>-A core philosophy of SQLAlchemy is that tables and domain classes are different beasts. That's why SQLAlchemy doesn't mix them. So let's first work with just tables alone; we construct an object that represents a table: </del><ins>+A core philosophy of SQLAlchemy is that tables and domain classes are different beasts. For this reason, SQLAlchemy provides constructs that represent tables by themselves (known as *table metadata*). So we will begin by constructing table metadata objects and performing SQL operations with them directly, keeping in mind that there is also an Object Relational Mapper (ORM) which does the same thing except via domain models. Let's construct an object that represents a table: </ins><span class="cx"> </span><span class="cx"> >>> users = Table('users', db, </span><span class="cx"> ... Column('user_id', Integer, primary_key = True), </span><span class="lines">@@ -64,7 +59,7 @@ </span><span class="cx"> ... Column('password', String(80)) </span><span class="cx"> ... ) </span><span class="cx"> </span><del>-As you might have guessed, we have just defined a table `users` with 3 columns: `user_id` (which is a primary key column), `user_name` and `password`. Currently it is just an object that may not correspond to an existing table in your database. So let's create the real table! To make it interesting we will ask SQLAlchemy to echo the SQL statements it sends to the database: </del><ins>+As you might have guessed, we have just defined a table named `users` which has three columns: `user_id` (which is a primary key column), `user_name` and `password`. Currently it is just an object that may not correspond to an existing table in your database. To actually create the table, we use the `create()` method. To make it interesting we will have SQLAlchemy to echo the SQL statements it sends to the database: </ins><span class="cx"> </span><span class="cx"> >>> db.echo = True </span><span class="cx"> >>> users.create() # doctest:+ELLIPSIS,+NORMALIZE_WHITESPACE </span><span class="lines">@@ -82,11 +77,11 @@ </span><span class="cx"> >>> list(users.columns)[0].name </span><span class="cx"> 'user_id' </span><span class="cx"> </span><del>-Main documentation: [metadata](rel:metadata). </del><ins>+Documentation on table metadata is available in [metadata](rel:metadata). </ins><span class="cx"> </span><del>-### Filling the Table </del><ins>+### Inserting Rows </ins><span class="cx"> </span><del>-So now we have the table. To insert some data, use the `insert()` method to create a query: </del><ins>+Inserting is achieved via the `insert()` method, which defines a *clause object* representing an INSERT statement: </ins><span class="cx"> </span><span class="cx"> >>> i = users.insert() </span><span class="cx"> >>> i # doctest:+ELLIPSIS </span><span class="lines">@@ -94,7 +89,7 @@ </span><span class="cx"> >>> print i </span><span class="cx"> INSERT INTO users (user_id, user_name, password) VALUES (?, ?, ?) </span><span class="cx"> </span><del>-Call the `execute()` method of the query object to actually add users: </del><ins>+The `execute()` method of the clause object executes the statement at the database level: </ins><span class="cx"> </span><span class="cx"> >>> for name in ['Tom', 'Dick', 'Harry']: # doctest:+ELLIPSIS </span><span class="cx"> ... i.execute(user_name = name) </span><span class="lines">@@ -103,15 +98,15 @@ </span><span class="cx"> >>> i.execute(user_name = 'Mary', password = 'secure') # doctest:+ELLIPSIS </span><span class="cx"> <sqlalchemy.engine.ResultProxy instance at 0x...> </span><span class="cx"> </span><del>-SQLAlchemy will bind all literal values into bind parameters, according to the paramstyle of the underlying DBAPI. This allows for better performance, because the database may cache a compiled representation of the statement and reuse upon new executions, substituting the new values. Also, when using bound values, you need not worry about [SQL injection][] attacks. </del><ins>+When constructing clause objects, SQLAlchemy will bind all literal values into bind parameters, according to the paramstyle of the underlying DBAPI. This allows for better performance, as the database may cache a compiled representation of the statement and reuse it for new executions, substituting the new values. Also, when using bound values, you need not worry about [SQL injection][] attacks. </ins><span class="cx"> </span><span class="cx"> [SQL injection]: http://en.wikipedia.org/wiki/SQL_injection </span><span class="cx"> </span><del>-Main documentation: [sql_insert](rel:sql_insert). </del><ins>+Documentation on inserting: [sql_insert](rel:sql_insert). </ins><span class="cx"> </span><del>-### Querying the Table </del><ins>+### Constructing Queries </ins><span class="cx"> </span><del>-Let's check that the data we have put into `users` table is actually there. The procedure is analogous to the insert example above, except you now call the `select()` function: </del><ins>+Let's check that the data we have put into `users` table is actually there. The procedure is analogous to the insert example above, except you now call the `select()` method off the `users` table: </ins><span class="cx"> </span><span class="cx"> >>> s = users.select() </span><span class="cx"> >>> print s </span><span class="lines">@@ -131,7 +126,7 @@ </span><span class="cx"> >>> r.fetchall() </span><span class="cx"> [(2, u'Dick', None), (3, u'Harry', None), (4, u'Mary', u'secure')] </span><span class="cx"> </span><del>-Main documentation: [sql_select](rel:sql_select). </del><ins>+Documentation on selecting: [sql_select](rel:sql_select). </ins><span class="cx"> </span><span class="cx"> ### Related Table </span><span class="cx"> </span></span></pre> </div> </div> </body> </html> |
From: <co...@sq...> - 2006-03-17 02:51: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>[1155] sqlalchemy/trunk/test: fixed nasty transaction counting bug with new session thing + unit test</title> </head> <body> <div id="msg"> <dl> <dt>Revision</dt> <dd>1155</dd> <dt>Author</dt> <dd>zzzeek</dd> <dt>Date</dt> <dd>2006-03-16 20:51:08 -0600 (Thu, 16 Mar 2006)</dd> </dl> <h3>Log Message</h3> <pre>fixed nasty transaction counting bug with new session thing + unit test</pre> <h3>Modified Paths</h3> <ul> <li><a href="#sqlalchemytrunklibsqlalchemyenginepy">sqlalchemy/trunk/lib/sqlalchemy/engine.py</a></li> <li><a href="#sqlalchemytrunktestproxy_enginepy">sqlalchemy/trunk/test/proxy_engine.py</a></li> </ul> </div> <div id="patch"> <h3>Diff</h3> <a id="sqlalchemytrunklibsqlalchemyenginepy"></a> <div class="modfile"><h4>Modified: sqlalchemy/trunk/lib/sqlalchemy/engine.py (1154 => 1155)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/trunk/lib/sqlalchemy/engine.py 2006-03-17 02:15:09 UTC (rev 1154) +++ sqlalchemy/trunk/lib/sqlalchemy/engine.py 2006-03-17 02:51:08 UTC (rev 1155) </span><span class="lines">@@ -198,6 +198,7 @@ </span><span class="cx"> """commits the transaction started by begin(). If begin() was called multiple times, a counter will be decreased for each call to commit(), with the actual commit operation occuring when the counter reaches zero. this is to provide "nested" behavior of transactions so that different functions in a particular call stack can call begin()/commit() independently of each other without knowledge of an existing transaction.""" </span><span class="cx"> if self.__tcount == 1: </span><span class="cx"> self.engine.do_commit(self.connection) </span><ins>+ self.__tcount = 0 </ins><span class="cx"> elif self.__tcount > 1: </span><span class="cx"> self.__tcount -= 1 </span><span class="cx"> def is_begun(self): </span><span class="lines">@@ -711,6 +712,11 @@ </span><span class="cx"> return parameters </span><span class="cx"> </span><span class="cx"> def proxy(self, statement=None, parameters=None): </span><ins>+ """returns a callable which will execute the given statement string and parameter object. + the parameter object is expected to be the result of a call to compiled.get_params(). + This callable is a generic version of a connection/cursor-specific callable that + is produced within the execute_compiled method, and is used for objects that require + this style of proxy when outside of an execute_compiled method, primarily the DefaultRunner.""" </ins><span class="cx"> parameters = self._convert_compiled_params(parameters) </span><span class="cx"> return self.execute(statement, parameters) </span><span class="cx"> </span></span></pre></div> <a id="sqlalchemytrunktestproxy_enginepy"></a> <div class="modfile"><h4>Modified: sqlalchemy/trunk/test/proxy_engine.py (1154 => 1155)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/trunk/test/proxy_engine.py 2006-03-17 02:15:09 UTC (rev 1154) +++ sqlalchemy/trunk/test/proxy_engine.py 2006-03-17 02:51:08 UTC (rev 1155) </span><span class="lines">@@ -11,7 +11,7 @@ </span><span class="cx"> # </span><span class="cx"> </span><span class="cx"> </span><del>-module_engine = ProxyEngine() </del><ins>+module_engine = ProxyEngine(echo=testbase.echo) </ins><span class="cx"> users = Table('users', module_engine, </span><span class="cx"> Column('user_id', Integer, primary_key=True), </span><span class="cx"> Column('user_name', String(16)), </span></span></pre> </div> </div> </body> </html> |
From: <co...@sq...> - 2006-03-17 02:15: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>[1154] sqlalchemy/trunk/test: refactor to engine to have a separate SQLSession object.</title> </head> <body> <div id="msg"> <dl> <dt>Revision</dt> <dd>1154</dd> <dt>Author</dt> <dd>zzzeek</dd> <dt>Date</dt> <dd>2006-03-16 20:15:09 -0600 (Thu, 16 Mar 2006)</dd> </dl> <h3>Log Message</h3> <pre>refactor to engine to have a separate SQLSession object. allows nested transactions. util.ThreadLocal __hasattr__ method/raise_error param meaningless, removed renamed old engines test to reflection</pre> <h3>Modified Paths</h3> <ul> <li><a href="#sqlalchemytrunklibsqlalchemydatabasessqlitepy">sqlalchemy/trunk/lib/sqlalchemy/databases/sqlite.py</a></li> <li><a href="#sqlalchemytrunklibsqlalchemyenginepy">sqlalchemy/trunk/lib/sqlalchemy/engine.py</a></li> <li><a href="#sqlalchemytrunklibsqlalchemyutilpy">sqlalchemy/trunk/lib/sqlalchemy/util.py</a></li> <li><a href="#sqlalchemytrunktestalltestspy">sqlalchemy/trunk/test/alltests.py</a></li> <li><a href="#sqlalchemytrunktesttablespy">sqlalchemy/trunk/test/tables.py</a></li> </ul> <h3>Added Paths</h3> <ul> <li><a href="#sqlalchemytrunktestreflectionpy">sqlalchemy/trunk/test/reflection.py</a></li> </ul> <h3>Removed Paths</h3> <ul> <li><a href="#sqlalchemytrunktestenginespy">sqlalchemy/trunk/test/engines.py</a></li> </ul> </div> <div id="patch"> <h3>Diff</h3> <a id="sqlalchemytrunklibsqlalchemydatabasessqlitepy"></a> <div class="modfile"><h4>Modified: sqlalchemy/trunk/lib/sqlalchemy/databases/sqlite.py (1153 => 1154)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/trunk/lib/sqlalchemy/databases/sqlite.py 2006-03-17 01:01:34 UTC (rev 1153) +++ sqlalchemy/trunk/lib/sqlalchemy/databases/sqlite.py 2006-03-17 02:15:09 UTC (rev 1154) </span><span class="lines">@@ -151,6 +151,9 @@ </span><span class="cx"> </span><span class="cx"> def dbapi(self): </span><span class="cx"> return sqlite </span><ins>+ + def push_session(self): + raise InvalidRequestError("SQLite doesn't support nested sessions") </ins><span class="cx"> </span><span class="cx"> def schemagenerator(self, **params): </span><span class="cx"> return SQLiteSchemaGenerator(self, **params) </span></span></pre></div> <a id="sqlalchemytrunklibsqlalchemyenginepy"></a> <div class="modfile"><h4>Modified: sqlalchemy/trunk/lib/sqlalchemy/engine.py (1153 => 1154)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/trunk/lib/sqlalchemy/engine.py 2006-03-17 01:01:34 UTC (rev 1153) +++ sqlalchemy/trunk/lib/sqlalchemy/engine.py 2006-03-17 02:15:09 UTC (rev 1154) </span><span class="lines">@@ -171,8 +171,38 @@ </span><span class="cx"> return default.arg </span><span class="cx"> </span><span class="cx"> class SQLSession(object): </span><del>- pass </del><ins>+ """represents a particular connection retrieved from a SQLEngine, and associated transactional state.""" + def __init__(self, engine, parent=None): + self.engine = engine + self.parent = parent + self.__tcount = 0 + def _connection(self): + try: + return self.__connection + except AttributeError: + self.__connection = self.engine._pool.connect() + return self.__connection + connection = property(_connection, doc="the connection represented by this SQLSession. The connection is late-connecting, meaning the call to the connection pool only occurs when it is first called (and the pool will typically only connect the first time it is called as well)") </ins><span class="cx"> </span><ins>+ def begin(self): + """begins" a transaction on this SQLSession's connection. repeated calls to begin() will increment a counter that must be decreased by corresponding commit() statements before an actual commit occurs. this is to provide "nested" behavior of transactions so that different functions in a particular call stack can call begin()/commit() independently of each other without knowledge of an existing transaction.""" + if self.__tcount == 0: + self.engine.do_begin(self.connection) + self.__tcount += 1 + def rollback(self): + """rolls back the transaction on this SQLSession's connection. this can be called regardless of the "begin" counter value, i.e. can be called from anywhere inside a callstack. the "begin" counter is cleared.""" + if self.__tcount > 0: + self.engine.do_rollback(self.connection) + self.__tcount = 0 + def commit(self): + """commits the transaction started by begin(). If begin() was called multiple times, a counter will be decreased for each call to commit(), with the actual commit operation occuring when the counter reaches zero. this is to provide "nested" behavior of transactions so that different functions in a particular call stack can call begin()/commit() independently of each other without knowledge of an existing transaction.""" + if self.__tcount == 1: + self.engine.do_commit(self.connection) + elif self.__tcount > 1: + self.__tcount -= 1 + def is_begun(self): + return self.__tcount > 0 + </ins><span class="cx"> class SQLEngine(schema.SchemaEngine): </span><span class="cx"> """ </span><span class="cx"> The central "database" object used by an application. Subclasses of this object is used </span><span class="lines">@@ -192,6 +222,7 @@ </span><span class="cx"> (cargs, cparams) = self.connect_args() </span><span class="cx"> if pool is None: </span><span class="cx"> params['echo'] = echo_pool </span><ins>+ params['use_threadlocal'] = False </ins><span class="cx"> self._pool = sqlalchemy.pool.manage(self.dbapi(), **params).get_pool(*cargs, **cparams) </span><span class="cx"> else: </span><span class="cx"> self._pool = pool </span><span class="lines">@@ -200,7 +231,7 @@ </span><span class="cx"> self.echo_uow = echo_uow </span><span class="cx"> self.convert_unicode = convert_unicode </span><span class="cx"> self.encoding = encoding </span><del>- self.context = util.ThreadLocal(raiseerror=False) </del><ins>+ self.context = util.ThreadLocal() </ins><span class="cx"> self._ischema = None </span><span class="cx"> self._figure_paramstyle() </span><span class="cx"> self.logger = logger or util.Logger(origin='engine') </span><span class="lines">@@ -390,9 +421,28 @@ </span><span class="cx"> """implementations might want to put logic here for turning autocommit on/off, etc.""" </span><span class="cx"> connection.commit() </span><span class="cx"> </span><ins>+ def _session(self): + if not hasattr(self.context, 'session'): + self.context.session = SQLSession(self) + return self.context.session + session = property(_session, doc="returns the current thread's SQLSession") + + def push_session(self): + """pushes a new SQLSession onto this engine, temporarily replacing the previous one for the current thread. The previous session can be restored by calling pop_session(). this allows the usage of a new connection and possibly transaction within a particular block, superceding the existing one, including any transactions that are in progress. Returns the new SQLSession object.""" + sess = SQLSession(self, self.context.session) + self.context.session = sess + return sess + def pop_session(self): + """restores the current thread's SQLSession to that before the last push_session. Returns the restored SQLSession object. Raises an exception if there is no SQLSession pushed onto the stack.""" + sess = self.context.session.parent + if sess is None: + raise InvalidRequestError("No SQLSession is pushed onto the stack.") + self.context.session = sess + return sess + </ins><span class="cx"> def connection(self): </span><span class="cx"> """returns a managed DBAPI connection from this SQLEngine's connection pool.""" </span><del>- return self._pool.connect() </del><ins>+ return self.session.connection </ins><span class="cx"> </span><span class="cx"> def unique_connection(self): </span><span class="cx"> """returns a DBAPI connection from this SQLEngine's connection pool that is distinct from the current thread's connection.""" </span><span class="lines">@@ -434,40 +484,15 @@ </span><span class="cx"> self.commit() </span><span class="cx"> </span><span class="cx"> def begin(self): </span><del>- """"begins" a transaction on a pooled connection, and stores the connection in a thread-local - context. repeated calls to begin() within the same thread will increment a counter that must be - decreased by corresponding commit() statements before an actual commit occurs. this is to provide - "nested" behavior of transactions so that different functions can all call begin()/commit() and still - call each other.""" - if getattr(self.context, 'transaction', None) is None: - conn = self.connection() - self.do_begin(conn) - self.context.transaction = conn - self.context.tcount = 1 - else: - self.context.tcount += 1 </del><ins>+ """"begins a transaction on the current thread's SQLSession.""" + self.session.begin() </ins><span class="cx"> </span><span class="cx"> def rollback(self): </span><del>- """rolls back the current thread-local transaction started by begin(). the "begin" counter - is cleared and the transaction ended.""" - if self.context.transaction is not None: - self.do_rollback(self.context.transaction) - self.context.transaction = None - self.context.tcount = None </del><ins>+ """rolls back the transaction on the current thread's SQLSession.""" + self.session.rollback() </ins><span class="cx"> </span><span class="cx"> def commit(self): </span><del>- """commits the current thread-local transaction started by begin(). If begin() was called multiple - times, a counter will be decreased for each call to commit(), with the actual commit operation occuring - when the counter reaches zero. this is to provide - "nested" behavior of transactions so that different functions can all call begin()/commit() and still - call each other.""" - if self.context.transaction is not None: - count = self.context.tcount - 1 - self.context.tcount = count - if count == 0: - self.do_commit(self.context.transaction) - self.context.transaction = None - self.context.tcount = None </del><ins>+ self.session.commit() </ins><span class="cx"> </span><span class="cx"> def _process_defaults(self, proxy, compiled, parameters, **kwargs): </span><span class="cx"> """INSERT and UPDATE statements, when compiled, may have additional columns added to their </span><span class="lines">@@ -642,7 +667,7 @@ </span><span class="cx"> self._executemany(cursor, statement, parameters) </span><span class="cx"> else: </span><span class="cx"> self._execute(cursor, statement, parameters) </span><del>- if self.context.transaction is None: </del><ins>+ if not self.session.is_begun(): </ins><span class="cx"> self.do_commit(connection) </span><span class="cx"> except: </span><span class="cx"> self.do_rollback(connection) </span></span></pre></div> <a id="sqlalchemytrunklibsqlalchemyutilpy"></a> <div class="modfile"><h4>Modified: sqlalchemy/trunk/lib/sqlalchemy/util.py (1153 => 1154)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/trunk/lib/sqlalchemy/util.py 2006-03-17 01:01:34 UTC (rev 1153) +++ sqlalchemy/trunk/lib/sqlalchemy/util.py 2006-03-17 02:15:09 UTC (rev 1154) </span><span class="lines">@@ -214,11 +214,8 @@ </span><span class="cx"> </span><span class="cx"> class ThreadLocal(object): </span><span class="cx"> """an object in which attribute access occurs only within the context of the current thread""" </span><del>- def __init__(self, raiseerror = True): </del><ins>+ def __init__(self): </ins><span class="cx"> self.__dict__['_tdict'] = {} </span><del>- self.__dict__['_raiseerror'] = raiseerror - def __hasattr__(self, key): - return self._tdict.has_key("%d_%s" % (thread.get_ident(), key)) </del><span class="cx"> def __delattr__(self, key): </span><span class="cx"> try: </span><span class="cx"> del self._tdict["%d_%s" % (thread.get_ident(), key)] </span><span class="lines">@@ -228,10 +225,7 @@ </span><span class="cx"> try: </span><span class="cx"> return self._tdict["%d_%s" % (thread.get_ident(), key)] </span><span class="cx"> except KeyError: </span><del>- if self._raiseerror: - raise AttributeError(key) - else: - return None </del><ins>+ raise AttributeError(key) </ins><span class="cx"> def __setattr__(self, key, value): </span><span class="cx"> self._tdict["%d_%s" % (thread.get_ident(), key)] = value </span><span class="cx"> </span></span></pre></div> <a id="sqlalchemytrunktestalltestspy"></a> <div class="modfile"><h4>Modified: sqlalchemy/trunk/test/alltests.py (1153 => 1154)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/trunk/test/alltests.py 2006-03-17 01:01:34 UTC (rev 1153) +++ sqlalchemy/trunk/test/alltests.py 2006-03-17 02:15:09 UTC (rev 1154) </span><span class="lines">@@ -12,11 +12,12 @@ </span><span class="cx"> 'attributes', </span><span class="cx"> 'dependency', </span><span class="cx"> </span><del>- # connectivity </del><ins>+ # connectivity, execution </ins><span class="cx"> 'pool', </span><ins>+ 'engine', </ins><span class="cx"> </span><span class="cx"> # schema/tables </span><del>- 'engines', </del><ins>+ 'reflection', </ins><span class="cx"> 'testtypes', </span><span class="cx"> 'indexes', </span><span class="cx"> </span></span></pre></div> <a id="sqlalchemytrunktestenginespy"></a> <div class="delfile"><h4>Deleted: sqlalchemy/trunk/test/engines.py (1153 => 1154)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/trunk/test/engines.py 2006-03-17 01:01:34 UTC (rev 1153) +++ sqlalchemy/trunk/test/engines.py 2006-03-17 02:15:09 UTC (rev 1154) </span><span class="lines">@@ -1,171 +0,0 @@ </span><del>- -import sqlalchemy.ansisql as ansisql -import sqlalchemy.databases.postgres as postgres -import sqlalchemy.databases.oracle as oracle -import sqlalchemy.databases.sqlite as sqllite - -from sqlalchemy import * - -from testbase import PersistTest -import testbase -import unittest, re - -class EngineTest(PersistTest): - def testbasic(self): - # really trip it up with a circular reference - - use_function_defaults = testbase.db.engine.__module__.endswith('postgres') or testbase.db.engine.__module__.endswith('oracle') - - use_string_defaults = use_function_defaults or testbase.db.engine.__module__.endswith('sqlite') - - if use_function_defaults: - defval = func.current_date() - deftype = Date - else: - defval = "3" - deftype = Integer - - if use_string_defaults: - deftype2 = String - defval2 = "im a default" - else: - deftype2 = Integer - defval2 = "15" - - users = Table('engine_users', testbase.db, - Column('user_id', INT, primary_key = True), - Column('user_name', VARCHAR(20), nullable = False), - Column('test1', CHAR(5), nullable = False), - Column('test2', FLOAT(5), nullable = False), - Column('test3', TEXT), - Column('test4', DECIMAL, nullable = False), - Column('test5', TIMESTAMP), - Column('parent_user_id', Integer, ForeignKey('engine_users.user_id')), - Column('test6', DateTime, nullable = False), - Column('test7', String), - Column('test8', Binary), - Column('test_passivedefault', deftype, PassiveDefault(defval)), - Column('test_passivedefault2', Integer, PassiveDefault("5")), - Column('test_passivedefault3', deftype2, PassiveDefault(defval2)), - Column('test9', Binary(100)), - mysql_engine='InnoDB' - ) - - addresses = Table('engine_email_addresses', testbase.db, - Column('address_id', Integer, primary_key = True), - Column('remote_user_id', Integer, ForeignKey(users.c.user_id)), - Column('email_address', String(20)), - mysql_engine='InnoDB' - ) - - -# users.c.parent_user_id.set_foreign_key(ForeignKey(users.c.user_id)) - - users.create() - addresses.create() - - # clear out table registry - users.deregister() - addresses.deregister() - - try: - users = Table('engine_users', testbase.db, autoload = True) - addresses = Table('engine_email_addresses', testbase.db, autoload = True) - finally: - addresses.drop() - users.drop() - - users.create() - addresses.create() - try: - # create a join from the two tables, this insures that - # theres a foreign key set up - # previously, we couldnt get foreign keys out of mysql. seems like - # we can now as long as we use InnoDB -# if testbase.db.engine.__module__.endswith('mysql'): - # addresses.c.remote_user_id.append_item(ForeignKey('engine_users.user_id')) - print users - print addresses - j = join(users, addresses) - print str(j.onclause) - self.assert_((users.c.user_id==addresses.c.remote_user_id).compare(j.onclause)) - finally: - addresses.drop() - users.drop() - - def testmultipk(self): - table = Table( - 'engine_multi', testbase.db, - Column('multi_id', Integer, primary_key=True), - Column('multi_rev', Integer, primary_key=True), - Column('name', String(50), nullable=False), - Column('value', String(100)) - ) - table.create() - # clear out table registry - table.deregister() - - try: - table = Table('engine_multi', testbase.db, autoload=True) - finally: - table.drop() - - print repr( - [table.c['multi_id'].primary_key, - table.c['multi_rev'].primary_key - ] - ) - table.create() - table.insert().execute({'multi_id':1,'multi_rev':1,'name':'row1', 'value':'value1'}) - table.insert().execute({'multi_id':2,'multi_rev':18,'name':'row2', 'value':'value2'}) - table.insert().execute({'multi_id':3,'multi_rev':3,'name':'row3', 'value':'value3'}) - table.select().execute().fetchall() - table.drop() - - def testtoengine(self): - db = ansisql.engine() - - table = Table('mytable', db, - Column('myid', Integer, key = 'id'), - Column('name', String, key = 'name', nullable=False), - Column('description', String, key = 'description'), - ) - - print repr(table) - - pgdb = postgres.engine({}) - - pgtable = table.toengine(pgdb) - - print repr(pgtable) - assert pgtable.c.id.nullable - assert not pgtable.c.name.nullable - assert pgtable.c.description.nullable - - def testoverride(self): - table = Table( - 'override_test', testbase.db, - Column('col1', Integer, primary_key=True), - Column('col2', String(20)), - Column('col3', Numeric) - ) - table.create() - # clear out table registry - table.deregister() - - try: - table = Table( - 'override_test', testbase.db, - Column('col2', Unicode()), - Column('col4', String(30)), autoload=True) - - print repr(table) - self.assert_(isinstance(table.c.col1.type, Integer)) - self.assert_(isinstance(table.c.col2.type, Unicode)) - self.assert_(isinstance(table.c.col4.type, String)) - finally: - table.drop() - -if __name__ == "__main__": - testbase.main() - </del></span></pre></div> <a id="sqlalchemytrunktestreflectionpyfromrev1140sqlalchemytrunktestenginespy"></a> <div class="copfile"><h4>Copied: sqlalchemy/trunk/test/reflection.py (from rev 1140, sqlalchemy/trunk/test/engines.py) (1140 => 1154)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/trunk/test/engines.py 2006-03-13 17:39:06 UTC (rev 1140) +++ sqlalchemy/trunk/test/reflection.py 2006-03-17 02:15:09 UTC (rev 1154) </span><span class="lines">@@ -0,0 +1,171 @@ </span><ins>+ +import sqlalchemy.ansisql as ansisql +import sqlalchemy.databases.postgres as postgres +import sqlalchemy.databases.oracle as oracle +import sqlalchemy.databases.sqlite as sqllite + +from sqlalchemy import * + +from testbase import PersistTest +import testbase +import unittest, re + +class ReflectionTest(PersistTest): + def testbasic(self): + # really trip it up with a circular reference + + use_function_defaults = testbase.db.engine.__module__.endswith('postgres') or testbase.db.engine.__module__.endswith('oracle') + + use_string_defaults = use_function_defaults or testbase.db.engine.__module__.endswith('sqlite') + + if use_function_defaults: + defval = func.current_date() + deftype = Date + else: + defval = "3" + deftype = Integer + + if use_string_defaults: + deftype2 = String + defval2 = "im a default" + else: + deftype2 = Integer + defval2 = "15" + + users = Table('engine_users', testbase.db, + Column('user_id', INT, primary_key = True), + Column('user_name', VARCHAR(20), nullable = False), + Column('test1', CHAR(5), nullable = False), + Column('test2', FLOAT(5), nullable = False), + Column('test3', TEXT), + Column('test4', DECIMAL, nullable = False), + Column('test5', TIMESTAMP), + Column('parent_user_id', Integer, ForeignKey('engine_users.user_id')), + Column('test6', DateTime, nullable = False), + Column('test7', String), + Column('test8', Binary), + Column('test_passivedefault', deftype, PassiveDefault(defval)), + Column('test_passivedefault2', Integer, PassiveDefault("5")), + Column('test_passivedefault3', deftype2, PassiveDefault(defval2)), + Column('test9', Binary(100)), + mysql_engine='InnoDB' + ) + + addresses = Table('engine_email_addresses', testbase.db, + Column('address_id', Integer, primary_key = True), + Column('remote_user_id', Integer, ForeignKey(users.c.user_id)), + Column('email_address', String(20)), + mysql_engine='InnoDB' + ) + + +# users.c.parent_user_id.set_foreign_key(ForeignKey(users.c.user_id)) + + users.create() + addresses.create() + + # clear out table registry + users.deregister() + addresses.deregister() + + try: + users = Table('engine_users', testbase.db, autoload = True) + addresses = Table('engine_email_addresses', testbase.db, autoload = True) + finally: + addresses.drop() + users.drop() + + users.create() + addresses.create() + try: + # create a join from the two tables, this insures that + # theres a foreign key set up + # previously, we couldnt get foreign keys out of mysql. seems like + # we can now as long as we use InnoDB +# if testbase.db.engine.__module__.endswith('mysql'): + # addresses.c.remote_user_id.append_item(ForeignKey('engine_users.user_id')) + print users + print addresses + j = join(users, addresses) + print str(j.onclause) + self.assert_((users.c.user_id==addresses.c.remote_user_id).compare(j.onclause)) + finally: + addresses.drop() + users.drop() + + def testmultipk(self): + table = Table( + 'engine_multi', testbase.db, + Column('multi_id', Integer, primary_key=True), + Column('multi_rev', Integer, primary_key=True), + Column('name', String(50), nullable=False), + Column('value', String(100)) + ) + table.create() + # clear out table registry + table.deregister() + + try: + table = Table('engine_multi', testbase.db, autoload=True) + finally: + table.drop() + + print repr( + [table.c['multi_id'].primary_key, + table.c['multi_rev'].primary_key + ] + ) + table.create() + table.insert().execute({'multi_id':1,'multi_rev':1,'name':'row1', 'value':'value1'}) + table.insert().execute({'multi_id':2,'multi_rev':18,'name':'row2', 'value':'value2'}) + table.insert().execute({'multi_id':3,'multi_rev':3,'name':'row3', 'value':'value3'}) + table.select().execute().fetchall() + table.drop() + + def testtoengine(self): + db = ansisql.engine() + + table = Table('mytable', db, + Column('myid', Integer, key = 'id'), + Column('name', String, key = 'name', nullable=False), + Column('description', String, key = 'description'), + ) + + print repr(table) + + pgdb = postgres.engine({}) + + pgtable = table.toengine(pgdb) + + print repr(pgtable) + assert pgtable.c.id.nullable + assert not pgtable.c.name.nullable + assert pgtable.c.description.nullable + + def testoverride(self): + table = Table( + 'override_test', testbase.db, + Column('col1', Integer, primary_key=True), + Column('col2', String(20)), + Column('col3', Numeric) + ) + table.create() + # clear out table registry + table.deregister() + + try: + table = Table( + 'override_test', testbase.db, + Column('col2', Unicode()), + Column('col4', String(30)), autoload=True) + + print repr(table) + self.assert_(isinstance(table.c.col1.type, Integer)) + self.assert_(isinstance(table.c.col2.type, Unicode)) + self.assert_(isinstance(table.c.col4.type, String)) + finally: + table.drop() + +if __name__ == "__main__": + testbase.main() + </ins></span></pre></div> <a id="sqlalchemytrunktesttablespy"></a> <div class="modfile"><h4>Modified: sqlalchemy/trunk/test/tables.py (1153 => 1154)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/trunk/test/tables.py 2006-03-17 01:01:34 UTC (rev 1153) +++ sqlalchemy/trunk/test/tables.py 2006-03-17 02:15:09 UTC (rev 1154) </span><span class="lines">@@ -14,7 +14,7 @@ </span><span class="cx"> users = Table('users', db, </span><span class="cx"> Column('user_id', Integer, Sequence('user_id_seq', optional=True), primary_key = True), </span><span class="cx"> Column('user_name', String(40)), </span><del>- </del><ins>+ mysql_engine='innodb' </ins><span class="cx"> ) </span><span class="cx"> </span><span class="cx"> addresses = Table('email_addresses', db, </span></span></pre> </div> </div> </body> </html> |
From: <co...@sq...> - 2006-03-17 01:01: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>[1153] sqlalchemy/trunk/doc/build/content: fixes to function/property formatting</title> </head> <body> <div id="msg"> <dl> <dt>Revision</dt> <dd>1153</dd> <dt>Author</dt> <dd>zzzeek</dd> <dt>Date</dt> <dd>2006-03-16 19:01:34 -0600 (Thu, 16 Mar 2006)</dd> </dl> <h3>Log Message</h3> <pre>fixes to function/property formatting</pre> <h3>Modified Paths</h3> <ul> <li><a href="#sqlalchemytrunkdocbuildcomponentsformattingmyt">sqlalchemy/trunk/doc/build/components/formatting.myt</a></li> <li><a href="#sqlalchemytrunkdocbuildcomponentspydocmyt">sqlalchemy/trunk/doc/build/components/pydoc.myt</a></li> <li><a href="#sqlalchemytrunkdocbuildcontentdocstringsmyt">sqlalchemy/trunk/doc/build/content/docstrings.myt</a></li> </ul> </div> <div id="patch"> <h3>Diff</h3> <a id="sqlalchemytrunkdocbuildcomponentsformattingmyt"></a> <div class="modfile"><h4>Modified: sqlalchemy/trunk/doc/build/components/formatting.myt (1152 => 1153)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/trunk/doc/build/components/formatting.myt 2006-03-17 01:01:15 UTC (rev 1152) +++ sqlalchemy/trunk/doc/build/components/formatting.myt 2006-03-17 01:01:34 UTC (rev 1153) </span><span class="lines">@@ -218,13 +218,14 @@ </span><span class="cx"> <%method member_doc> </span><span class="cx"> <%args> </span><span class="cx"> name = "" </span><ins>+ link = "" </ins><span class="cx"> type = None </span><span class="cx"> </%args> </span><span class="cx"> <tr> </span><span class="cx"> <td> </span><span class="cx"> <div class="darkcell"> </span><del>- <A name="<% m.comp('doclib.myt:current').path %>_<% name %>"></a> - <b><% name %></b></td> </del><ins>+ <A name="<% m.comp('doclib.myt:current').path %>_<% link %>"></a> + <b><% name %></b> </ins><span class="cx"> <div class="docstring"><% m.content() %></div> </span><span class="cx"> </div> </span><span class="cx"> </td> </span><span class="lines">@@ -235,6 +236,7 @@ </span><span class="cx"> <%method function_doc> </span><span class="cx"> <%args> </span><span class="cx"> name = "" </span><ins>+ link = "" </ins><span class="cx"> alt = None </span><span class="cx"> arglist = [] </span><span class="cx"> rettype = None </span><span class="lines">@@ -242,7 +244,7 @@ </span><span class="cx"> <tr> </span><span class="cx"> <td> </span><span class="cx"> <div class="darkcell"> </span><del>- <A name="<% m.comp('doclib.myt:current').path %>_<% name %>"></a> </del><ins>+ <A name="<% m.comp('doclib.myt:current').path %>_<% link %>"></a> </ins><span class="cx"> <b><% name %>(<% string.join(map(lambda k: "<i>%s</i>" % k, arglist), ", ")%>)</b> </span><span class="cx"> <div class="docstring"><% m.content() %></div> </span><span class="cx"> </div> </span></span></pre></div> <a id="sqlalchemytrunkdocbuildcomponentspydocmyt"></a> <div class="modfile"><h4>Modified: sqlalchemy/trunk/doc/build/components/pydoc.myt (1152 => 1153)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/trunk/doc/build/components/pydoc.myt 2006-03-17 01:01:15 UTC (rev 1152) +++ sqlalchemy/trunk/doc/build/components/pydoc.myt 2006-03-17 01:01:34 UTC (rev 1153) </span><span class="lines">@@ -123,7 +123,7 @@ </span><span class="cx"> argstrings.append("**%s" % varkw) </span><span class="cx"> </%init> </span><span class="cx"> </span><del>- <&| formatting.myt:function_doc, name="def " + func.__name__, arglist=argstrings &> </del><ins>+ <&|formatting.myt:function_doc, name="def " + func.__name__, link=func.__name__, arglist=argstrings &> </ins><span class="cx"> <&|formatting.myt:formatplain&><% format_paragraphs(func.__doc__) %></&> </span><span class="cx"> </&> </span><span class="cx"> </%method> </span><span class="lines">@@ -134,7 +134,7 @@ </span><span class="cx"> name </span><span class="cx"> prop </span><span class="cx"> </%args> </span><del>- <&| formatting.myt:member_doc, name=name + " = property()" &> </del><ins>+ <&|formatting.myt:member_doc, name=name + " = property()", link=name &> </ins><span class="cx"> <&|formatting.myt:formatplain&><% format_paragraphs(prop.__doc__) %></&> </span><span class="cx"> </&> </span><span class="cx"> </%method> </span><span class="cx">\ No newline at end of file </span></span></pre></div> <a id="sqlalchemytrunkdocbuildcontentdocstringsmyt"></a> <div class="modfile"><h4>Modified: sqlalchemy/trunk/doc/build/content/docstrings.myt (1152 => 1153)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/trunk/doc/build/content/docstrings.myt 2006-03-17 01:01:15 UTC (rev 1152) +++ sqlalchemy/trunk/doc/build/content/docstrings.myt 2006-03-17 01:01:34 UTC (rev 1153) </span><span class="lines">@@ -13,11 +13,11 @@ </span><span class="cx"> </span><span class="cx"> </span><span class="cx"> <& pydoc.myt:obj_doc, obj=schema &> </span><del>-<& pydoc.myt:obj_doc, obj=engine, classes=[engine.SQLEngine, engine.ResultProxy, engine.RowProxy] &> </del><ins>+<& pydoc.myt:obj_doc, obj=engine, classes=[engine.SQLSession, engine.SQLEngine, engine.ResultProxy, engine.RowProxy] &> </ins><span class="cx"> <& pydoc.myt:obj_doc, obj=sql, classes=[sql.ClauseParameters, sql.Compiled, sql.ClauseElement, sql.TableClause, sql.ColumnClause] &> </span><span class="cx"> <& pydoc.myt:obj_doc, obj=pool, classes=[pool.DBProxy, pool.Pool, pool.QueuePool, pool.SingletonThreadPool] &> </span><span class="cx"> <& pydoc.myt:obj_doc, obj=mapping &> </span><del>-<& pydoc.myt:obj_doc, obj=mapping.objectstore, classes=[mapping.objectstore.Session, mapping.objectstore.Session.SessionTrans, mapping.objectstore.UnitOfWork] &> </del><ins>+<& pydoc.myt:obj_doc, obj=mapping.objectstore, classes=[mapping.objectstore.Session, mapping.objectstore.Session.SessionTrans] &> </ins><span class="cx"> <& pydoc.myt:obj_doc, obj=exceptions &> </span><span class="cx"> <& pydoc.myt:obj_doc, obj=proxy &> </span><span class="cx"> </span></span></pre> </div> </div> </body> </html> |
From: <co...@sq...> - 2006-03-17 01:01:29
|
<!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>[1152] sqlalchemy/trunk/lib/sqlalchemy/engine.py: SQLSession.....</title> </head> <body> <div id="msg"> <dl> <dt>Revision</dt> <dd>1152</dd> <dt>Author</dt> <dd>zzzeek</dd> <dt>Date</dt> <dd>2006-03-16 19:01:15 -0600 (Thu, 16 Mar 2006)</dd> </dl> <h3>Log Message</h3> <pre>SQLSession.....</pre> <h3>Modified Paths</h3> <ul> <li><a href="#sqlalchemytrunklibsqlalchemyenginepy">sqlalchemy/trunk/lib/sqlalchemy/engine.py</a></li> </ul> </div> <div id="patch"> <h3>Diff</h3> <a id="sqlalchemytrunklibsqlalchemyenginepy"></a> <div class="modfile"><h4>Modified: sqlalchemy/trunk/lib/sqlalchemy/engine.py (1151 => 1152)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/trunk/lib/sqlalchemy/engine.py 2006-03-17 00:48:12 UTC (rev 1151) +++ sqlalchemy/trunk/lib/sqlalchemy/engine.py 2006-03-17 01:01:15 UTC (rev 1152) </span><span class="lines">@@ -170,7 +170,9 @@ </span><span class="cx"> else: </span><span class="cx"> return default.arg </span><span class="cx"> </span><del>- </del><ins>+class SQLSession(object): + pass + </ins><span class="cx"> class SQLEngine(schema.SchemaEngine): </span><span class="cx"> """ </span><span class="cx"> The central "database" object used by an application. Subclasses of this object is used </span></span></pre> </div> </div> </body> </html> |
From: <co...@sq...> - 2006-03-17 00:48:27
|
<!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>[1151] sqlalchemy/trunk/lib/sqlalchemy/mapping: factored objectstore into two packages, one more public facing the other more to be *feared* ! :)</title> </head> <body> <div id="msg"> <dl> <dt>Revision</dt> <dd>1151</dd> <dt>Author</dt> <dd>zzzeek</dd> <dt>Date</dt> <dd>2006-03-16 18:48:12 -0600 (Thu, 16 Mar 2006)</dd> </dl> <h3>Log Message</h3> <pre>factored objectstore into two packages, one more public facing the other more to be *feared* ! :)</pre> <h3>Modified Paths</h3> <ul> <li><a href="#sqlalchemytrunklibsqlalchemymappingobjectstorepy">sqlalchemy/trunk/lib/sqlalchemy/mapping/objectstore.py</a></li> </ul> <h3>Added Paths</h3> <ul> <li><a href="#sqlalchemytrunklibsqlalchemymappingunitofworkpy">sqlalchemy/trunk/lib/sqlalchemy/mapping/unitofwork.py</a></li> </ul> </div> <div id="patch"> <h3>Diff</h3> <a id="sqlalchemytrunklibsqlalchemymappingobjectstorepy"></a> <div class="modfile"><h4>Modified: sqlalchemy/trunk/lib/sqlalchemy/mapping/objectstore.py (1150 => 1151)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/trunk/lib/sqlalchemy/mapping/objectstore.py 2006-03-16 23:55:00 UTC (rev 1150) +++ sqlalchemy/trunk/lib/sqlalchemy/mapping/objectstore.py 2006-03-17 00:48:12 UTC (rev 1151) </span><span class="lines">@@ -4,28 +4,15 @@ </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><ins>+"""provides the Session object and a function-oriented convenience interface. This is the +"front-end" to the Unit of Work system in unitofwork.py. Issues of "scope" are dealt with here, +primarily through an important function "get_session()", which is where mappers and units of work go to get a handle on the current threa-local context. """ </ins><span class="cx"> </span><del>-"""maintains all currently loaded objects in memory, using the "identity map" pattern. Also -provides a "unit of work" object which tracks changes to objects so that they may be properly -persisted within a transactional scope.""" - -import thread -import sqlalchemy -import sqlalchemy.util as util -import sqlalchemy.attributes as attributes </del><ins>+from sqlalchemy import util </ins><span class="cx"> from sqlalchemy.exceptions import * </span><del>-import topological </del><ins>+import unitofwork </ins><span class="cx"> import weakref </span><del>-import string -import StringIO -from sets import * </del><span class="cx"> </span><del>-__all__ = ['get_id_key', 'get_row_key', 'is_dirty', 'import_instance', 'commit', 'update', 'clear', 'delete', 'instance_key', 'begin', 'has_key', 'has_instance', 'UnitOfWork'] - -# a global indicating if all commit() operations should have their plan -# printed to standard output. also can be affected by creating an engine -# with the "echo_uow=True" keyword argument. -LOG = False </del><span class="cx"> </span><span class="cx"> class Session(object): </span><span class="cx"> """Maintains a UnitOfWork instance, including transaction state.""" </span><span class="lines">@@ -39,7 +26,7 @@ </span><span class="cx"> hash_key - the hash_key used to identify objects against this session, which </span><span class="cx"> defaults to the id of the Session instance. </span><span class="cx"> """ </span><del>- self.uow = UnitOfWork() </del><ins>+ self.uow = unitofwork.UnitOfWork() </ins><span class="cx"> self.parent_uow = None </span><span class="cx"> self.begin_count = 0 </span><span class="cx"> self.nest_transactions = nest_transactions </span><span class="lines">@@ -115,7 +102,7 @@ </span><span class="cx"> if self.parent_uow is not None: </span><span class="cx"> return Session.SessionTrans(self, self.uow, False) </span><span class="cx"> self.parent_uow = self.uow </span><del>- self.uow = UnitOfWork(identity_map = self.uow.identity_map) </del><ins>+ self.uow = unitofwork.UnitOfWork(identity_map = self.uow.identity_map) </ins><span class="cx"> return Session.SessionTrans(self, self.uow, True) </span><span class="cx"> </span><span class="cx"> def _trans_commit(self, trans): </span><span class="lines">@@ -174,7 +161,7 @@ </span><span class="cx"> return getattr(self.uow, key) </span><span class="cx"> </span><span class="cx"> def clear(self): </span><del>- self.uow = UnitOfWork() </del><ins>+ self.uow = unitofwork.UnitOfWork() </ins><span class="cx"> </span><span class="cx"> def delete(self, *obj): </span><span class="cx"> """registers the given objects as to be deleted upon the next commit""" </span><span class="lines">@@ -267,788 +254,6 @@ </span><span class="cx"> def import_instance(instance): </span><span class="cx"> return get_session().import_instance(instance) </span><span class="cx"> </span><del>-class UOWProperty(attributes.SmartProperty): - def __init__(self, class_, *args, **kwargs): - super(UOWProperty, self).__init__(*args, **kwargs) - self.class_ = class_ - property = property(lambda s:class_mapper(s.class_).props[s.key], doc="returns the MapperProperty object associated with this property") - -class UOWListElement(attributes.ListElement): - def __init__(self, obj, key, data=None, deleteremoved=False, **kwargs): - attributes.ListElement.__init__(self, obj, key, data=data, **kwargs) - self.deleteremoved = deleteremoved - def list_value_changed(self, obj, key, item, listval, isdelete): - sess = get_session(obj) - if not isdelete and sess.deleted.contains(item): - raise InvalidRequestError("re-inserting a deleted value into a list") - sess.modified_lists.append(self) - if self.deleteremoved and isdelete: - sess.register_deleted(item) - def append(self, item, _mapper_nohistory = False): - if _mapper_nohistory: - self.append_nohistory(item) - else: - attributes.ListElement.append(self, item) - -class UOWAttributeManager(attributes.AttributeManager): - def __init__(self): - attributes.AttributeManager.__init__(self) - - def value_changed(self, obj, key, value): - if hasattr(obj, '_instance_key'): - get_session(obj).register_dirty(obj) - else: - get_session(obj).register_new(obj) - - def create_prop(self, class_, key, uselist, **kwargs): - return UOWProperty(class_, self, key, uselist) - - def create_list(self, obj, key, list_, **kwargs): - return UOWListElement(obj, key, list_, **kwargs) - -class UnitOfWork(object): - def __init__(self, identity_map=None): - if identity_map is not None: - self.identity_map = identity_map - else: - self.identity_map = weakref.WeakValueDictionary() - - self.attributes = global_attributes - self.new = util.HashSet(ordered = True) - self.dirty = util.HashSet() - self.modified_lists = util.HashSet() - self.deleted = util.HashSet() - - def get(self, class_, *id): - """given a class and a list of primary key values in their table-order, locates the mapper - for this class and calls get with the given primary key values.""" - return object_mapper(class_).get(*id) - - def _get(self, key): - return self.identity_map[key] - - def _put(self, key, obj): - self.identity_map[key] = obj - - def refresh(self, obj): - self.rollback_object(obj) - object_mapper(obj)._get(obj._instance_key, reload=True) - - def has_key(self, key): - """returns True if the given key is present in this UnitOfWork's identity map.""" - return self.identity_map.has_key(key) - - def _remove_deleted(self, obj): - if hasattr(obj, "_instance_key"): - del self.identity_map[obj._instance_key] - try: - del self.deleted[obj] - except KeyError: - pass - try: - del self.dirty[obj] - except KeyError: - pass - try: - del self.new[obj] - except KeyError: - pass - self.attributes.commit(obj) - self.attributes.remove(obj) - - def _validate_obj(self, obj): - """validates that dirty/delete/commit operations can occur upon the given object, by checking - if it has an instance key and that the instance key is present in the identity map.""" - if hasattr(obj, '_instance_key') and not self.identity_map.has_key(obj._instance_key): - raise InvalidRequestError("Detected a mapped object not present in the current thread's Identity Map: '%s'. Use objectstore.import_instance() to place deserialized instances or instances from other threads" % repr(obj._instance_key)) - - def update(self, obj): - """called to add an object to this UnitOfWork as though it were loaded from the DB, - but is actually coming from somewhere else, like a web session or similar.""" - self._put(obj._instance_key, obj) - self.register_dirty(obj) - - def register_attribute(self, class_, key, uselist, **kwargs): - self.attributes.register_attribute(class_, key, uselist, **kwargs) - - def register_callable(self, obj, key, func, uselist, **kwargs): - self.attributes.set_callable(obj, key, func, uselist, **kwargs) - - def register_clean(self, obj): - try: - del self.dirty[obj] - except KeyError: - pass - try: - del self.new[obj] - except KeyError: - pass - if not hasattr(obj, '_instance_key'): - mapper = object_mapper(obj) - obj._instance_key = mapper.instance_key(obj) - self._put(obj._instance_key, obj) - self.attributes.commit(obj) - - def register_new(self, obj): - if not self.new.contains(obj): - self.new.append(obj) - - def register_dirty(self, obj): - if not self.dirty.contains(obj): - self._validate_obj(obj) - self.dirty.append(obj) - - def is_dirty(self, obj): - if not self.dirty.contains(obj): - return False - else: - return True - - def register_deleted(self, obj): - if not self.deleted.contains(obj): - self._validate_obj(obj) - self.deleted.append(obj) - mapper = object_mapper(obj) - # TODO: should the cascading delete dependency thing - # happen wtihin PropertyLoader.process_dependencies ? - mapper.register_deleted(obj, self) - - def unregister_deleted(self, obj): - try: - self.deleted.remove(obj) - except KeyError: - pass - - def commit(self, *objects): - commit_context = UOWTransaction(self) - - if len(objects): - objset = util.HashSet(iter=objects) - else: - objset = None - - for obj in [n for n in self.new] + [d for d in self.dirty]: - if objset is not None and not objset.contains(obj): - continue - if self.deleted.contains(obj): - continue - commit_context.register_object(obj) - for item in self.modified_lists: - obj = item.obj - if objset is not None and not objset.contains(obj): - continue - if self.deleted.contains(obj): - continue - commit_context.register_object(obj, listonly = True) - commit_context.register_saved_history(item) - -# for o in item.added_items() + item.deleted_items(): -# if self.deleted.contains(o): -# continue -# commit_context.register_object(o, listonly=True) - - for obj in self.deleted: - if objset is not None and not objset.contains(obj): - continue - commit_context.register_object(obj, isdelete=True) - - engines = util.HashSet() - for mapper in commit_context.mappers: - for e in mapper.engines: - engines.append(e) - - echo_commit = False - for e in engines: - echo_commit = echo_commit or e.echo_uow - e.begin() - try: - commit_context.execute(echo=echo_commit) - except: - for e in engines: - e.rollback() - raise - for e in engines: - e.commit() - - commit_context.post_exec() - - - def rollback_object(self, obj): - """'rolls back' the attributes that have been changed on an object instance.""" - self.attributes.rollback(obj) - -class UOWTransaction(object): - """handles the details of organizing and executing transaction tasks - during a UnitOfWork object's commit() operation.""" - def __init__(self, uow): - self.uow = uow - - # unique list of all the mappers we come across - self.mappers = util.HashSet() - self.dependencies = {} - self.tasks = {} - self.saved_histories = util.HashSet() - self.__modified = False - - def register_object(self, obj, isdelete = False, listonly = False, postupdate=False, **kwargs): - """adds an object to this UOWTransaction to be updated in the database. - - 'isdelete' indicates whether the object is to be deleted or saved (update/inserted). - - 'listonly', indicates that only this object's dependency relationships should be - refreshed/updated to reflect a recent save/upcoming delete operation, but not a full - save/delete operation on the object itself, unless an additional save/delete - registration is entered for the object.""" - #print "REGISTER", repr(obj), repr(getattr(obj, '_instance_key', None)), str(isdelete), str(listonly) - # things can get really confusing if theres duplicate instances floating around, - # so make sure everything is OK - self.uow._validate_obj(obj) - - mapper = object_mapper(obj) - self.mappers.append(mapper) - task = self.get_task_by_mapper(mapper) - - if postupdate: - mod = task.append_postupdate(obj) - self.__modified = self.__modified or mod - return - - # for a cyclical task, things need to be sorted out already, - # so this object should have already been added to the appropriate sub-task - # can put an assertion here to make sure.... - if task.circular: - return - - mod = task.append(obj, listonly, isdelete=isdelete, **kwargs) - self.__modified = self.__modified or mod - - def unregister_object(self, obj): - mapper = object_mapper(obj) - task = self.get_task_by_mapper(mapper) - task.delete(obj) - self.__modified = True - - def get_task_by_mapper(self, mapper): - """every individual mapper involved in the transaction has a single - corresponding UOWTask object, which stores all the operations involved - with that mapper as well as operations dependent on those operations. - this method returns or creates the single per-transaction instance of - UOWTask that exists for that mapper.""" - try: - return self.tasks[mapper] - except KeyError: - return UOWTask(self, mapper) - - def register_dependency(self, mapper, dependency): - """called by mapper.PropertyLoader to register the objects handled by - one mapper being dependent on the objects handled by another.""" - # correct for primary mapper (the mapper offcially associated with the class) - self.dependencies[(mapper._primary_mapper(), dependency._primary_mapper())] = True - self.__modified = True - - def register_processor(self, mapper, processor, mapperfrom, isdeletefrom): - """called by mapper.PropertyLoader to register itself as a "processor", which - will be associated with a particular UOWTask, and be given a list of "dependent" - objects corresponding to another UOWTask to be processed, either after that secondary - task saves its objects or before it deletes its objects.""" - # when the task from "mapper" executes, take the objects from the task corresponding - # to "mapperfrom"'s list of save/delete objects, and send them to "processor" - # for dependency processing - #print "registerprocessor", str(mapper), repr(processor.key), str(mapperfrom), repr(isdeletefrom) - - # correct for primary mapper (the mapper offcially associated with the class) - mapper = mapper._primary_mapper() - mapperfrom = mapperfrom._primary_mapper() - task = self.get_task_by_mapper(mapper) - targettask = self.get_task_by_mapper(mapperfrom) - task.dependencies.append(UOWDependencyProcessor(processor, targettask, isdeletefrom)) - self.__modified = True - - def register_saved_history(self, listobj): - self.saved_histories.append(listobj) - - def execute(self, echo=False): - for task in self.tasks.values(): - task.mapper.register_dependencies(self) - - head = self._sort_dependencies() - self.__modified = False - if LOG or echo: - if head is None: - print "Task dump: None" - else: - print "Task dump:\n" + head.dump() - if head is not None: - head.execute(self) - if LOG or echo: - if self.__modified and head is not None: - print "\nAfter Execute:\n" + head.dump() - else: - print "\nExecute complete (no post-exec changes)\n" - - def post_exec(self): - """after an execute/commit is completed, all of the objects and lists that have - been committed are updated in the parent UnitOfWork object to mark them as clean.""" - - for task in self.tasks.values(): - for elem in task.objects.values(): - if elem.isdelete: - self.uow._remove_deleted(elem.obj) - else: - self.uow.register_clean(elem.obj) - - for obj in self.saved_histories: - try: - obj.commit() - del self.uow.modified_lists[obj] - except KeyError: - pass - - # this assertion only applies to a full commit(), not a - # partial one - #if len(self.uow.new) > 0 or len(self.uow.dirty) >0 or len(self.uow.modified_lists) > 0: - # raise "assertion failed" - - def _sort_dependencies(self): - """creates a hierarchical tree of dependent tasks. the root node is returned. - when the root node is executed, it also executes its child tasks recursively.""" - def sort_hier(node): - if node is None: - return None - task = self.get_task_by_mapper(node.item) - if node.cycles is not None: - tasks = [] - for n in node.cycles: - tasks.append(self.get_task_by_mapper(n.item)) - task.circular = task._sort_circular_dependencies(self, tasks) - for child in node.children: - t = sort_hier(child) - if t is not None: - task.childtasks.append(t) - return task - - mappers = util.HashSet() - for task in self.tasks.values(): - mappers.append(task.mapper) - - head = DependencySorter(self.dependencies, mappers).sort(allow_all_cycles=True) - #print str(head) - task = sort_hier(head) - return task - - -class UOWTaskElement(object): - """an element within a UOWTask. corresponds to a single object instance - to be saved, deleted, or just part of the transaction as a placeholder for - further dependencies (i.e. 'listonly'). - in the case of self-referential mappers, may also store a "childtask", which is a - UOWTask containing objects dependent on this element's object instance.""" - def __init__(self, obj): - self.obj = obj - self.listonly = True - self.childtasks = [] - self.isdelete = False - self.mapper = None - def __repr__(self): - return "UOWTaskElement/%d: %s/%d %s" % (id(self), self.obj.__class__.__name__, id(self.obj), (self.listonly and 'listonly' or (self.isdelete and 'delete' or 'save')) ) - -class UOWDependencyProcessor(object): - """in between the saving and deleting of objects, process "dependent" data, such as filling in - a foreign key on a child item from a new primary key, or deleting association rows before a - delete.""" - def __init__(self, processor, targettask, isdeletefrom): - self.processor = processor - self.targettask = targettask - self.isdeletefrom = isdeletefrom - - def execute(self, trans, delete): - if not delete: - self.processor.process_dependencies(self.targettask, [elem.obj for elem in self.targettask.tosave_elements() if elem.obj is not None], trans, delete = delete) - else: - self.processor.process_dependencies(self.targettask, [elem.obj for elem in self.targettask.todelete_elements() if elem.obj is not None], trans, delete = delete) - - def get_object_dependencies(self, obj, trans, passive): - return self.processor.get_object_dependencies(obj, trans, passive=passive) - - def whose_dependent_on_who(self, obj, o): - return self.processor.whose_dependent_on_who(obj, o) - - def branch(self, task): - return UOWDependencyProcessor(self.processor, task, self.isdeletefrom) - -class UOWTask(object): - def __init__(self, uowtransaction, mapper): - if uowtransaction is not None: - uowtransaction.tasks[mapper] = self - self.uowtransaction = uowtransaction - self.mapper = mapper - self.objects = util.OrderedDict() - self.dependencies = [] - self.cyclical_dependencies = [] - self.circular = None - self.postcircular = None - self.childtasks = [] -# print "NEW TASK", repr(self) - - def is_empty(self): - return len(self.objects) == 0 and len(self.dependencies) == 0 and len(self.childtasks) == 0 - - def append(self, obj, listonly = False, childtask = None, isdelete = False): - """appends an object to this task, to be either saved or deleted depending on the - 'isdelete' attribute of this UOWTask. 'listonly' indicates that the object should - only be processed as a dependency and not actually saved/deleted. if the object - already exists with a 'listonly' flag of False, it is kept as is. 'childtask' is used - internally when creating a hierarchical list of self-referential tasks, to assign - dependent operations at the per-object instead of per-task level. """ - try: - rec = self.objects[obj] - retval = False - except KeyError: - rec = UOWTaskElement(obj) - self.objects[obj] = rec - retval = True - if not listonly: - rec.listonly = False - if childtask: - rec.childtasks.append(childtask) - if isdelete: - rec.isdelete = True - return retval - - def append_postupdate(self, obj): - # postupdates are UPDATED immeditely (for now) - self.mapper.save_obj([obj], self.uowtransaction, postupdate=True) - return True - - def delete(self, obj): - try: - del self.objects[obj] - except KeyError: - pass - - def execute(self, trans): - """executes this UOWTask. saves objects to be saved, processes all dependencies - that have been registered, and deletes objects to be deleted. """ - if self.circular is not None: - self.circular.execute(trans) - return - - self.mapper.save_obj(self.tosave_objects(), trans) - for dep in self.cyclical_save_dependencies(): - dep.execute(trans, delete=False) - for element in self.tosave_elements(): - for task in element.childtasks: - task.execute(trans) - for dep in self.save_dependencies(): - dep.execute(trans, delete=False) - for dep in self.delete_dependencies(): - dep.execute(trans, delete=True) - for dep in self.cyclical_delete_dependencies(): - dep.execute(trans, delete=True) - for child in self.childtasks: - child.execute(trans) - for element in self.todelete_elements(): - for task in element.childtasks: - task.execute(trans) - self.mapper.delete_obj(self.todelete_objects(), trans) - - def tosave_elements(self): - return [rec for rec in self.objects.values() if not rec.isdelete] - def todelete_elements(self): - return [rec for rec in self.objects.values() if rec.isdelete] - def tosave_objects(self): - return [rec.obj for rec in self.objects.values() if rec.obj is not None and not rec.listonly and rec.isdelete is False] - def todelete_objects(self): - return [rec.obj for rec in self.objects.values() if rec.obj is not None and not rec.listonly and rec.isdelete is True] - def save_dependencies(self): - return [dep for dep in self.dependencies if not dep.isdeletefrom] - def cyclical_save_dependencies(self): - return [dep for dep in self.cyclical_dependencies if not dep.isdeletefrom] - def delete_dependencies(self): - return [dep for dep in self.dependencies if dep.isdeletefrom] - def cyclical_delete_dependencies(self): - return [dep for dep in self.cyclical_dependencies if dep.isdeletefrom] - - def _sort_circular_dependencies(self, trans, cycles): - """for a single task, creates a hierarchical tree of "subtasks" which associate - specific dependency actions with individual objects. This is used for a - "cyclical" task, or a task where elements - of its object list contain dependencies on each other. - - this is not the normal case; this logic only kicks in when something like - a hierarchical tree is being represented.""" - - allobjects = [] - for task in cycles: - allobjects += task.objects.keys() - tuples = [] - - objecttotask = {} - - cycles = Set(cycles) - - # dependency processors that arent part of the cyclical thing - # get put here - extradeplist = [] - - def get_object_task(parent, obj): - try: - return objecttotask[obj] - except KeyError: - t = UOWTask(None, parent.mapper) - t.parent = parent - objecttotask[obj] = t - return t - - dependencies = {} - def get_dependency_task(obj, depprocessor): - try: - dp = dependencies[obj] - except KeyError: - dp = {} - dependencies[obj] = dp - try: - l = dp[depprocessor] - except KeyError: - l = UOWTask(None, depprocessor.targettask.mapper) - dp[depprocessor] = l - return l - - # work out a list of all the "dependency processors" that - # represent objects that have to be dependency sorted at the - # per-object level. all other dependency processors go in - # "extradep." - deps_by_targettask = {} - for task in cycles: - for dep in task.dependencies: - if dep.targettask not in cycles or trans.get_task_by_mapper(dep.processor.mapper) not in cycles: - extradeplist.append(dep) - l = deps_by_targettask.setdefault(dep.targettask, []) - l.append(dep) - - for task in cycles: - for taskelement in task.objects.values(): - obj = taskelement.obj - #print "OBJ", repr(obj), "TASK", repr(task) - - # create a placeholder UOWTask that may be built into the final - # task tree - get_object_task(task, obj) - for dep in deps_by_targettask.get(task, []): - (processor, targettask, isdelete) = (dep.processor, dep.targettask, dep.isdeletefrom) - if taskelement.isdelete is not dep.isdeletefrom: - continue - #print "GETING LIST OFF PROC", processor.key, "OBJ", repr(obj) - - # traverse through the modified child items of each object. normally this - # is done via PropertyLoader in properties.py, but we need all the info - # up front here to do the object-level topological sort. - - # list of dependent objects from this object - childlist = dep.get_object_dependencies(obj, trans, passive = True) - # the task corresponding to the processor's objects - childtask = trans.get_task_by_mapper(processor.mapper) - # is this dependency involved in one of the cycles ? - cyclicaldep = dep.targettask in cycles and trans.get_task_by_mapper(dep.processor.mapper) in cycles - if isdelete: - childlist = childlist.unchanged_items() + childlist.deleted_items() - else: - childlist = childlist.added_items() - - for o in childlist: - if not o in childtask.objects: - # item needs to be saved since its added, or attached to a deleted object - childtask.append(o, isdelete=isdelete and dep.processor.private) - if cyclicaldep: - # cyclical, so create a placeholder UOWTask that may be built into the - # final task tree - t = get_object_task(childtask, o) - if not cyclicaldep: - # not cyclical, so we are done with this - continue - # cyclical, so create an ordered pair for the dependency sort - whosdep = dep.whose_dependent_on_who(obj, o) - if whosdep is not None: - tuples.append(whosdep) - # then locate a UOWDependencyProcessor to add the object onto, which - # will handle the modifications between saves/deletes - if whosdep[0] is obj: - get_dependency_task(whosdep[0], dep).append(whosdep[0], isdelete=isdelete) - else: - get_dependency_task(whosdep[0], dep).append(whosdep[1], isdelete=isdelete) - else: - get_dependency_task(obj, dep).append(obj, isdelete=isdelete) - - head = DependencySorter(tuples, allobjects).sort() - if head is None: - return None - - #print str(head) - - def make_task_tree(node, parenttask): - """takes a dependency-sorted tree of objects and creates a tree of UOWTasks""" - t = objecttotask[node.item] - can_add_to_parent = t.mapper is parenttask.mapper - if can_add_to_parent: - parenttask.append(node.item, t.parent.objects[node.item].listonly, isdelete=t.parent.objects[node.item].isdelete, childtask=t) - else: - t.append(node.item, t.parent.objects[node.item].listonly, isdelete=t.parent.objects[node.item].isdelete) - parenttask.append(None, listonly=False, isdelete=t.parent.objects[node.item].isdelete, childtask=t) - if dependencies.has_key(node.item): - for depprocessor, deptask in dependencies[node.item].iteritems(): - if can_add_to_parent: - parenttask.cyclical_dependencies.append(depprocessor.branch(deptask)) - else: - t.cyclical_dependencies.append(depprocessor.branch(deptask)) - for n in node.children: - t2 = make_task_tree(n, t) - return t - - # this is the new "circular" UOWTask which will execute in place of "self" - t = UOWTask(None, self.mapper) - - # stick the non-circular dependencies and child tasks onto the new - # circular UOWTask - t.dependencies += [d for d in extradeplist] - t.childtasks = self.childtasks - make_task_tree(head, t) - return t - - def dump(self): - buf = StringIO.StringIO() - self._dump(buf) - return buf.getvalue() - - def _dump(self, buf, indent=0, circularparent=None): - - def _indent(): - return " | " * indent - - headers = {} - def header(buf, text): - """writes a given header just once""" - try: - headers[text] - except KeyError: - buf.write(_indent() + " |\n") - buf.write(text) - headers[text] = True - - def _dump_processor(proc): - if proc.isdeletefrom: - val = [t for t in proc.targettask.objects.values() if t.isdelete] - else: - val = [t for t in proc.targettask.objects.values() if not t.isdelete] - - buf.write(_indent() + " |- UOWDependencyProcessor(%d) %s attribute on %s (%s)\n" % ( - id(proc), - repr(proc.processor.key), - (proc.isdeletefrom and - "%s's to be deleted" % _repr_task_class(proc.targettask) - or "saved %s's" % _repr_task_class(proc.targettask)), - _repr_task(proc.targettask)) - ) - - if len(val) == 0: - buf.write(_indent() + " | |-" + "(no objects)\n") - for v in val: - buf.write(_indent() + " | |-" + _repr_task_element(v) + "\n") - - def _repr_task_element(te): - if te.obj is None: - objid = "(placeholder)" - else: - objid = "%s(%d)" % (te.obj.__class__.__name__, id(te.obj)) - return "UOWTaskElement(%d): %s %s%s" % (id(te), objid, (te.listonly and '(listonly)' or (te.isdelete and '(delete' or '(save')), - (te.mapper is not None and " w/ " + str(te.mapper) + ")" or ")") - ) - - def _repr_task(task): - if task.mapper is not None: - if task.mapper.__class__.__name__ == 'Mapper': - name = task.mapper.class_.__name__ + "/" + task.mapper.primarytable.id + "/" + str(id(task.mapper)) - else: - name = repr(task.mapper) - else: - name = '(none)' - return ("UOWTask(%d) '%s'" % (id(task), name)) - def _repr_task_class(task): - if task.mapper is not None and task.mapper.__class__.__name__ == 'Mapper': - return task.mapper.class_.__name__ - else: - return '(none)' - - def _repr(obj): - return "%s(%d)" % (obj.__class__.__name__, id(obj)) - - if self.circular is not None: - self.circular._dump(buf, indent, self) - return - - i = _indent() - if len(i): - i = i[0:-1] + "-" - if circularparent is not None: - buf.write(i + " " + _repr_task(circularparent)) - buf.write("->circular->" + _repr_task(self)) - else: - buf.write(i + " " + _repr_task(self)) - - buf.write("\n") - for rec in self.tosave_elements(): - if rec.listonly: - continue - header(buf, _indent() + " |- Save elements\n") - buf.write(_indent() + " |- Save: " + _repr_task_element(rec) + "\n") - for dep in self.cyclical_save_dependencies(): - header(buf, _indent() + " |- Cyclical Save dependencies\n") - _dump_processor(dep) - for element in self.tosave_elements(): - for task in element.childtasks: - header(buf, _indent() + " |- Save subelements of UOWTaskElement(%s)\n" % id(element)) - task._dump(buf, indent + 1) - for dep in self.save_dependencies(): - header(buf, _indent() + " |- Save dependencies\n") - _dump_processor(dep) - for dep in self.delete_dependencies(): - header(buf, _indent() + " |- Delete dependencies\n") - _dump_processor(dep) - for dep in self.cyclical_delete_dependencies(): - header(buf, _indent() + " |- Cyclical Delete dependencies\n") - _dump_processor(dep) - for child in self.childtasks: - header(buf, _indent() + " |- Child tasks\n") - child._dump(buf, indent + 1) -# for obj in self.postupdate: -# header(buf, _indent() + " |- Post Update objects\n") -# buf.write(_repr(obj) + "\n") - for element in self.todelete_elements(): - for task in element.childtasks: - header(buf, _indent() + " |- Delete subelements of UOWTaskElement(%s)\n" % id(element)) - task._dump(buf, indent + 1) - - for rec in self.todelete_elements(): - if rec.listonly: - continue - header(buf, _indent() + " |- Delete elements\n") - buf.write(_indent() + " |- Delete: " + _repr_task_element(rec) + "\n") - - buf.write(_indent() + " |----\n") - buf.write(_indent() + "\n") - - def __repr__(self): - if self.mapper is not None: - if self.mapper.__class__.__name__ == 'Mapper': - name = self.mapper.class_.__name__ + "/" + self.mapper.primarytable.name - else: - name = repr(self.mapper) - else: - name = '(none)' - return ("UOWTask(%d) Mapper: '%s'" % (id(self), name)) - -class DependencySorter(topological.QueueDependencySorter): - pass - </del><span class="cx"> def mapper(*args, **params): </span><span class="cx"> return sqlalchemy.mapperlib.mapper(*args, **params) </span><span class="cx"> </span><span class="lines">@@ -1058,7 +263,7 @@ </span><span class="cx"> def class_mapper(class_): </span><span class="cx"> return sqlalchemy.mapperlib.class_mapper(class_) </span><span class="cx"> </span><del>-global_attributes = UOWAttributeManager() </del><ins>+global_attributes = unitofwork.global_attributes </ins><span class="cx"> </span><span class="cx"> session_registry = util.ScopedRegistry(Session) # Default session registry </span><span class="cx"> _sessions = weakref.WeakValueDictionary() # all referenced sessions (including user-created) </span><span class="lines">@@ -1076,7 +281,8 @@ </span><span class="cx"> raise InvalidRequestError("Session '%s' referenced by object '%s' no longer exists" % (hashkey, repr(obj))) </span><span class="cx"> </span><span class="cx"> return session_registry() </span><del>- </del><ins>+ +unitofwork.get_session = get_session </ins><span class="cx"> uow = get_session # deprecated </span><span class="cx"> </span><span class="cx"> def push_session(sess): </span></span></pre></div> <a id="sqlalchemytrunklibsqlalchemymappingunitofworkpy"></a> <div class="addfile"><h4>Added: sqlalchemy/trunk/lib/sqlalchemy/mapping/unitofwork.py (1150 => 1151)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/trunk/lib/sqlalchemy/mapping/unitofwork.py 2006-03-16 23:55:00 UTC (rev 1150) +++ sqlalchemy/trunk/lib/sqlalchemy/mapping/unitofwork.py 2006-03-17 00:48:12 UTC (rev 1151) </span><span class="lines">@@ -0,0 +1,828 @@ </span><ins>+# unitofwork.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 + +"""the internals for the Unit Of Work system. includes hooks into the attributes package +enabling the routing of change events to Unit Of Work objects, as well as the commit mechanism +which creates a dependency structure that executes change operations. + +a Unit of Work is essentially a system of maintaining a graph of in-memory objects and their +modified state. Objects are maintained as unique against their primary key identity using +an "identity map" pattern. The Unit of Work then maintains lists of objects that are new, +dirty, or deleted and provides the capability to commit all those changes at once. +""" + +from sqlalchemy import attributes +from sqlalchemy import util +import sqlalchemy +from sqlalchemy.exceptions import * +import StringIO +import weakref +import topological +from sets import * + +# a global indicating if all commit() operations should have their plan +# printed to standard output. also can be affected by creating an engine +# with the "echo_uow=True" keyword argument. +LOG = False + +class UOWProperty(attributes.SmartProperty): + """overrides SmartProperty to provide ORM-specific accessors""" + def __init__(self, class_, *args, **kwargs): + super(UOWProperty, self).__init__(*args, **kwargs) + self.class_ = class_ + property = property(lambda s:class_mapper(s.class_).props[s.key], doc="returns the MapperProperty object associated with this property") + +class UOWListElement(attributes.ListElement): + """overrides ListElement to provide unit-of-work "dirty" hooks when list attributes are modified, + plus specialzed append() method.""" + def __init__(self, obj, key, data=None, deleteremoved=False, **kwargs): + attributes.ListElement.__init__(self, obj, key, data=data, **kwargs) + self.deleteremoved = deleteremoved + def list_value_changed(self, obj, key, item, listval, isdelete): + sess = get_session(obj) + if not isdelete and sess.deleted.contains(item): + raise InvalidRequestError("re-inserting a deleted value into a list") + sess.modified_lists.append(self) + if self.deleteremoved and isdelete: + sess.register_deleted(item) + def append(self, item, _mapper_nohistory = False): + if _mapper_nohistory: + self.append_nohistory(item) + else: + attributes.ListElement.append(self, item) + +class UOWAttributeManager(attributes.AttributeManager): + """overrides AttributeManager to provide unit-of-work "dirty" hooks when scalar attribues are modified, plus factory methods for UOWProperrty/UOWListElement.""" + def __init__(self): + attributes.AttributeManager.__init__(self) + + def value_changed(self, obj, key, value): + if hasattr(obj, '_instance_key'): + get_session(obj).register_dirty(obj) + else: + get_session(obj).register_new(obj) + + def create_prop(self, class_, key, uselist, **kwargs): + return UOWProperty(class_, self, key, uselist) + + def create_list(self, obj, key, list_, **kwargs): + return UOWListElement(obj, key, list_, **kwargs) + +class UnitOfWork(object): + """main UOW object which stores lists of dirty/new/deleted objects, as well as 'modified_lists' for list attributes. provides top-level "commit" functionality as well as the transaction boundaries with the SQLEngine(s) involved in a write operation.""" + def __init__(self, identity_map=None): + if identity_map is not None: + self.identity_map = identity_map + else: + self.identity_map = weakref.WeakValueDictionary() + + self.attributes = global_attributes + self.new = util.HashSet(ordered = True) + self.dirty = util.HashSet() + self.modified_lists = util.HashSet() + self.deleted = util.HashSet() + + def get(self, class_, *id): + """given a class and a list of primary key values in their table-order, locates the mapper + for this class and calls get with the given primary key values.""" + return object_mapper(class_).get(*id) + + def _get(self, key): + return self.identity_map[key] + + def _put(self, key, obj): + self.identity_map[key] = obj + + def refresh(self, obj): + self.rollback_object(obj) + object_mapper(obj)._get(obj._instance_key, reload=True) + + def has_key(self, key): + """returns True if the given key is present in this UnitOfWork's identity map.""" + return self.identity_map.has_key(key) + + def _remove_deleted(self, obj): + if hasattr(obj, "_instance_key"): + del self.identity_map[obj._instance_key] + try: + del self.deleted[obj] + except KeyError: + pass + try: + del self.dirty[obj] + except KeyError: + pass + try: + del self.new[obj] + except KeyError: + pass + self.attributes.commit(obj) + self.attributes.remove(obj) + + def _validate_obj(self, obj): + """validates that dirty/delete/commit operations can occur upon the given object, by checking + if it has an instance key and that the instance key is present in the identity map.""" + if hasattr(obj, '_instance_key') and not self.identity_map.has_key(obj._instance_key): + raise InvalidRequestError("Detected a mapped object not present in the current thread's Identity Map: '%s'. Use objectstore.import_instance() to place deserialized instances or instances from other threads" % repr(obj._instance_key)) + + def update(self, obj): + """called to add an object to this UnitOfWork as though it were loaded from the DB, + but is actually coming from somewhere else, like a web session or similar.""" + self._put(obj._instance_key, obj) + self.register_dirty(obj) + + def register_attribute(self, class_, key, uselist, **kwargs): + self.attributes.register_attribute(class_, key, uselist, **kwargs) + + def register_callable(self, obj, key, func, uselist, **kwargs): + self.attributes.set_callable(obj, key, func, uselist, **kwargs) + + def register_clean(self, obj): + try: + del self.dirty[obj] + except KeyError: + pass + try: + del self.new[obj] + except KeyError: + pass + if not hasattr(obj, '_instance_key'): + mapper = object_mapper(obj) + obj._instance_key = mapper.instance_key(obj) + self._put(obj._instance_key, obj) + self.attributes.commit(obj) + + def register_new(self, obj): + if not self.new.contains(obj): + self.new.append(obj) + + def register_dirty(self, obj): + if not self.dirty.contains(obj): + self._validate_obj(obj) + self.dirty.append(obj) + + def is_dirty(self, obj): + if not self.dirty.contains(obj): + return False + else: + return True + + def register_deleted(self, obj): + if not self.deleted.contains(obj): + self._validate_obj(obj) + self.deleted.append(obj) + mapper = object_mapper(obj) + # TODO: should the cascading delete dependency thing + # happen wtihin PropertyLoader.process_dependencies ? + mapper.register_deleted(obj, self) + + def unregister_deleted(self, obj): + try: + self.deleted.remove(obj) + except KeyError: + pass + + def commit(self, *objects): + commit_context = UOWTransaction(self) + + if len(objects): + objset = util.HashSet(iter=objects) + else: + objset = None + + for obj in [n for n in self.new] + [d for d in self.dirty]: + if objset is not None and not objset.contains(obj): + continue + if self.deleted.contains(obj): + continue + commit_context.register_object(obj) + for item in self.modified_lists: + obj = item.obj + if objset is not None and not objset.contains(obj): + continue + if self.deleted.contains(obj): + continue + commit_context.register_object(obj, listonly = True) + commit_context.register_saved_history(item) + +# for o in item.added_items() + item.deleted_items(): +# if self.deleted.contains(o): +# continue +# commit_context.register_object(o, listonly=True) + + for obj in self.deleted: + if objset is not None and not objset.contains(obj): + continue + commit_context.register_object(obj, isdelete=True) + + engines = util.HashSet() + for mapper in commit_context.mappers: + for e in mapper.engines: + engines.append(e) + + echo_commit = False + for e in engines: + echo_commit = echo_commit or e.echo_uow + e.begin() + try: + commit_context.execute(echo=echo_commit) + except: + for e in engines: + e.rollback() + raise + for e in engines: + e.commit() + + commit_context.post_exec() + + + def rollback_object(self, obj): + """'rolls back' the attributes that have been changed on an object instance.""" + self.attributes.rollback(obj) + +class UOWTransaction(object): + """handles the details of organizing and executing transaction tasks + during a UnitOfWork object's commit() operation.""" + def __init__(self, uow): + self.uow = uow + + # unique list of all the mappers we come across + self.mappers = util.HashSet() + self.dependencies = {} + self.tasks = {} + self.saved_histories = util.HashSet() + self.__modified = False + + def register_object(self, obj, isdelete = False, listonly = False, postupdate=False, **kwargs): + """adds an object to this UOWTransaction to be updated in the database. + + 'isdelete' indicates whether the object is to be deleted or saved (update/inserted). + + 'listonly', indicates that only this object's dependency relationships should be + refreshed/updated to reflect a recent save/upcoming delete operation, but not a full + save/delete operation on the object itself, unless an additional save/delete + registration is entered for the object.""" + #print "REGISTER", repr(obj), repr(getattr(obj, '_instance_key', None)), str(isdelete), str(listonly) + # things can get really confusing if theres duplicate instances floating around, + # so make sure everything is OK + self.uow._validate_obj(obj) + + mapper = object_mapper(obj) + self.mappers.append(mapper) + task = self.get_task_by_mapper(mapper) + + if postupdate: + mod = task.append_postupdate(obj) + self.__modified = self.__modified or mod + return + + # for a cyclical task, things need to be sorted out already, + # so this object should have already been added to the appropriate sub-task + # can put an assertion here to make sure.... + if task.circular: + return + + mod = task.append(obj, listonly, isdelete=isdelete, **kwargs) + self.__modified = self.__modified or mod + + def unregister_object(self, obj): + mapper = object_mapper(obj) + task = self.get_task_by_mapper(mapper) + task.delete(obj) + self.__modified = True + + def get_task_by_mapper(self, mapper): + """every individual mapper involved in the transaction has a single + corresponding UOWTask object, which stores all the operations involved + with that mapper as well as operations dependent on those operations. + this method returns or creates the single per-transaction instance of + UOWTask that exists for that mapper.""" + try: + return self.tasks[mapper] + except KeyError: + return UOWTask(self, mapper) + + def register_dependency(self, mapper, dependency): + """called by mapper.PropertyLoader to register the objects handled by + one mapper being dependent on the objects handled by another.""" + # correct for primary mapper (the mapper offcially associated with the class) + self.dependencies[(mapper._primary_mapper(), dependency._primary_mapper())] = True + self.__modified = True + + def register_processor(self, mapper, processor, mapperfrom, isdeletefrom): + """called by mapper.PropertyLoader to register itself as a "processor", which + will be associated with a particular UOWTask, and be given a list of "dependent" + objects corresponding to another UOWTask to be processed, either after that secondary + task saves its objects or before it deletes its objects.""" + # when the task from "mapper" executes, take the objects from the task corresponding + # to "mapperfrom"'s list of save/delete objects, and send them to "processor" + # for dependency processing + #print "registerprocessor", str(mapper), repr(processor.key), str(mapperfrom), repr(isdeletefrom) + + # correct for primary mapper (the mapper offcially associated with the class) + mapper = mapper._primary_mapper() + mapperfrom = mapperfrom._primary_mapper() + task = self.get_task_by_mapper(mapper) + targettask = self.get_task_by_mapper(mapperfrom) + task.dependencies.append(UOWDependencyProcessor(processor, targettask, isdeletefrom)) + self.__modified = True + + def register_saved_history(self, listobj): + self.saved_histories.append(listobj) + + def execute(self, echo=False): + for task in self.tasks.values(): + task.mapper.register_dependencies(self) + + head = self._sort_dependencies() + self.__modified = False + if LOG or echo: + if head is None: + print "Task dump: None" + else: + print "Task dump:\n" + head.dump() + if head is not None: + head.execute(self) + if LOG or echo: + if self.__modified and head is not None: + print "\nAfter Execute:\n" + head.dump() + else: + print "\nExecute complete (no post-exec changes)\n" + + def post_exec(self): + """after an execute/commit is completed, all of the objects and lists that have + been committed are updated in the parent UnitOfWork object to mark them as clean.""" + + for task in self.tasks.values(): + for elem in task.objects.values(): + if elem.isdelete: + self.uow._remove_deleted(elem.obj) + else: + self.uow.register_clean(elem.obj) + + for obj in self.saved_histories: + try: + obj.commit() + del self.uow.modified_lists[obj] + except KeyError: + pass + + # this assertion only applies to a full commit(), not a + # partial one + #if len(self.uow.new) > 0 or len(self.uow.dirty) >0 or len(self.uow.modified_lists) > 0: + # raise "assertion failed" + + def _sort_dependencies(self): + ""&... [truncated message content] |
<!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>[1150] sqlalchemy/trunk/lib/sqlalchemy/databases/oracle.py: John Dell'Aquila's patch which fixes [ticket:103] [ticket:105], selecting primary keys properly and using the ALL_* instead of USER_* tables</title> </head> <body> <div id="msg"> <dl> <dt>Revision</dt> <dd>1150</dd> <dt>Author</dt> <dd>zzzeek</dd> <dt>Date</dt> <dd>2006-03-16 17:55:00 -0600 (Thu, 16 Mar 2006)</dd> </dl> <h3>Log Message</h3> <pre>John Dell'Aquila's patch which fixes [ticket:103] [ticket:105], selecting primary keys properly and using the ALL_* instead of USER_* tables</pre> <h3>Modified Paths</h3> <ul> <li><a href="#sqlalchemytrunklibsqlalchemydatabasesoraclepy">sqlalchemy/trunk/lib/sqlalchemy/databases/oracle.py</a></li> </ul> </div> <div id="patch"> <h3>Diff</h3> <a id="sqlalchemytrunklibsqlalchemydatabasesoraclepy"></a> <div class="modfile"><h4>Modified: sqlalchemy/trunk/lib/sqlalchemy/databases/oracle.py (1149 => 1150)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/trunk/lib/sqlalchemy/databases/oracle.py 2006-03-16 23:38:14 UTC (rev 1149) +++ sqlalchemy/trunk/lib/sqlalchemy/databases/oracle.py 2006-03-16 23:55:00 UTC (rev 1150) </span><span class="lines">@@ -75,6 +75,24 @@ </span><span class="cx"> 'CLOB' : OracleText </span><span class="cx"> } </span><span class="cx"> </span><ins>+constraintSQL = """SELECT + ac.constraint_name, + ac.constraint_type, + LOWER(loc.column_name) AS local_column, + LOWER(rem.table_name) AS remote_table, + LOWER(rem.column_name) AS remote_column +FROM all_constraints ac, + all_cons_columns loc, + all_cons_columns rem +WHERE ac.table_name = :table_name +AND ac.constraint_type IN ('R','P') +AND ac.owner = loc.owner +AND ac.constraint_name = loc.constraint_name +AND ac.r_owner = rem.owner(+) +AND ac.r_constraint_name = rem.constraint_name(+) +-- order multiple primary keys correctly +ORDER BY ac.constraint_name, loc.position""" + </ins><span class="cx"> def engine(*args, **params): </span><span class="cx"> return OracleSQLEngine(*args, **params) </span><span class="cx"> </span><span class="lines">@@ -123,7 +141,7 @@ </span><span class="cx"> return OracleDefaultRunner(self, proxy) </span><span class="cx"> </span><span class="cx"> def reflecttable(self, table): </span><del>- c = self.execute ("select COLUMN_NAME, DATA_TYPE, DATA_LENGTH, DATA_PRECISION, DATA_SCALE, NULLABLE, DATA_DEFAULT from USER_TAB_COLUMNS where TABLE_NAME = :table_name", {'table_name':table.name.upper()}) </del><ins>+ c = self.execute ("select COLUMN_NAME, DATA_TYPE, DATA_LENGTH, DATA_PRECISION, DATA_SCALE, NULLABLE, DATA_DEFAULT from ALL_TAB_COLUMNS where TABLE_NAME = :table_name", {'table_name':table.name.upper()}) </ins><span class="cx"> </span><span class="cx"> while True: </span><span class="cx"> row = c.fetchone() </span><span class="lines">@@ -159,23 +177,22 @@ </span><span class="cx"> table.append_item (schema.Column(name, coltype, nullable=nullable, *colargs)) </span><span class="cx"> </span><span class="cx"> </span><del>- c = self.execute("""select UCC.CONSTRAINT_NAME, UCC.COLUMN_NAME, UC.CONSTRAINT_TYPE, UC.SEARCH_CONDITION, UC2.TABLE_NAME as REFERENCES_TABLE -from USER_CONS_COLUMNS UCC, USER_CONSTRAINTS UC, USER_CONSTRAINTS UC2 -where UCC.CONSTRAINT_NAME = UC.CONSTRAINT_NAME -and UC.R_CONSTRAINT_NAME = UC2.CONSTRAINT_NAME(+) -and UCC.TABLE_NAME = :table_name -order by UCC.CONSTRAINT_NAME""",{'table_name' : table.name.upper()}) </del><ins>+ c = self.execute(constraintSQL, {'table_name' : table.name.upper()}) </ins><span class="cx"> while True: </span><span class="cx"> row = c.fetchone() </span><span class="cx"> if row is None: </span><span class="cx"> break </span><span class="cx"> #print "ROW:" , row </span><del>- (cons_name, column_name, type, search, referred_table) = row - if type=='P' : - table.c[column_name.lower()]._set_primary_key() - elif type=='R': - remotetable = Table(referred_table.lower(), table.engine, autoload = True) - table.c[column_name.lower()].append_item(schema.ForeignKey(remotetable.primary_key[0])) </del><ins>+ (cons_name, cons_type, local_column, remote_table, remote_column) = row + if cons_type == 'P': + table.c[local_column]._set_primary_key() + elif cons_type == 'R': + table.c[local_column].append_item( + schema.ForeignKey(Table(remote_table, + self, + autoload=True).c[remote_column] + ) + ) </ins><span class="cx"> </span><span class="cx"> def last_inserted_ids(self): </span><span class="cx"> return self.context.last_inserted_ids </span></span></pre> </div> </div> </body> </html> |
From: <co...@sq...> - 2006-03-16 23:38:25
|
<!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>[1149] sqlalchemy/trunk/test: expanded and integrated qvx's patch for dotted function names</title> </head> <body> <div id="msg"> <dl> <dt>Revision</dt> <dd>1149</dd> <dt>Author</dt> <dd>zzzeek</dd> <dt>Date</dt> <dd>2006-03-16 17:38:14 -0600 (Thu, 16 Mar 2006)</dd> </dl> <h3>Log Message</h3> <pre>expanded and integrated qvx's patch for dotted function names</pre> <h3>Modified Paths</h3> <ul> <li><a href="#sqlalchemytrunklibsqlalchemyansisqlpy">sqlalchemy/trunk/lib/sqlalchemy/ansisql.py</a></li> <li><a href="#sqlalchemytrunklibsqlalchemyenginepy">sqlalchemy/trunk/lib/sqlalchemy/engine.py</a></li> <li><a href="#sqlalchemytrunklibsqlalchemysqlpy">sqlalchemy/trunk/lib/sqlalchemy/sql.py</a></li> <li><a href="#sqlalchemytrunktestselectpy">sqlalchemy/trunk/test/select.py</a></li> </ul> </div> <div id="patch"> <h3>Diff</h3> <a id="sqlalchemytrunklibsqlalchemyansisqlpy"></a> <div class="modfile"><h4>Modified: sqlalchemy/trunk/lib/sqlalchemy/ansisql.py (1148 => 1149)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/trunk/lib/sqlalchemy/ansisql.py 2006-03-16 19:09:53 UTC (rev 1148) +++ sqlalchemy/trunk/lib/sqlalchemy/ansisql.py 2006-03-16 23:38:14 UTC (rev 1149) </span><span class="lines">@@ -224,9 +224,9 @@ </span><span class="cx"> if len(self.select_stack): </span><span class="cx"> self.typemap.setdefault(func.name, func.type) </span><span class="cx"> if func.name.upper() in ANSI_FUNCS and not len(func.clauses): </span><del>- self.strings[func] = func.name </del><ins>+ self.strings[func] = ".".join(func.packagenames + [func.name]) </ins><span class="cx"> else: </span><del>- self.strings[func] = func.name + "(" + string.join([self.get_str(c) for c in func.clauses], ', ') + ")" </del><ins>+ self.strings[func] = ".".join(func.packagenames + [func.name]) + "(" + string.join([self.get_str(c) for c in func.clauses], ', ') + ")" </ins><span class="cx"> </span><span class="cx"> def visit_compound_select(self, cs): </span><span class="cx"> text = string.join([self.get_str(c) for c in cs.selects], " " + cs.keyword + " ") </span></span></pre></div> <a id="sqlalchemytrunklibsqlalchemyenginepy"></a> <div class="modfile"><h4>Modified: sqlalchemy/trunk/lib/sqlalchemy/engine.py (1148 => 1149)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/trunk/lib/sqlalchemy/engine.py 2006-03-16 19:09:53 UTC (rev 1148) +++ sqlalchemy/trunk/lib/sqlalchemy/engine.py 2006-03-16 23:38:14 UTC (rev 1149) </span><span class="lines">@@ -280,10 +280,7 @@ </span><span class="cx"> return typeobj </span><span class="cx"> </span><span class="cx"> def _func(self): </span><del>- class FunctionGateway(object): - def __getattr__(s, name): - return lambda *c, **kwargs: sql.Function(name, engine=self, *c, **kwargs) - return FunctionGateway() </del><ins>+ return sql.FunctionGenerator(self) </ins><span class="cx"> func = property(_func) </span><span class="cx"> </span><span class="cx"> def text(self, text, *args, **kwargs): </span></span></pre></div> <a id="sqlalchemytrunklibsqlalchemysqlpy"></a> <div class="modfile"><h4>Modified: sqlalchemy/trunk/lib/sqlalchemy/sql.py (1148 => 1149)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/trunk/lib/sqlalchemy/sql.py 2006-03-16 19:09:53 UTC (rev 1148) +++ sqlalchemy/trunk/lib/sqlalchemy/sql.py 2006-03-16 23:38:14 UTC (rev 1149) </span><span class="lines">@@ -218,7 +218,7 @@ </span><span class="cx"> """returns a callable based on an attribute name, which then returns a Function </span><span class="cx"> object with that name.""" </span><span class="cx"> def __getattr__(self, name): </span><del>- return lambda *c, **kwargs: Function(name, *c, **kwargs) </del><ins>+ return getattr(FunctionGenerator(), name) </ins><span class="cx"> func = FunctionGateway() </span><span class="cx"> </span><span class="cx"> def _compound_clause(keyword, *clauses): </span><span class="lines">@@ -794,6 +794,7 @@ </span><span class="cx"> def __init__(self, name, *clauses, **kwargs): </span><span class="cx"> self.name = name </span><span class="cx"> self.type = kwargs.get('type', sqltypes.NULLTYPE) </span><ins>+ self.packagenames = kwargs.get('packagenames') </ins><span class="cx"> self._engine = kwargs.get('engine', None) </span><span class="cx"> if self._engine is not None: </span><span class="cx"> self.type = self._engine.type_descriptor(self.type) </span><span class="lines">@@ -827,6 +828,17 @@ </span><span class="cx"> return select([self]).execute() </span><span class="cx"> def _compare_type(self, obj): </span><span class="cx"> return self.type </span><ins>+ +class FunctionGenerator(object): + """generates Function objects based on getattr calls""" + def __init__(self, engine=None): + self.__engine = engine + self.__names = [] + def __getattr__(self, name): + self.__names.append(name) + return self + def __call__(self, *c, **kwargs): + return Function(self.__names[-1], packagenames=self.__names[0:-1], engine=self.__engine, *c, **kwargs) </ins><span class="cx"> </span><span class="cx"> class BinaryClause(ClauseElement): </span><span class="cx"> """represents two clauses with an operator in between""" </span></span></pre></div> <a id="sqlalchemytrunktestselectpy"></a> <div class="modfile"><h4>Modified: sqlalchemy/trunk/test/select.py (1148 => 1149)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/trunk/test/select.py 2006-03-16 19:09:53 UTC (rev 1148) +++ sqlalchemy/trunk/test/select.py 2006-03-16 23:38:14 UTC (rev 1149) </span><span class="lines">@@ -343,12 +343,27 @@ </span><span class="cx"> "SELECT :literal + :literal_1 FROM mytable") </span><span class="cx"> </span><span class="cx"> def testfunction(self): </span><ins>+ """tests the generation of functions using the func keyword""" + # test an expression with a function </ins><span class="cx"> self.runtest(func.lala(3, 4, literal("five"), table1.c.myid) * table2.c.otherid, </span><span class="cx"> "lala(:lala, :lala_1, :literal, mytable.myid) * myothertable.otherid") </span><span class="cx"> </span><ins>+ # test it in a SELECT </ins><span class="cx"> self.runtest(select([func.count(table1.c.myid)]), </span><span class="cx"> "SELECT count(mytable.myid) FROM mytable") </span><span class="cx"> </span><ins>+ # test a "dotted" function name + self.runtest(select([func.foo.bar.lala(table1.c.myid)]), + "SELECT foo.bar.lala(mytable.myid) FROM mytable") + + # test the bind parameter name with a "dotted" function name is only the name + # (limits the length of the bind param name) + self.runtest(select([func.foo.bar.lala(12)]), + "SELECT foo.bar.lala(:lala)") + + # test a dotted func off the engine itself + self.runtest(db.func.lala.hoho(7), "lala.hoho(:hoho)") + </ins><span class="cx"> def testjoin(self): </span><span class="cx"> self.runtest( </span><span class="cx"> join(table2, table1, table1.c.myid == table2.c.otherid).select(), </span></span></pre> </div> </div> </body> </html> |
<!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>[1148] sqlalchemy/trunk/test: reorganized SingletonThreadPool to return distinct connections in the same thread; use_threadlocal behavior is now switchable</title> </head> <body> <div id="msg"> <dl> <dt>Revision</dt> <dd>1148</dd> <dt>Author</dt> <dd>zzzeek</dd> <dt>Date</dt> <dd>2006-03-16 13:09:53 -0600 (Thu, 16 Mar 2006)</dd> </dl> <h3>Log Message</h3> <pre>reorganized SingletonThreadPool to return distinct connections in the same thread; use_threadlocal behavior is now switchable</pre> <h3>Modified Paths</h3> <ul> <li><a href="#sqlalchemytrunklibsqlalchemypoolpy">sqlalchemy/trunk/lib/sqlalchemy/pool.py</a></li> <li><a href="#sqlalchemytrunktestpoolpy">sqlalchemy/trunk/test/pool.py</a></li> </ul> </div> <div id="patch"> <h3>Diff</h3> <a id="sqlalchemytrunklibsqlalchemypoolpy"></a> <div class="modfile"><h4>Modified: sqlalchemy/trunk/lib/sqlalchemy/pool.py (1147 => 1148)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/trunk/lib/sqlalchemy/pool.py 2006-03-16 19:08:35 UTC (rev 1147) +++ sqlalchemy/trunk/lib/sqlalchemy/pool.py 2006-03-16 19:09:53 UTC (rev 1148) </span><span class="lines">@@ -120,7 +120,7 @@ </span><span class="cx"> raise NotImplementedError() </span><span class="cx"> </span><span class="cx"> def log(self, msg): </span><del>- self.logger.write(msg) </del><ins>+ self._logger.write(msg) </ins><span class="cx"> </span><span class="cx"> class ConnectionFairy(object): </span><span class="cx"> def __init__(self, pool, connection=None): </span><span class="lines">@@ -155,19 +155,17 @@ </span><span class="cx"> """Maintains one connection per each thread, never moving to another thread. this is </span><span class="cx"> used for SQLite and other databases with a similar restriction.""" </span><span class="cx"> def __init__(self, creator, **params): </span><del>- params['use_threadlocal'] = False </del><span class="cx"> Pool.__init__(self, **params) </span><span class="cx"> self._conns = {} </span><span class="cx"> self._creator = creator </span><span class="cx"> </span><span class="cx"> def status(self): </span><del>- return "SingletonThreadPool size: %d" % len(self._conns) </del><ins>+ return "SingletonThreadPool thread:%d size: %d" % (thread.get_ident(), len(self._conns)) </ins><span class="cx"> </span><del>- def unique_connection(self): - return ConnectionFairy(self, self._creator()) </del><ins>+ def do_return_conn(self, conn): + if self._conns.get(thread.get_ident(), None) is None: + self._conns[thread.get_ident()] = conn </ins><span class="cx"> </span><del>- def do_return_conn(self, conn): - pass </del><span class="cx"> def do_return_invalid(self): </span><span class="cx"> try: </span><span class="cx"> del self._conns[thread.get_ident()] </span><span class="lines">@@ -176,9 +174,13 @@ </span><span class="cx"> </span><span class="cx"> def do_get(self): </span><span class="cx"> try: </span><del>- return self._conns[thread.get_ident()] </del><ins>+ c = self._conns[thread.get_ident()] + if c is None: + return self._creator() </ins><span class="cx"> except KeyError: </span><del>- return self._conns.setdefault(thread.get_ident(), self._creator()) </del><ins>+ c = self._creator() + self._conns[thread.get_ident()] = None + return c </ins><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></span></pre></div> <a id="sqlalchemytrunktestpoolpy"></a> <div class="modfile"><h4>Modified: sqlalchemy/trunk/test/pool.py (1147 => 1148)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/trunk/test/pool.py 2006-03-16 19:08:35 UTC (rev 1147) +++ sqlalchemy/trunk/test/pool.py 2006-03-16 19:09:53 UTC (rev 1148) </span><span class="lines">@@ -69,7 +69,22 @@ </span><span class="cx"> self.assert_(status(p) == (3, 1, 0, 2)) </span><span class="cx"> c2 = None </span><span class="cx"> self.assert_(status(p) == (3, 2, 0, 1)) </span><del>- </del><ins>+ + def testthreadlocal(self): + for p in ( + pool.QueuePool(creator = lambda: sqlite.connect('foo.db'), pool_size = 3, max_overflow = -1, use_threadlocal = True, echo = False), + pool.SingletonThreadPool(creator = lambda: sqlite.connect('foo.db'), use_threadlocal = True) + ): + c1 = p.connect() + c2 = p.connect() + self.assert_(c1 is c2) + c3 = p.unique_connection() + self.assert_(c3 is not c1) + c2 = None + c2 = p.connect() + self.assert_(c1 is c2) + self.assert_(c3 is not c1) + </ins><span class="cx"> def tearDown(self): </span><span class="cx"> pool.clear_managers() </span><span class="cx"> for file in ('foo.db', 'bar.db'): </span></span></pre> </div> </div> </body> </html> |
From: <co...@sq...> - 2006-03-16 19:08:51
|
<!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>[1147] sqlalchemy/trunk/doc/build: added txt2myt.py to the genhtml/runhtml scripts, added exception if required modules arent found.</title> </head> <body> <div id="msg"> <dl> <dt>Revision</dt> <dd>1147</dd> <dt>Author</dt> <dd>zzzeek</dd> <dt>Date</dt> <dd>2006-03-16 13:08:35 -0600 (Thu, 16 Mar 2006)</dd> </dl> <h3>Log Message</h3> <pre>added txt2myt.py to the genhtml/runhtml scripts, added exception if required modules arent found. edited tutorial.txt, added particles, etc. added clue that firebird might be supported to dbengine.myt</pre> <h3>Modified Paths</h3> <ul> <li><a href="#sqlalchemytrunkdocbuildcontentdbenginemyt">sqlalchemy/trunk/doc/build/content/dbengine.myt</a></li> <li><a href="#sqlalchemytrunkdocbuildcontenttutorialtxt">sqlalchemy/trunk/doc/build/content/tutorial.txt</a></li> <li><a href="#sqlalchemytrunkdocbuildgenhtmlpy">sqlalchemy/trunk/doc/build/genhtml.py</a></li> <li><a href="#sqlalchemytrunkdocbuildrunhtmlpy">sqlalchemy/trunk/doc/build/runhtml.py</a></li> <li><a href="#sqlalchemytrunkdocbuildtxt2mytpy">sqlalchemy/trunk/doc/build/txt2myt.py</a></li> </ul> </div> <div id="patch"> <h3>Diff</h3> <a id="sqlalchemytrunkdocbuildcontentdbenginemyt"></a> <div class="modfile"><h4>Modified: sqlalchemy/trunk/doc/build/content/dbengine.myt (1146 => 1147)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/trunk/doc/build/content/dbengine.myt 2006-03-16 17:48:51 UTC (rev 1146) +++ sqlalchemy/trunk/doc/build/content/dbengine.myt 2006-03-16 19:08:35 UTC (rev 1147) </span><span class="lines">@@ -11,7 +11,7 @@ </span><span class="cx"> </p> </span><span class="cx"> <&|doclib.myt:item, name="establishing", description="Establishing a Database Engine" &> </span><span class="cx"> <p> </span><del>- Engines exist for SQLite, Postgres, MySQL, and Oracle, using the Pysqlite, Psycopg (1 or 2), MySQLDB, and cx_Oracle modules. Each engine imports its corresponding module which is required to be installed. For Postgres and Oracle, an alternate module may be specified at construction time as well. </del><ins>+ Engines exist for SQLite, Postgres, MySQL, and Oracle, using the Pysqlite, Psycopg (1 or 2), MySQLDB, and cx_Oracle modules (there is also experimental support for Firebird). Each engine imports its corresponding module which is required to be installed. For Postgres and Oracle, an alternate module may be specified at construction time as well. </ins><span class="cx"> </p> </span><span class="cx"> <p>The string based argument names for connecting are translated to the appropriate names when the connection is made; argument names include "host" or "hostname" for database host, "database", "db", or "dbname" for the database name (also is dsn for Oracle), "user" or "username" for the user, and "password", "pw", or "passwd" for the password. SQLite expects "filename" or "file" for the filename, or if None it defaults to "":memory:".</p> </span><span class="cx"> <p>The connection arguments can be specified as a string + dictionary pair, or a single URL-encoded string, as follows:</p> </span></span></pre></div> <a id="sqlalchemytrunkdocbuildcontenttutorialtxt"></a> <div class="modfile"><h4>Modified: sqlalchemy/trunk/doc/build/content/tutorial.txt (1146 => 1147)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/trunk/doc/build/content/tutorial.txt 2006-03-16 17:48:51 UTC (rev 1146) +++ sqlalchemy/trunk/doc/build/content/tutorial.txt 2006-03-16 19:08:35 UTC (rev 1147) </span><span class="lines">@@ -1,9 +1,9 @@ </span><span class="cx"> Tutorial </span><span class="cx"> ======== </span><del>-In this tutorial we will guide you to get started with SQLAlchemy, Python SQL toolkit and Object Relational Mapper. -SQLAlchemy provides a *lot* of functionality that will help you manage your SQL database. But very little is needed to know in order to begin doing useful things with it. </del><ins>+This tutorial is a "quick start" guide to SQLAlchemy, the Python SQL Toolkit and Object Relational Mapper. +SQLAlchemy provides a lot of functionality to help manage SQL databases, but you don't need much in order to begin doing useful things with it. </ins><span class="cx"> </span><del>-Note that it is not neccessary to read this tutorial, you may wish to skip it and dive into [main manual][manual]. </del><ins>+Note that it is not neccessary to read this tutorial; you may wish to skip it and dive into the [main manual][manual] which is more reference-oriented. </ins><span class="cx"> </span><span class="cx"> [manual]: rel:howtoread </span><span class="cx"> </span><span class="lines">@@ -22,16 +22,18 @@ </span><span class="cx"> [install setuptools]: http://peak.telecommunity.com/DevCenter/EasyInstall#installation-instructions </span><span class="cx"> [cheese]: http://cheeseshop.python.org/pypi </span><span class="cx"> </span><del>-### Installing Database Manager {@name=dbms} </del><ins>+### Installing A Database Manager {@name=dbms} </ins><span class="cx"> </span><del>-If you have one of the [supported database managers][supported dbms], you can proceed to the following section. Otherwise we recommend installing [SQLite][]. SQLite needs very little configuration and is easy to work with. </del><ins>+ SQLAlchemy is designed to operate with the [DBAPI][DBAPI] built for a particular database, and includes implementations supporting the most popular ones. If you have one of the [supported database managers][supported dbms], you can proceed to the following section. Otherwise we recommend installing [SQLite][] for starters. SQLite needs very little configuration and is easy to work with. </ins><span class="cx"> </span><ins>+[DBAPI]: http://www.python.org/doc/peps/pep-0249/ + </ins><span class="cx"> To work with SQLite, you'll need: </span><span class="cx"> </span><span class="cx"> * SQLite library </span><span class="cx"> * [pysqlite][] - Python interface for SQLite </span><span class="cx"> </span><del>-If you use Windows, you only have to download and install compiled [pysqlite binary][pysqlite]. It includes SQLite library, so you don't have to install it separately. </del><ins>+If you use Windows, you only have to download and install the compiled [pysqlite binary][pysqlite]. It includes the SQLite library already linked in, so you don't have to install it separately. </ins><span class="cx"> </span><span class="cx"> If you use Linux or FreeBSD, you may want to install pysqlite and SQLite from [packages][pysqlite packages] made for your operating system. Or you may install them [from sources][pysqlite]. </span><span class="cx"> </span><span class="lines">@@ -43,18 +45,18 @@ </span><span class="cx"> Get Going! {@name=getgoing} </span><span class="cx"> -------------------------- </span><span class="cx"> </span><del>-### Connecting to Database </del><ins>+### Connecting to the Database </ins><span class="cx"> </span><del>-First of all you need to connect to database you want to work with: </del><ins>+First, you need to connect to the database you want to work with: </ins><span class="cx"> </span><span class="cx"> >>> from sqlalchemy import * </span><del>- >>> db = create_engine('sqlite', {'filename': 'tutorial.db'}) </del><ins>+ >>> db = create_engine('sqlite://filename=tutorial.db') </ins><span class="cx"> </span><span class="cx"> Main documentation: [dbengine](rel:dbengine). </span><span class="cx"> </span><span class="cx"> ### Creating a Table {@name=table} </span><span class="cx"> </span><del>-SQL tables and your domain classes are different beasts. That's why SQLAlchemy don't mix them. Let's construct an object that represents an SQL table: </del><ins>+A core philosophy of SQLAlchemy is that tables and domain classes are different beasts. That's why SQLAlchemy doesn't mix them. So let's first work with just tables alone; we construct an object that represents a table: </ins><span class="cx"> </span><span class="cx"> >>> users = Table('users', db, </span><span class="cx"> ... Column('user_id', Integer, primary_key = True), </span><span class="lines">@@ -62,7 +64,7 @@ </span><span class="cx"> ... Column('password', String(80)) </span><span class="cx"> ... ) </span><span class="cx"> </span><del>-As you can guess we have just defined a table `users` with 3 columns: `user_id` (which is a primary key), `user_name` and `password`. Currently it is just an object that may not correspond to existing table in your database. So let's create the real table! To make it interesting we will ask SQLAlchemy to echo SQL statements it sends to database: </del><ins>+As you might have guessed, we have just defined a table `users` with 3 columns: `user_id` (which is a primary key column), `user_name` and `password`. Currently it is just an object that may not correspond to an existing table in your database. So let's create the real table! To make it interesting we will ask SQLAlchemy to echo the SQL statements it sends to the database: </ins><span class="cx"> </span><span class="cx"> >>> db.echo = True </span><span class="cx"> >>> users.create() # doctest:+ELLIPSIS,+NORMALIZE_WHITESPACE </span><span class="lines">@@ -74,7 +76,7 @@ </span><span class="cx"> ... </span><span class="cx"> >>> db.echo = False # you can skip this if you want to keep logging SQL statements </span><span class="cx"> </span><del>-Alternatively, `users` table could already exist (e.g. you're running examples from this introduction for the second time). In this case you can just skip `create()` method call. You can even skip defining `users` table in your code and ask SQLAlchemy to load its definition from the database: </del><ins>+Alternatively, the `users` table might already exist (such as, if you're running examples from this tutorial for the second time), in which case you can just skip the `create()` method call. You can even skip defining the individual columns in the `users` table and ask SQLAlchemy to load its definition from the database: </ins><span class="cx"> </span><span class="cx"> >>> users = Table('users', db, autoload = True) </span><span class="cx"> >>> list(users.columns)[0].name </span><span class="lines">@@ -84,7 +86,7 @@ </span><span class="cx"> </span><span class="cx"> ### Filling the Table </span><span class="cx"> </span><del>-Ok, we have the table. To insert some data into the table you have to prepare a query first. You can use `insert()` method to do it: </del><ins>+So now we have the table. To insert some data, use the `insert()` method to create a query: </ins><span class="cx"> </span><span class="cx"> >>> i = users.insert() </span><span class="cx"> >>> i # doctest:+ELLIPSIS </span><span class="lines">@@ -92,7 +94,7 @@ </span><span class="cx"> >>> print i </span><span class="cx"> INSERT INTO users (user_id, user_name, password) VALUES (?, ?, ?) </span><span class="cx"> </span><del>-Call `execute()` method of the prepared query to actually add users: </del><ins>+Call the `execute()` method of the query object to actually add users: </ins><span class="cx"> </span><span class="cx"> >>> for name in ['Tom', 'Dick', 'Harry']: # doctest:+ELLIPSIS </span><span class="cx"> ... i.execute(user_name = name) </span><span class="lines">@@ -101,7 +103,7 @@ </span><span class="cx"> >>> i.execute(user_name = 'Mary', password = 'secure') # doctest:+ELLIPSIS </span><span class="cx"> <sqlalchemy.engine.ResultProxy instance at 0x...> </span><span class="cx"> </span><del>-If possible, SQLAlchemy will bind values into a prepared query using native database API. This allows for better performance, because the database may cache compiled representation of the statement and reuse it between executions with different values. Also, when using bound values, you need not worry about [SQL injection][] attacks. </del><ins>+SQLAlchemy will bind all literal values into bind parameters, according to the paramstyle of the underlying DBAPI. This allows for better performance, because the database may cache a compiled representation of the statement and reuse upon new executions, substituting the new values. Also, when using bound values, you need not worry about [SQL injection][] attacks. </ins><span class="cx"> </span><span class="cx"> [SQL injection]: http://en.wikipedia.org/wiki/SQL_injection </span><span class="cx"> </span><span class="lines">@@ -109,7 +111,7 @@ </span><span class="cx"> </span><span class="cx"> ### Querying the Table </span><span class="cx"> </span><del>-Let's check that the data we have put into `users` table is actually there. The procedure is analogous to insert example above. The difference is that you have to call `select()` method now: </del><ins>+Let's check that the data we have put into `users` table is actually there. The procedure is analogous to the insert example above, except you now call the `select()` function: </ins><span class="cx"> </span><span class="cx"> >>> s = users.select() </span><span class="cx"> >>> print s </span><span class="lines">@@ -117,7 +119,7 @@ </span><span class="cx"> FROM users </span><span class="cx"> >>> r = s.execute() </span><span class="cx"> </span><del>-Now we won't ignore return value of `execute()`: </del><ins>+This time, we won't ignore the return value of `execute()`: </ins><span class="cx"> </span><span class="cx"> >>> r # doctest:+ELLIPSIS </span><span class="cx"> <sqlalchemy.engine.ResultProxy instance at 0x...> </span></span></pre></div> <a id="sqlalchemytrunkdocbuildgenhtmlpy"></a> <div class="modfile"><h4>Modified: sqlalchemy/trunk/doc/build/genhtml.py (1146 => 1147)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/trunk/doc/build/genhtml.py 2006-03-16 17:48:51 UTC (rev 1146) +++ sqlalchemy/trunk/doc/build/genhtml.py 2006-03-16 19:08:35 UTC (rev 1147) </span><span class="lines">@@ -1,6 +1,9 @@ </span><span class="cx"> #!/usr/bin/env python </span><span class="cx"> import sys,re,os </span><span class="cx"> </span><ins>+print "Running txt2myt.py..." +execfile("txt2myt.py") + </ins><span class="cx"> component_root = [ </span><span class="cx"> {'components': './components'}, </span><span class="cx"> {'content' : './content'} </span></span></pre></div> <a id="sqlalchemytrunkdocbuildrunhtmlpy"></a> <div class="modfile"><h4>Modified: sqlalchemy/trunk/doc/build/runhtml.py (1146 => 1147)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/trunk/doc/build/runhtml.py 2006-03-16 17:48:51 UTC (rev 1146) +++ sqlalchemy/trunk/doc/build/runhtml.py 2006-03-16 19:08:35 UTC (rev 1147) </span><span class="lines">@@ -1,6 +1,9 @@ </span><span class="cx"> #!/usr/bin/env python </span><span class="cx"> import sys,re,os </span><span class="cx"> </span><ins>+print "Running txt2myt.py..." +execfile("txt2myt.py") + </ins><span class="cx"> component_root = [ </span><span class="cx"> {'components': './components'}, </span><span class="cx"> {'content' : './content'} </span><span class="lines">@@ -23,4 +26,4 @@ </span><span class="cx"> </span><span class="cx"> ) </span><span class="cx"> </span><del>-httpd.serve_forever() </del><span class="cx">\ No newline at end of file </span><ins>+httpd.serve_forever() </ins></span></pre></div> <a id="sqlalchemytrunkdocbuildtxt2mytpy"></a> <div class="modfile"><h4>Modified: sqlalchemy/trunk/doc/build/txt2myt.py (1146 => 1147)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/trunk/doc/build/txt2myt.py 2006-03-16 17:48:51 UTC (rev 1146) +++ sqlalchemy/trunk/doc/build/txt2myt.py 2006-03-16 19:08:35 UTC (rev 1147) </span><span class="lines">@@ -6,7 +6,10 @@ </span><span class="cx"> import string </span><span class="cx"> import re </span><span class="cx"> </span><del>-import elementtree.ElementTree as et </del><ins>+try: + import elementtree.ElementTree as et +except: + raise "This module requires ElementTree to run (http://effbot.org/zone/element-index.htm)" </ins><span class="cx"> </span><span class="cx"> sys.path.insert(0, './lib') </span><span class="cx"> import markdown </span></span></pre> </div> </div> </body> </html> |
From: <co...@sq...> - 2006-03-16 17:49:04
|
<!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>[1146] sqlalchemy/trunk/lib/sqlalchemy/ext/sqlsoup.py: J.</title> </head> <body> <div id="msg"> <dl> <dt>Revision</dt> <dd>1146</dd> <dt>Author</dt> <dd>zzzeek</dd> <dt>Date</dt> <dd>2006-03-16 11:48:51 -0600 (Thu, 16 Mar 2006)</dd> </dl> <h3>Log Message</h3> <pre>J. Ellis' "Simple" ORM module...for Spyce ! (and others...)</pre> <h3>Added Paths</h3> <ul> <li><a href="#sqlalchemytrunklibsqlalchemyextsqlsouppy">sqlalchemy/trunk/lib/sqlalchemy/ext/sqlsoup.py</a></li> </ul> </div> <div id="patch"> <h3>Diff</h3> <a id="sqlalchemytrunklibsqlalchemyextsqlsouppy"></a> <div class="addfile"><h4>Added: sqlalchemy/trunk/lib/sqlalchemy/ext/sqlsoup.py (1145 => 1146)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/trunk/lib/sqlalchemy/ext/sqlsoup.py 2006-03-15 13:39:11 UTC (rev 1145) +++ sqlalchemy/trunk/lib/sqlalchemy/ext/sqlsoup.py 2006-03-16 17:48:51 UTC (rev 1146) </span><span class="lines">@@ -0,0 +1,72 @@ </span><ins>+from sqlalchemy import * + +class NoSuchTableError(SQLAlchemyError): pass + +# metaclass is necessary to expose class methods with getattr, e.g. +# we want to pass db.users.select through to users._mapper.select +class TableClassType(type): + def insert(cls, **kwargs): + o = cls() + o.__dict__.update(kwargs) + return o + def __getattr__(cls, attr): + if attr == '_mapper': + # called during mapper init + raise AttributeError() + return getattr(cls._mapper, attr) + +def class_for_table(table): + klass = TableClassType('Class_' + table.name.capitalize(), (object,), {}) + def __repr__(self): + import locale + encoding = locale.getdefaultlocale()[1] + L = [] + for k in self.__class__.c.keys(): + value = getattr(self, k, '') + if isinstance(value, unicode): + value = value.encode(encoding) + L.append("%s=%r" % (k, value)) + return '%s(%s)' % (self.__class__.__name__, ','.join(L)) + klass.__repr__ = __repr__ + klass._mapper = mapper(klass, table) + return klass + +class SqlSoup: + def __init__(self, *args, **kwargs): + """ + args may either be an SQLEngine or a set of arguments suitable + for passing to create_engine + """ + from sqlalchemy.engine import SQLEngine + # meh, sometimes having method overloading instead of kwargs would be easier + if isinstance(args[0], SQLEngine): + engine = args.pop(0) + if args or kwargs: + raise ArgumentError('Extra arguments not allowed when engine is given') + else: + engine = create_engine(*args, **kwargs) + self._engine = engine + self._cache = {} + def delete(self, *args, **kwargs): + objectstore.delete(*args, **kwargs) + def commit(self): + objectstore.get_session().commit() + def rollback(self): + objectstore.clear() + def _reset(self): + # for debugging + self._cache = {} + self.rollback() + def __getattr__(self, attr): + try: + t = self._cache[attr] + except KeyError: + table = Table(attr, self._engine, autoload=True) + if table.columns: + t = class_for_table(table) + else: + t = None + self._cache[attr] = t + if not t: + raise NoSuchTableError('%s does not exist' % attr) + return t </ins></span></pre> </div> </div> </body> </html> |
From: <co...@sq...> - 2006-03-15 13:39: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>[1145] sqlalchemy/trunk/doc/build: Tutorial draft (not finished) and documentation framework improvements</title> </head> <body> <div id="msg"> <dl> <dt>Revision</dt> <dd>1145</dd> <dt>Author</dt> <dd>ash</dd> <dt>Date</dt> <dd>2006-03-15 07:39:11 -0600 (Wed, 15 Mar 2006)</dd> </dl> <h3>Log Message</h3> <pre>Tutorial draft (not finished) and documentation framework improvements * a first step to a new documentation framework, using Markdown syntax, with some extensions (detailed in txt2myt.py docstrings): * `rel:something` for internal links * `{@name=something}` to override default header names (used when linking) * `{python}` to force code block to use Python syntax highlighting (not needed when using examples with `>>>` prompt) * txt2myt.py -- converter from .txt to .myt * a draft of tutorial.txt, which uses new syntax * testdocs.py -- check examples in documentation using doctest (currently only in tutorial.txt) </pre> <h3>Modified Paths</h3> <ul> <li><a href="#sqlalchemytrunkdocbuildcontentdocument_basemyt">sqlalchemy/trunk/doc/build/content/document_base.myt</a></li> <li><a href="#sqlalchemytrunkdocbuildcontenttrailmapmyt">sqlalchemy/trunk/doc/build/content/trailmap.myt</a></li> </ul> <h3>Added Paths</h3> <ul> <li><a href="#sqlalchemytrunkdocbuildcontenttutorialtxt">sqlalchemy/trunk/doc/build/content/tutorial.txt</a></li> <li><a href="#sqlalchemytrunkdocbuildlibmarkdownpy">sqlalchemy/trunk/doc/build/lib/markdown.py</a></li> <li><a href="#sqlalchemytrunkdocbuildtestdocspy">sqlalchemy/trunk/doc/build/testdocs.py</a></li> <li><a href="#sqlalchemytrunkdocbuildtxt2mytpy">sqlalchemy/trunk/doc/build/txt2myt.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 (1144 => 1145)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/trunk/doc/build/content/document_base.myt 2006-03-14 19:51:03 UTC (rev 1144) +++ sqlalchemy/trunk/doc/build/content/document_base.myt 2006-03-15 13:39:11 UTC (rev 1145) </span><span class="lines">@@ -3,6 +3,7 @@ </span><span class="cx"> <%python scope="global"> </span><span class="cx"> </span><span class="cx"> files = [ </span><ins>+ 'tutorial', </ins><span class="cx"> 'trailmap', </span><span class="cx"> 'pooling', </span><span class="cx"> 'dbengine', </span></span></pre></div> <a id="sqlalchemytrunkdocbuildcontenttrailmapmyt"></a> <div class="modfile"><h4>Modified: sqlalchemy/trunk/doc/build/content/trailmap.myt (1144 => 1145)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/trunk/doc/build/content/trailmap.myt 2006-03-14 19:51:03 UTC (rev 1144) +++ sqlalchemy/trunk/doc/build/content/trailmap.myt 2006-03-15 13:39:11 UTC (rev 1145) </span><span class="lines">@@ -1,8 +1,9 @@ </span><span class="cx"> <%flags>inherit='document_base.myt'</%flags> </span><del>-<%attr>title='Introduction'</%attr> -<&|doclib.myt:item, name="intro", description="Introduction" &> -<p>SQLAlchemy features a lot of tools and patterns to help in every area of writing applications that talk to relational databases. To achieve this, it has a lot of areas of functionality which work together to provide a cohesive package. Ultimately, just a little bit of familiarity with each concept is all thats needed to get off the ground.</p> </del><ins>+<%attr>title='How to Read this Manual'</%attr> +<&|doclib.myt:item, name="howtoread", description="How to Read this Manual" &> </ins><span class="cx"> </span><ins>+<p>SQLAlchemy features a lot of tools and patterns to help in every area of writing applications that talk to relational databases. To achieve this, it has a lot of areas of functionality which work together to provide a cohesive package. Ultimately, just a little bit of familiarity with each concept is all that's needed to get off the ground.</p> + </ins><span class="cx"> <p>That said, here's two quick links that summarize the two most prominent features of SQLAlchemy: </span><span class="cx"> <ul> </span><span class="cx"> <li><&formatting.myt:link, path="datamapping", class_="trailbold"&> - a synopsis of how to map objects to database tables (Object Relational Mapping)</li> </span></span></pre></div> <a id="sqlalchemytrunkdocbuildcontenttutorialtxt"></a> <div class="addfile"><h4>Added: sqlalchemy/trunk/doc/build/content/tutorial.txt (1144 => 1145)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/trunk/doc/build/content/tutorial.txt 2006-03-14 19:51:03 UTC (rev 1144) +++ sqlalchemy/trunk/doc/build/content/tutorial.txt 2006-03-15 13:39:11 UTC (rev 1145) </span><span class="lines">@@ -0,0 +1,151 @@ </span><ins>+Tutorial +======== +In this tutorial we will guide you to get started with SQLAlchemy, Python SQL toolkit and Object Relational Mapper. +SQLAlchemy provides a *lot* of functionality that will help you manage your SQL database. But very little is needed to know in order to begin doing useful things with it. + +Note that it is not neccessary to read this tutorial, you may wish to skip it and dive into [main manual][manual]. + +[manual]: rel:howtoread + +Installation +------------ + +### Installing SQLAlchemy {@name=sqlalchemy} + +Installing SQLAlchemy is easy if you have [setuptools][] on your system (if you don't, [install it][install setuptools], it's easy!). Just run this from the command-line: + + $ easy_install SQLAlchemy + +This command will download the latest version of SQLAlchemy from the [Python Cheese Shop][cheese] and install it to your system. + +[setuptools]: http://peak.telecommunity.com/DevCenter/setuptools +[install setuptools]: http://peak.telecommunity.com/DevCenter/EasyInstall#installation-instructions +[cheese]: http://cheeseshop.python.org/pypi + +### Installing Database Manager {@name=dbms} + +If you have one of the [supported database managers][supported dbms], you can proceed to the following section. Otherwise we recommend installing [SQLite][]. SQLite needs very little configuration and is easy to work with. + +To work with SQLite, you'll need: + + * SQLite library + * [pysqlite][] - Python interface for SQLite + +If you use Windows, you only have to download and install compiled [pysqlite binary][pysqlite]. It includes SQLite library, so you don't have to install it separately. + +If you use Linux or FreeBSD, you may want to install pysqlite and SQLite from [packages][pysqlite packages] made for your operating system. Or you may install them [from sources][pysqlite]. + +[supported dbms]: rel:dbengine_establishing +[sqlite]: http://sqlite.org/ +[pysqlite]: http://pysqlite.org/ +[pysqlite packages]: http://initd.org/tracker/pysqlite/wiki/PysqlitePackages + +Get Going! {@name=getgoing} +-------------------------- + +### Connecting to Database + +First of all you need to connect to database you want to work with: + + >>> from sqlalchemy import * + >>> db = create_engine('sqlite', {'filename': 'tutorial.db'}) + +Main documentation: [dbengine](rel:dbengine). + +### Creating a Table {@name=table} + +SQL tables and your domain classes are different beasts. That's why SQLAlchemy don't mix them. Let's construct an object that represents an SQL table: + + >>> users = Table('users', db, + ... Column('user_id', Integer, primary_key = True), + ... Column('user_name', String(40)), + ... Column('password', String(80)) + ... ) + +As you can guess we have just defined a table `users` with 3 columns: `user_id` (which is a primary key), `user_name` and `password`. Currently it is just an object that may not correspond to existing table in your database. So let's create the real table! To make it interesting we will ask SQLAlchemy to echo SQL statements it sends to database: + + >>> db.echo = True + >>> users.create() # doctest:+ELLIPSIS,+NORMALIZE_WHITESPACE + CREATE TABLE users( + user_id INTEGER NOT NULL PRIMARY KEY, + user_name VARCHAR(40), + password VARCHAR(80) + ) + ... + >>> db.echo = False # you can skip this if you want to keep logging SQL statements + +Alternatively, `users` table could already exist (e.g. you're running examples from this introduction for the second time). In this case you can just skip `create()` method call. You can even skip defining `users` table in your code and ask SQLAlchemy to load its definition from the database: + + >>> users = Table('users', db, autoload = True) + >>> list(users.columns)[0].name + 'user_id' + +Main documentation: [metadata](rel:metadata). + +### Filling the Table + +Ok, we have the table. To insert some data into the table you have to prepare a query first. You can use `insert()` method to do it: + + >>> i = users.insert() + >>> i # doctest:+ELLIPSIS + <sqlalchemy.sql.Insert object at 0x...> + >>> print i + INSERT INTO users (user_id, user_name, password) VALUES (?, ?, ?) + +Call `execute()` method of the prepared query to actually add users: + + >>> for name in ['Tom', 'Dick', 'Harry']: # doctest:+ELLIPSIS + ... i.execute(user_name = name) + <sqlalchemy.engine.ResultProxy instance at 0x...> + ... + >>> i.execute(user_name = 'Mary', password = 'secure') # doctest:+ELLIPSIS + <sqlalchemy.engine.ResultProxy instance at 0x...> + +If possible, SQLAlchemy will bind values into a prepared query using native database API. This allows for better performance, because the database may cache compiled representation of the statement and reuse it between executions with different values. Also, when using bound values, you need not worry about [SQL injection][] attacks. + +[SQL injection]: http://en.wikipedia.org/wiki/SQL_injection + +Main documentation: [sql_insert](rel:sql_insert). + +### Querying the Table + +Let's check that the data we have put into `users` table is actually there. The procedure is analogous to insert example above. The difference is that you have to call `select()` method now: + + >>> s = users.select() + >>> print s + SELECT users.user_id, users.user_name, users.password + FROM users + >>> r = s.execute() + +Now we won't ignore return value of `execute()`: + + >>> r # doctest:+ELLIPSIS + <sqlalchemy.engine.ResultProxy instance at 0x...> + >>> r.keys + ['user_id', 'user_name', 'password'] + >>> row = r.fetchone() + >>> row['user_name'] + u'Tom' + >>> r.fetchall() + [(2, u'Dick', None), (3, u'Harry', None), (4, u'Mary', u'secure')] + +Main documentation: [sql_select](rel:sql_select). + +### Related Table + +Main documentation: [sql](rel:sql). + +### Fancier Querying {@name=fancyquery} + +Main documentation: [sql](rel:sql). + +### Data Mapping {@name=mapping} + +Main documentation: [datamapping](rel:datamapping), [adv_datamapping](rel:adv_datamapping). + +### Transactions + +Main documentation: [unitofwork](rel:unitofwork), [dbengine_transactions](rel:dbengine_transactions). + +Conclusion +---------- </ins></span></pre></div> <a id="sqlalchemytrunkdocbuildlibmarkdownpy"></a> <div class="addfile"><h4>Added: sqlalchemy/trunk/doc/build/lib/markdown.py (1144 => 1145)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/trunk/doc/build/lib/markdown.py 2006-03-14 19:51:03 UTC (rev 1144) +++ sqlalchemy/trunk/doc/build/lib/markdown.py 2006-03-15 13:39:11 UTC (rev 1145) </span><span class="lines">@@ -0,0 +1,1746 @@ </span><ins>+#!/usr/bin/env python + +""" +==================================================================== +IF YOU ARE LOOKING TO EXTEND MARKDOWN, SEE THE "FOOTNOTES" SECTION +==================================================================== + +Python-Markdown +=============== + +Converts Markdown to HTML. Basic usage as a module: + + import markdown + html = markdown.markdown(your_text_string) + +Started by [Manfred Stienstra](http://www.dwerg.net/). Continued and +maintained by [Yuri Takhteyev](http://www.freewisdom.org). + +Project website: http://www.freewisdom.org/projects/python-markdown +Contact: yuri [at] freewisdom.org + +License: GPL 2 (http://www.gnu.org/copyleft/gpl.html) or BSD + +Version: 1.3 (Feb. 28, 2006) + +For changelog, see end of file +""" + +import re, sys, os, random + +# set debug level: 3 none, 2 critical, 1 informative, 0 all +(VERBOSE, INFO, CRITICAL, NONE) = range(4) + +MESSAGE_THRESHOLD = CRITICAL + +def message(level, text) : + if level >= MESSAGE_THRESHOLD : + print text + + +# --------------- CONSTANTS YOU MIGHT WANT TO MODIFY ----------------- + +# all tabs will be expanded to up to this many spaces +TAB_LENGTH = 4 +ENABLE_ATTRIBUTES = 1 + + +# --------------- CONSTANTS YOU _SHOULD NOT_ HAVE TO CHANGE ---------- + +FN_BACKLINK_TEXT = "zz1337820767766393qq" +# a template for html placeholders +HTML_PLACEHOLDER_PREFIX = "qaodmasdkwaspemas" +HTML_PLACEHOLDER = HTML_PLACEHOLDER_PREFIX + "%dajkqlsmdqpakldnzsdfls" + +BLOCK_LEVEL_ELEMENTS = ['p', 'div', 'blockquote', 'pre', 'table', + 'dl', 'ol', 'ul', 'script', 'noscript', + 'form', 'fieldset', 'iframe', 'math', 'ins', + 'del', 'hr', 'hr/'] + +def is_block_level (tag) : + return ( (tag in BLOCK_LEVEL_ELEMENTS) or + (tag[0] == 'h' and tag[1] in "0123456789") ) + +""" +====================================================================== +========================== NANODOM =================================== +====================================================================== + +The three classes below implement some of the most basic DOM +methods. I use this instead of minidom because I need a simpler +functionality and do not want to require additional libraries. + +Importantly, NanoDom does not do normalization, which is what we +want. It also adds extra white space when converting DOM to string +""" + + +class Document : + + def appendChild(self, child) : + self.documentElement = child + child.parent = self + self.entities = {} + + def createElement(self, tag) : + el = Element(tag) + el.doc = self + return el + + def createTextNode(self, text) : + node = TextNode(text) + node.doc = self + return node + + def createEntityReference(self, entity): + if entity not in self.entities: + self.entities[entity] = EntityReference(entity) + return self.entities[entity] + + def toxml (self) : + return self.documentElement.toxml() + + def normalizeEntities(self, text) : + + pairs = [ #("&", "&amp;"), + ("<", "&lt;"), + (">", "&gt;"), + ("\"", "&quot;")] + + for old, new in pairs : + text = text.replace(old, new) + return text + + def find(self, test) : + return self.documentElement.find(test) + + def unlink(self) : + self.documentElement.unlink() + self.documentElement = None + + +class Element : + + def __init__ (self, tag) : + self.type = "element" + self.nodeName = tag + self.attributes = [] + self.attribute_values = {} + self.childNodes = [] + + def unlink(self) : + for child in self.childNodes : + if child.type == "element" : + child.unlink() + self.childNodes = None + + def setAttribute(self, attr, value) : + if not attr in self.attributes : + self.attributes.append(attr) + + self.attribute_values[attr] = value + + def insertChild(self, position, child) : + self.childNodes.insert(position, child) + child.parent = self + + def removeChild(self, child) : + self.childNodes.remove(child) + + def replaceChild(self, oldChild, newChild) : + position = self.childNodes.index(oldChild) + self.removeChild(oldChild) + self.insertChild(position, newChild) + + def appendChild(self, child) : + self.childNodes.append(child) + child.parent = self + + def handleAttributes(self) : + pass + + def find(self, test, depth=0) : + """ Returns a list of descendants that pass the test function """ + matched_nodes = [] + for child in self.childNodes : + if test(child) : + matched_nodes.append(child) + if child.type == "element" : + matched_nodes += child.find(test, depth+1) + return matched_nodes + + def toxml(self): + if ENABLE_ATTRIBUTES : + for child in self.childNodes: + child.handleAttributes() + buffer = "" + if self.nodeName in ['h1', 'h2', 'h3', 'h4'] : + buffer += "\n" + elif self.nodeName in ['li'] : + buffer += "\n " + buffer += "<" + self.nodeName + for attr in self.attributes : + value = self.attribute_values[attr] + value = self.doc.normalizeEntities(value) + buffer += ' %s="%s"' % (attr, value) + if self.childNodes : + buffer += ">" + for child in self.childNodes : + buffer += child.toxml() + if self.nodeName == 'p' : + buffer += "\n" + elif self.nodeName == 'li' : + buffer += "\n " + buffer += "</%s>" % self.nodeName + else : + buffer += "/>" + if self.nodeName in ['p', 'li', 'ul', 'ol', + 'h1', 'h2', 'h3', 'h4'] : + buffer += "\n" + return buffer + + +class TextNode : + + def __init__ (self, text) : + self.type = "text" + self.value = text + self.attrRegExp = re.compile(r'\{@([^\}]*)=([^\}]*)}') # {@id=123} + + def attributeCallback(self, match) : + self.parent.setAttribute(match.group(1), match.group(2)) + + def handleAttributes(self) : + self.value = self.attrRegExp.sub(self.attributeCallback, self.value) + + def toxml(self) : + text = self.value + if not text.startswith(HTML_PLACEHOLDER_PREFIX): + if self.parent.nodeName == "p" : + text = text.replace("\n", "\n ") + elif (self.parent.nodeName == "li" + and self.parent.childNodes[0]==self): + text = "\n " + text.replace("\n", "\n ") + text = self.doc.normalizeEntities(text) + return text + + +class EntityReference: + + def __init__(self, entity): + self.type = "entity_ref" + self.entity = entity + + def handleAttributes(self): + pass + + def toxml(self): + return "&" + self.entity + ";" + + +""" +====================================================================== +========================== PRE-PROCESSORS ============================ +====================================================================== + +Preprocessors munge source text before we start doing anything too +complicated. + +Each preprocessor implements a "run" method that takes a pointer to +a list of lines of the document, modifies it as necessary and +returns either the same pointer or a pointer to a new list. +""" + +class HeaderPreprocessor : + + """ + Replaces underlined headers with hashed headers to avoid + the nead for lookahead later. + """ + + def run (self, lines) : + + for i in range(len(lines)) : + if not lines[i] : + continue + + if (i+1 <= len(lines) + and lines[i+1] + and lines[i+1][0] in ['-', '=']) : + + underline = lines[i+1].strip() + + if underline == "="*len(underline) : + lines[i] = "# " + lines[i].strip() + lines[i+1] = "" + elif underline == "-"*len(underline) : + lines[i] = "## " + lines[i].strip() + lines[i+1] = "" + + return lines + +HEADER_PREPROCESSOR = HeaderPreprocessor() + +class LinePreprocessor : + """Deals with HR lines (needs to be done before processing lists)""" + + def run (self, lines) : + for i in range(len(lines)) : + if self._isLine(lines[i]) : + lines[i] = "<hr />" + return lines + + def _isLine(self, block) : + """Determines if a block should be replaced with an <HR>""" + if block.startswith(" ") : return 0 # a code block + text = "".join([x for x in block if not x.isspace()]) + if len(text) <= 2 : + return 0 + for pattern in ['isline1', 'isline2', 'isline3'] : + m = RE.regExp[pattern].match(text) + if (m and m.group(1)) : + return 1 + else: + return 0 + +LINE_PREPROCESSOR = LinePreprocessor() + + +class LineBreaksPreprocessor : + """Replaces double spaces at the end of the lines with <br/ >.""" + + def run (self, lines) : + for i in range(len(lines)) : + if (lines[i].endswith(" ") + and not RE.regExp['tabbed'].match(lines[i]) ): + lines[i] += "<br />" + return lines + +LINE_BREAKS_PREPROCESSOR = LineBreaksPreprocessor() + + +class HtmlBlockPreprocessor : + """Removes html blocks from self.lines""" + + def run (self, lines) : + new_blocks = [] + text = "\n".join(lines) + for block in text.split("\n\n") : + if block.startswith("\n") : + block = block[1:] + if ( (block.startswith("<") and block.rstrip().endswith(">")) + and (block[1] in ["!", "?", "@", "%"] + or is_block_level( block[1:].replace(">", " ") + .split()[0].lower()))) : + new_blocks.append( + self.stash.store(block.strip())) + else : + new_blocks.append(block) + return "\n\n".join(new_blocks).split("\n") + +HTML_BLOCK_PREPROCESSOR = HtmlBlockPreprocessor() + + +class ReferencePreprocessor : + + def run (self, lines) : + new_text = []; + for line in lines: + m = RE.regExp['reference-def'].match(line) + if m: + id = m.group(2).strip().lower() + title = dequote(m.group(4).strip()) #.replace('"', "&quot;") + self.references[id] = (m.group(3), title) + else: + new_text.append(line) + return new_text #+ "\n" + +REFERENCE_PREPROCESSOR = ReferencePreprocessor() + +""" +====================================================================== +========================== INLINE PATTERNS =========================== +====================================================================== + +Inline patterns such as *emphasis* are handled by means of auxiliary +objects, one per pattern. Each pattern object uses a single regular +expression and needs support the following methods: + + pattern.getCompiledRegExp() - returns a regular expression + + pattern.handleMatch(m, doc) - takes a match object and returns + a NanoDom node (as a part of the provided + doc) or None + +All of python markdown's built-in patterns subclass from BasePatter, +but you can add additional patterns that don't. + +Also note that all the regular expressions used by inline must +capture the whole block. For this reason, they all start with +'^(.*)' and end with '(.*)!'. In case with built-in expression +BasePattern takes care of adding the "^(.*)" and "(.*)!". + +Finally, the order in which regular expressions are applied is very +important - e.g. if we first replace http://.../ links with <a> tags +and _then_ try to replace inline html, we would end up with a mess. +So, we apply the expressions in the following order: + + * escape and backticks have to go before everything else, so + that we can preempt any markdown patterns by escaping them. + + * then we handle auto-links (must be done before inline html) + + * then we handle inline HTML. At this point we will simply + replace all inline HTML strings with a placeholder and add + the actual HTML to a hash. + + * then inline images (must be done before links) + + * then bracketed links, first regular then reference-style + + * finally we apply strong and emphasis +""" + +NOBRACKET = r'[^\]\[]*' +BRK = ( r'\[(' + + (NOBRACKET + r'(\['+NOBRACKET)*6 + + (NOBRACKET+ r'\])*'+NOBRACKET)*6 + + NOBRACKET + r')\]' ) + +BACKTICK_RE = r'\`([^\`]*)\`' # `e= m*c^2` +DOUBLE_BACKTICK_RE = r'\`\`(.*)\`\`' # ``e=f("`")`` +ESCAPE_RE = r'\\(.)' # \< +EMPHASIS_RE = r'\*([^\*]*)\*' # *emphasis* +EMPHASIS_2_RE = r'_([^_]*)_' # _emphasis_ +LINK_RE = BRK + r'\s*\(([^\)]*)\)' # [text](url) +LINK_ANGLED_RE = BRK + r'\s*\(<([^\)]*)>\)' # [text](<url>) +IMAGE_LINK_RE = r'\!' + BRK + r'\s*\(([^\)]*)\)' #  +REFERENCE_RE = BRK+ r'\s*\[([^\]]*)\]' # [Google][3] +IMAGE_REFERENCE_RE = r'\!' + BRK + '\s*\[([^\]]*)\]' # ![alt text][2] +NOT_STRONG_RE = r'( \* )' # stand-alone * or _ +STRONG_RE = r'\*\*(.*)\*\*' # **strong** +STRONG_2_RE = r'__([^_]*)__' # __strong__ +STRONG_EM_RE = r'\*\*\*([^_]*)\*\*\*' # ***strong*** +STRONG_EM_2_RE = r'___([^_]*)___' # ___strong___ +AUTOLINK_RE = r'<(http://[^>]*)>' # <http://www.123.com> +AUTOMAIL_RE = r'<([^> ]*@[^> ]*)>' # <me...@ex...> +HTML_RE = r'(\<[^\>]*\>)' # <...> +ENTITY_RE = r'(&[\#a-zA-Z0-9]*;)' # &amp; + +class BasePattern: + + def __init__ (self, pattern) : + self.pattern = pattern + self.compiled_re = re.compile("^(.*)%s(.*)$" % pattern, re.DOTALL) + + def getCompiledRegExp (self) : + return self.compiled_re + +class SimpleTextPattern (BasePattern) : + + def handleMatch(self, m, doc) : + return doc.createTextNode(m.group(2)) + +class SimpleTagPattern (BasePattern): + + def __init__ (self, pattern, tag) : + BasePattern.__init__(self, pattern) + self.tag = tag + + def handleMatch(self, m, doc) : + el = doc.createElement(self.tag) + el.appendChild(doc.createTextNode(m.group(2))) + return el + +class BacktickPattern (BasePattern): + + def __init__ (self, pattern): + BasePattern.__init__(self, pattern) + self.tag = "code" + + def handleMatch(self, m, doc) : + el = doc.createElement(self.tag) + text = m.group(2).strip() + text = text.replace("&", "&amp;") + el.appendChild(doc.createTextNode(text)) + return el + + +class DoubleTagPattern (SimpleTagPattern) : + + def handleMatch(self, m, doc) : + tag1, tag2 = self.tag.split(",") + el1 = doc.createElement(tag1) + el2 = doc.createElement(tag2) + el1.appendChild(el2) + el2.appendChild(doc.createTextNode(m.group(2))) + return el1 + + +class HtmlPattern (BasePattern): + + def handleMatch (self, m, doc) : + place_holder = self.stash.store(m.group(2)) + return doc.createTextNode(place_holder) + + +class LinkPattern (BasePattern): + + def handleMatch(self, m, doc) : + el = doc.createElement('a') + el.appendChild(doc.createTextNode(m.group(2))) + parts = m.group(9).split() + # We should now have [], [href], or [href, title] + if parts : + el.setAttribute('href', parts[0]) + else : + el.setAttribute('href', "") + if len(parts) > 1 : + # we also got a title + title = " ".join(parts[1:]).strip() + title = dequote(title) #.replace('"', "&quot;") + el.setAttribute('title', title) + return el + + +class ImagePattern (BasePattern): + + def handleMatch(self, m, doc): + el = doc.createElement('img') + src_parts = m.group(9).split() + el.setAttribute('src', src_parts[0]) + if len(src_parts) > 1 : + el.setAttribute('title', dequote(" ".join(src_parts[1:]))) + if ENABLE_ATTRIBUTES : + text = doc.createTextNode(m.group(2)) + el.appendChild(text) + text.handleAttributes() + truealt = text.value + el.childNodes.remove(text) + else: + truealt = m.group(2) + el.setAttribute('alt', truealt) + return el + +class ReferencePattern (BasePattern): + + def handleMatch(self, m, doc): + if m.group(9) : + id = m.group(9).lower() + else : + # if we got something like "[Google][]" + # we'll use "google" as the id + id = m.group(2).lower() + if not self.references.has_key(id) : # ignore undefined refs + return None + href, title = self.references[id] + text = m.group(2) + return self.makeTag(href, title, text, doc) + + def makeTag(self, href, title, text, doc): + el = doc.createElement('a') + el.setAttribute('href', href) + if title : + el.setAttribute('title', title) + el.appendChild(doc.createTextNode(text)) + return el + + +class ImageReferencePattern (ReferencePattern): + + def makeTag(self, href, title, text, doc): + el = doc.createElement('img') + el.setAttribute('src', href) + if title : + el.setAttribute('title', title) + el.setAttribute('alt', text) + return el + + +class AutolinkPattern (BasePattern): + + def handleMatch(self, m, doc): + el = doc.createElement('a') + el.setAttribute('href', m.group(2)) + el.appendChild(doc.createTextNode(m.group(2))) + return el + +class AutomailPattern (BasePattern): + + def handleMatch(self, m, doc) : + el = doc.createElement('a') + email = m.group(2) + if email.startswith("mailto:"): + email = email[len("mailto:"):] + for letter in email: + entity = doc.createEntityReference("#%d" % ord(letter)) + el.appendChild(entity) + mailto = "mailto:" + email + mailto = "".join(['&#%d;' % ord(letter) for letter in mailto]) + el.setAttribute('href', mailto) + return el + +ESCAPE_PATTERN = SimpleTextPattern(ESCAPE_RE) +NOT_STRONG_PATTERN = SimpleTextPattern(NOT_STRONG_RE) + +BACKTICK_PATTERN = BacktickPattern(BACKTICK_RE) +DOUBLE_BACKTICK_PATTERN = BacktickPattern(DOUBLE_BACKTICK_RE) +STRONG_PATTERN = SimpleTagPattern(STRONG_RE, 'strong') +STRONG_PATTERN_2 = SimpleTagPattern(STRONG_2_RE, 'strong') +EMPHASIS_PATTERN = SimpleTagPattern(EMPHASIS_RE, 'em') +EMPHASIS_PATTERN_2 = SimpleTagPattern(EMPHASIS_2_RE, 'em') + +STRONG_EM_PATTERN = DoubleTagPattern(STRONG_EM_RE, 'strong,em') +STRONG_EM_PATTERN_2 = DoubleTagPattern(STRONG_EM_2_RE, 'strong,em') + +LINK_PATTERN = LinkPattern(LINK_RE) +LINK_ANGLED_PATTERN = LinkPattern(LINK_ANGLED_RE) +IMAGE_LINK_PATTERN = ImagePattern(IMAGE_LINK_RE) +IMAGE_REFERENCE_PATTERN = ImageReferencePattern(IMAGE_REFERENCE_RE) +REFERENCE_PATTERN = ReferencePattern(REFERENCE_RE) + +HTML_PATTERN = HtmlPattern(HTML_RE) +ENTITY_PATTERN = HtmlPattern(ENTITY_RE) + +AUTOLINK_PATTERN = AutolinkPattern(AUTOLINK_RE) +AUTOMAIL_PATTERN = AutomailPattern(AUTOMAIL_RE) + + +""" +====================================================================== +========================== POST-PROCESSORS =========================== +====================================================================== + +Markdown also allows post-processors, which are similar to +preprocessors in that they need to implement a "run" method. Unlike +pre-processors, they take a NanoDom document as a parameter and work +with that. +# +There are currently no standard post-processors, but the footnote +extension below uses one. +""" +""" +====================================================================== +========================== MISC AUXILIARY CLASSES ==================== +====================================================================== +""" + +class HtmlStash : + """This class is used for stashing HTML objects that we extract + in the beginning and replace with place-holders.""" + + def __init__ (self) : + self.html_counter = 0 # for counting inline html segments + self.rawHtmlBlocks=[] + + def store(self, html) : + """Saves an HTML segment for later reinsertion. Returns a + placeholder string that needs to be inserted into the + document. + + @param html: an html segment + @returns : a placeholder string """ + self.rawHtmlBlocks.append(html) + placeholder = HTML_PLACEHOLDER % self.html_counter + self.html_counter += 1 + return placeholder + + +class BlockGuru : + + def _findHead(self, lines, fn, allowBlank=0) : + + """Functional magic to help determine boundaries of indented + blocks. + + @param lines: an array of strings + @param fn: a function that returns a substring of a string + if the string matches the necessary criteria + @param allowBlank: specifies whether it's ok to have blank + lines between matching functions + @returns: a list of post processes items and the unused + remainder of the original list""" + + items = [] + item = -1 + + i = 0 # to keep track of where we are + + for line in lines : + + if not line.strip() and not allowBlank: + return items, lines[i:] + + if not line.strip() and allowBlank: + # If we see a blank line, this _might_ be the end + i += 1 + + # Find the next non-blank line + for j in range(i, len(lines)) : + if lines[j].strip() : + next = lines[j] + break + else : + # There is no more text => this is the end + break + + # Check if the next non-blank line is still a part of the list + + part = fn(next) + + if part : + items.append("") + continue + else : + break # found end of the list + + part = fn(line) + + if part : + items.append(part) + i += 1 + continue + else : + return items, lines[i:] + else : + i += 1 + + return items, lines[i:] + + + def detabbed_fn(self, line) : + """ An auxiliary method to be passed to _findHead """ + m = RE.regExp['tabbed'].match(line) + if m: + return m.group(4) + else : + return None + + + def detectTabbed(self, lines) : + + return self._findHead(lines, self.detabbed_fn, + allowBlank = 1) + + +def print_error(string): + """Print an error string to stderr""" + sys.stderr.write(string +'\n') + + +def dequote(string) : + """ Removes quotes from around a string """ + if ( ( string.startswith('"') and string.endswith('"')) + or (string.startswith("'") and string.endswith("'")) ) : + return string[1:-1] + else : + return string + +""" +====================================================================== +========================== CORE MARKDOWN ============================= +====================================================================== + +This stuff is ugly, so if you are thinking of extending the syntax, +see first if you can do it via pre-processors, post-processors, +inline patterns or a combination of the three. +""" + +class CorePatterns : + """This class is scheduled for removal as part of a refactoring + effort.""" + + patterns = { + 'header': r'(#*)([^#]*)(#*)', # # A title + 'reference-def' : r'(\ ?\ ?\ ?)\[([^\]]*)\]:\s*([^ ]*)(.*)', + # [Google]: http://www.google.com/ + 'containsline': r'([-]*)$|^([=]*)', # -----, =====, etc. + 'ol': r'[ ]{0,3}[\d]*\.\s+(.*)', # 1. text + 'ul': r'[ ]{0,3}[*+-]\s+(.*)', # "* text" + 'isline1': r'(\**)', # *** + 'isline2': r'(\-*)', # --- + 'isline3': r'(\_*)', # ___ + 'tabbed': r'((\t)|( ))(.*)', # an indented line + 'quoted' : r'> ?(.*)', # a quoted block ("> ...") + } + + def __init__ (self) : + + self.regExp = {} + for key in self.patterns.keys() : + self.regExp[key] = re.compile("^%s$" % self.patterns[key], + re.DOTALL) + + self.regExp['containsline'] = re.compile(r'^([-]*)$|^([=]*)$', re.M) + +RE = CorePatterns() + + +class Markdown: + """ Markdown formatter class for creating an html document from + Markdown text """ + + + def __init__(self, source=None): + """Creates a new Markdown instance. + + @param source: The text in Markdown format. """ + + self.source = source + self.blockGuru = BlockGuru() + self.registeredExtensions = [] + + self.preprocessors = [ HEADER_PREPROCESSOR, + LINE_PREPROCESSOR, + HTML_BLOCK_PREPROCESSOR, + LINE_BREAKS_PREPROCESSOR, + # A footnote preprocessor will + # get inserted here + REFERENCE_PREPROCESSOR ] + + + self.postprocessors = [] # a footnote postprocessor will get + # inserted later + + self.inlinePatterns = [ DOUBLE_BACKTICK_PATTERN, + BACKTICK_PATTERN, + ESCAPE_PATTERN, + IMAGE_LINK_PATTERN, + IMAGE_REFERENCE_PATTERN, + REFERENCE_PATTERN, + LINK_ANGLED_PATTERN, + LINK_PATTERN, + AUTOLINK_PATTERN, + AUTOMAIL_PATTERN, + HTML_PATTERN, + ENTITY_PATTERN, + NOT_STRONG_PATTERN, + STRONG_EM_PATTERN, + STRONG_EM_PATTERN_2, + STRONG_PATTERN, + STRONG_PATTERN_2, + EMPHASIS_PATTERN, + EMPHASIS_PATTERN_2 + # The order of the handlers matters!!! + ] + + self.reset() + + def registerExtension(self, extension) : + self.registeredExtensions.append(extension) + + def reset(self) : + """Resets all state variables so that we can start + with a new text.""" + self.references={} + self.htmlStash = HtmlStash() + + HTML_BLOCK_PREPROCESSOR.stash = self.htmlStash + REFERENCE_PREPROCESSOR.references = self.references + HTML_PATTERN.stash = self.htmlStash + ENTITY_PATTERN.stash = self.htmlStash + REFERENCE_PATTERN.references = self.references + IMAGE_REFERENCE_PATTERN.references = self.references + + for extension in self.registeredExtensions : + extension.reset() + + + def _transform(self): + """Transforms the Markdown text into a XHTML body document + + @returns: A NanoDom Document """ + + # Setup the document + + self.doc = Document() + self.top_element = self.doc.createElement("span") + self.top_element.appendChild(self.doc.createTextNode('\n')) + self.top_element.setAttribute('class', 'markdown') + self.doc.appendChild(self.top_element) + + # Fixup the source text + text = self.source.strip() + text = text.replace("\r\n", "\n").replace("\r", "\n") + text += "\n\n" + text = text.expandtabs(TAB_LENGTH) + + # Split into lines and run the preprocessors that will work with + # self.lines + + self.lines = text.split("\n") + + # Run the pre-processors on the lines + for prep in self.preprocessors : + self.lines = prep.run(self.lines) + + # Create a NanoDom tree from the lines and attach it to Document + self._processSection(self.top_element, self.lines) + + # Not sure why I put this in but let's leave it for now. + self.top_element.appendChild(self.doc.createTextNode('\n')) + + # Run the post-processors + for postprocessor in self.postprocessors : + postprocessor.run(self.doc) + + return self.doc + + + def _processSection(self, parent_elem, lines, + inList = 0, looseList = 0) : + + """Process a section of a source document, looking for high + level structural elements like lists, block quotes, code + segments, html blocks, etc. Some those then get stripped + of their high level markup (e.g. get unindented) and the + lower-level markup is processed recursively. + + @param parent_elem: A NanoDom element to which the content + will be added + @param lines: a list of lines + @param inList: a level + @returns: None""" + + if not lines : + return + + # Check if this section starts with a list, a blockquote or + # a code block + + processFn = { 'ul' : self._processUList, + 'ol' : self._processOList, + 'quoted' : self._processQuote, + 'tabbed' : self._processCodeBlock } + + for regexp in ['ul', 'ol', 'quoted', 'tabbed'] : + m = RE.regExp[regexp].match(lines[0]) + if m : + processFn[regexp](parent_elem, lines, inList) + return + + # We are NOT looking at one of the high-level structures like + # lists or blockquotes. So, it's just a regular paragraph + # (though perhaps nested inside a list or something else). If + # we are NOT inside a list, we just need to look for a blank + # line to find the end of the block. If we ARE inside a + # list, however, we need to consider that a sublist does not + # need to be separated by a blank line. Rather, the following + # markup is legal: + # + # * The top level list item + # + # Another paragraph of the list. This is where we are now. + # * Underneath we might have a sublist. + # + + if inList : + + start, theRest = self._linesUntil(lines, (lambda line: + RE.regExp['ul'].match(line) + or RE.regExp['ol'].match(line) + or not line.strip())) + + self._processSection(parent_elem, start, + inList - 1, looseList = looseList) + self._processSection(parent_elem, theRest, + inList - 1, looseList = looseList) + + + else : # Ok, so it's just a simple block + + paragraph, theRest = self._linesUntil(lines, lambda line: + not line.strip()) + + if len(paragraph) and paragraph[0].startswith('#') : + m = RE.regExp['header'].match(paragraph[0]) + if m : + level = len(m.group(1)) + h = self.doc.createElement("h%d" % level) + parent_elem.appendChild(h) + for item in self._handleInline(m.group(2)) : + h.appendChild(item) + else : + message(CRITICAL, "We've got a problem header!") + + elif paragraph : + + list = self._handleInline("\n".join(paragraph)) + + if ( parent_elem.nodeName == 'li' + and not (looseList or parent_elem.childNodes)): + + #and not parent_elem.childNodes) : + # If this is the first paragraph inside "li", don't + # put <p> around it - append the paragraph bits directly + # onto parent_elem + el = parent_elem + else : + # Otherwise make a "p" element + el = self.doc.createElement("p") + parent_elem.appendChild(el) + + for item in list : + el.appendChild(item) + + if theRest : + theRest = theRest[1:] # skip the first (blank) line + + self._processSection(parent_elem, theRest, inList) + + + + def _processUList(self, parent_elem, lines, inList) : + self._processList(parent_elem, lines, inList, + listexpr='ul', tag = 'ul') + + def _processOList(self, parent_elem, lines, inList) : + self._processList(parent_elem, lines, inList, + listexpr='ol', tag = 'ol') + + + def _processList(self, parent_elem, lines, inList, listexpr, tag) : + """Given a list of document lines starting with a list item, + finds the end of the list, breaks it up, and recursively + processes each list item and the remainder of the text file. + + @param parent_elem: A dom element to which the content will be added + @param lines: a list of lines + @param inList: a level + @returns: None""" + + ul = self.doc.createElement(tag) # ul might actually be '<ol>' + parent_elem.appendChild(ul) + + looseList = 0 + + # Make a list of list items + items = [] + item = -1 + + i = 0 # a counter to keep track of where we are + + for line in lines : + + loose = 0 + if not line.strip() : + # If we see a blank line, this _might_ be the end of the list + i += 1 + loose = 1 + + # Find the next non-blank line + for j in range(i, len(lines)) : + if lines[j].strip() : + next = lines[j] + break + else : + # There is no more text => end of the list + break + + # Check if the next non-blank line is still a part of the list + if ( RE.regExp[listexpr].match(next) or + RE.regExp['tabbed'].match(next) ): + # get rid of any white space in the line + items[item].append(line.strip()) + looseList = loose or looseList + continue + else : + break # found end of the list + + # Now we need to detect list items (at the current level) + # while also detabing child elements if necessary + + for expr in [listexpr, 'tabbed']: + + m = RE.regExp[expr].match(line) + if m : + if expr == listexpr : # We are looking at a new item + if m.group(1) : + items.append([m.group(1)]) + item += 1 + elif expr == 'tabbed' : # This line needs to be detabbed + items[item].append(m.group(4)) #after the 'tab' + + i += 1 + break + else : + items[item].append(line) # Just regular continuation + i += 1 # added on 2006.02.25 + else : + i += 1 + + # Add the dom elements + for item in items : + li = self.doc.createElement("li") + ul.appendChild(li) + + self._processSection(li, item, inList + 1, looseList = looseList) + + # Process the remaining part of the section + + self._processSection(parent_elem, lines[i:], inList) + + + def _linesUntil(self, lines, condition) : + """ A utility function to break a list of lines upon the + first line that satisfied a condition. The condition + argument should be a predicate function. + """ + + i = -1 + for line in lines : + i += 1 + if condition(line) : break + else : + i += 1 + return lines[:i], lines[i:] + + def _processQuote(self, parent_elem, lines, inList) : + """Given a list of document lines starting with a quote finds + the end of the quote, unindents it and recursively + processes the body of the quote and the remainder of the + text file. + + @param parent_elem: DOM element to which the content will be added + @param lines: a list of lines + @param inList: a level + @returns: None """ + + dequoted = [] + i = 0 + for line in lines : + m = RE.regExp['quoted'].match(line) + if m : + dequoted.append(m.group(1)) + i += 1 + else : + break + else : + i += 1 + + blockquote = self.doc.createElement('blockquote') + parent_elem.appendChild(blockquote) + + self._processSection(blockquote, dequoted, inList) + self._processSection(parent_elem, lines[i:], inList) + + + + + def _processCodeBlock(self, parent_elem, lines, inList) : + """Given a list of document lines starting with a code block + finds the end of the block, puts it into the dom verbatim + wrapped in ("<pre><code>") and recursively processes the + the remainder of the text file. + + @param parent_elem: DOM element to which the content will be added + @param lines: a list of lines + @param inList: a level + @returns: None""" + + detabbed, theRest = self.blockGuru.detectTabbed(lines) + + pre = self.doc.createElement('pre') + code = self.doc.createElement('code') + parent_elem.appendChild(pre) + pre.appendChild(code) + text = "\n".join(detabbed).rstrip()+"\n" + text = text.replace("&", "&amp;") + code.appendChild(self.doc.createTextNode(text)) + self._processSection(parent_elem, theRest, inList) + + + def _handleInline(self, line): + """Transform a Markdown line with inline elements to an XHTML fragment. + + Note that this function works recursively: we look for a + pattern, which usually splits the paragraph in half, and then + call this function on the two parts. + + This function uses auxiliary objects called inline patterns. + See notes on inline patterns above. + + @param item: A block of Markdown text + @return: A list of NanoDomnodes """ + if not(line): + return [self.doc.createTextNode(' ')] + # two spaces at the end of the line denote a <br/> + #if line.endswith(' '): + # list = self._handleInline( line.rstrip()) + # list.append(self.doc.createElement('br')) + # return list + # + # ::TODO:: Replace with a preprocessor + + for pattern in self.inlinePatterns : + list = self._applyPattern( line, pattern) + if list: return list + + return [self.doc.createTextNode(line)] + + def _applyPattern(self, line, pattern) : + """ Given a pattern name, this function checks if the line + fits the pattern, creates the necessary elements and + recursively calls _handleInline (via. _inlineRecurse) + + @param line: the text to be processed + @param pattern: the pattern to be checked + + @returns: the appropriate newly created NanoDom element if the + pattern matches, None otherwise. + """ + + # match the line to pattern's pre-compiled reg exp. + # if no match, move on. + + m = pattern.getCompiledRegExp().match(line) + if not m : + return None + + # if we got a match let the pattern make us a NanoDom node + # if it doesn't, move on + node = pattern.handleMatch(m, self.doc) + if not node : + return None + + # determine what we've got to the left and to the right + + left = m.group(1) # the first match group + left_list = self._handleInline(left) + right = m.groups()[-1] # the last match group + right_list = self._handleInline(right) + + # put the three parts together + left_list.append(node) + left_list.extend(right_list) + + return left_list + + + def __str__(self): + """Return the document in XHTML format. + + @returns: A serialized XHTML body.""" + #try : + doc = self._transform() + xml = doc.toxml() + #finally: + # doc.unlink() + + # Let's stick in all the raw html pieces + + for i in range(self.htmlStash.html_counter) : + xml = xml.replace("<p>%s\n</p>" % (HTML_PLACEHOLDER % i), + self.htmlStash.rawHtmlBlocks[i] + "\n") + xml = xml.replace(HTML_PLACEHOLDER % i, + self.htmlStash.rawHtmlBlocks[i]) + + xml = xml.replace(FN_BACKLINK_TEXT, "&#8617;") + + # And return everything but the top level tag + xml = xml.strip()[23:-7] + + return xml + + + toString = __str__ + + +""" +========================= FOOTNOTES ================================= + +This section adds footnote handling to markdown. It can be used as +an example for extending python-markdown with relatively complex +functionality. While in this case the extension is included inside +the module itself, it could just as easily be added from outside the +module. Not that all markdown classes above are ignorant about +footnotes. All footnote functionality is provided separately and +then added to the markdown instance at the run time. + +Footnote functionality is attached by calling extendMarkdown() +method of FootnoteExtension. The method also registers the +extension to allow it's state to be reset by a call to reset() +method. +""" + +class FootnoteExtension : + + def __init__ (self) : + self.DEF_RE = re.compile(r'(\ ?\ ?\ ?)\[\^([^\]]*)\]:\s*(.*)') + self.SHORT_USE_RE = re.compile(r'\[\^([^\]]*)\]', re.M) # [^a] + self.reset() + + def extendMarkdown(self, md) : + + self.md = md + + # Stateless extensions do not need to be registered + md.registerExtension(self) + + # Insert a preprocessor before ReferencePreprocessor + index = md.preprocessors.index(REFERENCE_PREPROCESSOR) + preprocessor = FootnotePreprocessor(self) + preprocessor.md = md + md.preprocessors.insert(index, preprocessor) + + # Insert an inline pattern before ImageReferencePattern + FOOTNOTE_RE = r'\[\^([^\]]*)\]' # blah blah [^1] blah + index = md.inlinePatterns.index(IMAGE_REFERENCE_PATTERN) + md.inlinePatterns.insert(index, FootnotePattern(FOOTNOTE_RE, self)) + + # Insert a post-processor that would actually add the footnote div + md.postprocessors.append(FootnotePostprocessor(self)) + + def reset(self) : + # May be called by Markdown is state reset is desired + + self.footnote_suffix = "-" + str(int(random.random()*1000000000)) + ... [truncated message content] |
From: <co...@sq...> - 2006-03-14 19:51:15
|
<!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>[1144] sqlalchemy/trunk: added unique_connection() method to engine, connection pool to return a connection</title> </head> <body> <div id="msg"> <dl> <dt>Revision</dt> <dd>1144</dd> <dt>Author</dt> <dd>zzzeek</dd> <dt>Date</dt> <dd>2006-03-14 13:51:03 -0600 (Tue, 14 Mar 2006)</dd> </dl> <h3>Log Message</h3> <pre>added unique_connection() method to engine, connection pool to return a connection that is not part of the thread-local context or any current transaction</pre> <h3>Modified Paths</h3> <ul> <li><a href="#sqlalchemytrunkCHANGES">sqlalchemy/trunk/CHANGES</a></li> <li><a href="#sqlalchemytrunklibsqlalchemyenginepy">sqlalchemy/trunk/lib/sqlalchemy/engine.py</a></li> <li><a href="#sqlalchemytrunklibsqlalchemypoolpy">sqlalchemy/trunk/lib/sqlalchemy/pool.py</a></li> </ul> </div> <div id="patch"> <h3>Diff</h3> <a id="sqlalchemytrunkCHANGES"></a> <div class="modfile"><h4>Modified: sqlalchemy/trunk/CHANGES (1143 => 1144)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/trunk/CHANGES 2006-03-14 16:56:05 UTC (rev 1143) +++ sqlalchemy/trunk/CHANGES 2006-03-14 19:51:03 UTC (rev 1144) </span><span class="lines">@@ -1,6 +1,8 @@ </span><span class="cx"> 0.1.5 </span><span class="cx"> - fixed attributes bug where if an object is committed, its lazy-loaded list got </span><span class="cx"> blown away if it hadnt been loaded </span><ins>+- added unique_connection() method to engine, connection pool to return a connection +that is not part of the thread-local context or any current transaction </ins><span class="cx"> </span><span class="cx"> 0.1.4 </span><span class="cx"> - create_engine() now uses genericized parameters; host/hostname, db/dbname/database, </span></span></pre></div> <a id="sqlalchemytrunklibsqlalchemyenginepy"></a> <div class="modfile"><h4>Modified: sqlalchemy/trunk/lib/sqlalchemy/engine.py (1143 => 1144)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/trunk/lib/sqlalchemy/engine.py 2006-03-14 16:56:05 UTC (rev 1143) +++ sqlalchemy/trunk/lib/sqlalchemy/engine.py 2006-03-14 19:51:03 UTC (rev 1144) </span><span class="lines">@@ -395,6 +395,10 @@ </span><span class="cx"> """returns a managed DBAPI connection from this SQLEngine's connection pool.""" </span><span class="cx"> return self._pool.connect() </span><span class="cx"> </span><ins>+ def unique_connection(self): + """returns a DBAPI connection from this SQLEngine's connection pool that is distinct from the current thread's connection.""" + return self._pool.unique_connection() + </ins><span class="cx"> def multi_transaction(self, tables, func): </span><span class="cx"> """provides a transaction boundary across tables which may be in multiple databases. </span><span class="cx"> If you have three tables, and a function that operates upon them, providing the tables as a </span></span></pre></div> <a id="sqlalchemytrunklibsqlalchemypoolpy"></a> <div class="modfile"><h4>Modified: sqlalchemy/trunk/lib/sqlalchemy/pool.py (1143 => 1144)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/trunk/lib/sqlalchemy/pool.py 2006-03-14 16:56:05 UTC (rev 1143) +++ sqlalchemy/trunk/lib/sqlalchemy/pool.py 2006-03-14 19:51:03 UTC (rev 1144) </span><span class="lines">@@ -75,7 +75,10 @@ </span><span class="cx"> self._use_threadlocal = use_threadlocal </span><span class="cx"> self._echo = echo </span><span class="cx"> self._logger = logger or util.Logger(origin='pool') </span><del>- </del><ins>+ + def unique_connection(self): + return ConnectionFairy(self) + </ins><span class="cx"> def connect(self): </span><span class="cx"> if not self._use_threadlocal: </span><span class="cx"> return ConnectionFairy(self) </span><span class="lines">@@ -120,14 +123,17 @@ </span><span class="cx"> self.logger.write(msg) </span><span class="cx"> </span><span class="cx"> class ConnectionFairy(object): </span><del>- def __init__(self, pool): </del><ins>+ def __init__(self, pool, connection=None): </ins><span class="cx"> self.pool = pool </span><del>- try: - self.connection = pool.get() - except: - self.connection = None - self.pool.return_invalid() - raise </del><ins>+ if connection is not None: + self.connection = connection + else: + try: + self.connection = pool.get() + except: + self.connection = None + self.pool.return_invalid() + raise </ins><span class="cx"> def cursor(self): </span><span class="cx"> return CursorFairy(self, self.connection.cursor()) </span><span class="cx"> def __getattr__(self, key): </span><span class="lines">@@ -157,6 +163,9 @@ </span><span class="cx"> def status(self): </span><span class="cx"> return "SingletonThreadPool size: %d" % len(self._conns) </span><span class="cx"> </span><ins>+ def unique_connection(self): + return ConnectionFairy(self, self._creator()) + </ins><span class="cx"> def do_return_conn(self, conn): </span><span class="cx"> pass </span><span class="cx"> def do_return_invalid(self): </span></span></pre> </div> </div> </body> </html> |
From: <co...@sq...> - 2006-03-14 16:56:17
|
<!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>[1143] sqlalchemy/trunk: fixed attributes bug where if an object is committed, its lazy-loaded list got </title> </head> <body> <div id="msg"> <dl> <dt>Revision</dt> <dd>1143</dd> <dt>Author</dt> <dd>zzzeek</dd> <dt>Date</dt> <dd>2006-03-14 10:56:05 -0600 (Tue, 14 Mar 2006)</dd> </dl> <h3>Log Message</h3> <pre>fixed attributes bug where if an object is committed, its lazy-loaded list got blown away if it hadnt been loaded</pre> <h3>Modified Paths</h3> <ul> <li><a href="#sqlalchemytrunkCHANGES">sqlalchemy/trunk/CHANGES</a></li> <li><a href="#sqlalchemytrunklibsqlalchemyattributespy">sqlalchemy/trunk/lib/sqlalchemy/attributes.py</a></li> <li><a href="#sqlalchemytrunktestobjectstorepy">sqlalchemy/trunk/test/objectstore.py</a></li> </ul> </div> <div id="patch"> <h3>Diff</h3> <a id="sqlalchemytrunkCHANGES"></a> <div class="modfile"><h4>Modified: sqlalchemy/trunk/CHANGES (1142 => 1143)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/trunk/CHANGES 2006-03-14 16:49:15 UTC (rev 1142) +++ sqlalchemy/trunk/CHANGES 2006-03-14 16:56:05 UTC (rev 1143) </span><span class="lines">@@ -1,3 +1,7 @@ </span><ins>+0.1.5 +- fixed attributes bug where if an object is committed, its lazy-loaded list got +blown away if it hadnt been loaded + </ins><span class="cx"> 0.1.4 </span><span class="cx"> - create_engine() now uses genericized parameters; host/hostname, db/dbname/database, </span><span class="cx"> password/passwd, etc. for all engine connections. makes engine URIs much more "universal" </span></span></pre></div> <a id="sqlalchemytrunklibsqlalchemyattributespy"></a> <div class="modfile"><h4>Modified: sqlalchemy/trunk/lib/sqlalchemy/attributes.py (1142 => 1143)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/trunk/lib/sqlalchemy/attributes.py 2006-03-14 16:49:15 UTC (rev 1142) +++ sqlalchemy/trunk/lib/sqlalchemy/attributes.py 2006-03-14 16:56:05 UTC (rev 1143) </span><span class="lines">@@ -214,7 +214,7 @@ </span><span class="cx"> value = None </span><span class="cx"> p = self.manager.create_list(self.obj, self.key, value, readonly=self.live, **self.kwargs) </span><span class="cx"> </span><del>- if not self.live: </del><ins>+ if not self.live and not passive: </ins><span class="cx"> # set the new history list as the new attribute, discards ourself </span><span class="cx"> self.manager.attribute_history(self.obj)[self.key] = p </span><span class="cx"> self.manager = None </span></span></pre></div> <a id="sqlalchemytrunktestobjectstorepy"></a> <div class="modfile"><h4>Modified: sqlalchemy/trunk/test/objectstore.py (1142 => 1143)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/trunk/test/objectstore.py 2006-03-14 16:49:15 UTC (rev 1142) +++ sqlalchemy/trunk/test/objectstore.py 2006-03-14 16:56:05 UTC (rev 1143) </span><span class="lines">@@ -386,6 +386,26 @@ </span><span class="cx"> self.assert_(u.user_id == userlist[0].user_id and userlist[0].user_name == 'modifiedname') </span><span class="cx"> self.assert_(u2.user_id == userlist[1].user_id and userlist[1].user_name == 'savetester2') </span><span class="cx"> </span><ins>+ def testlazyattrcommit(self): + """tests that when a lazy-loaded list is unloaded, and a commit occurs, that the + 'passive' call on that list does not blow away its value""" + m1 = mapper(User, users, properties = { + 'addresses': relation(mapper(Address, addresses)) + }) + + u = User() + u.addresses.append(Address()) + u.addresses.append(Address()) + u.addresses.append(Address()) + u.addresses.append(Address()) + objectstore.commit() + objectstore.clear() + ulist = m1.select() + u1 = ulist[0] + u1.user_name = 'newname' + objectstore.commit() + self.assert_(len(u1.addresses) == 4) + </ins><span class="cx"> def testinherits(self): </span><span class="cx"> m1 = mapper(User, users) </span><span class="cx"> </span></span></pre> </div> </div> </body> </html> |
From: <co...@sq...> - 2006-03-14 16:49: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>[1142] sqlalchemy/trunk/doc/build/content/datamapping.myt: formatting...</title> </head> <body> <div id="msg"> <dl> <dt>Revision</dt> <dd>1142</dd> <dt>Author</dt> <dd>zzzeek</dd> <dt>Date</dt> <dd>2006-03-14 10:49:15 -0600 (Tue, 14 Mar 2006)</dd> </dl> <h3>Log Message</h3> <pre>formatting...</pre> <h3>Modified Paths</h3> <ul> <li><a href="#sqlalchemytrunkdocbuildcontentdatamappingmyt">sqlalchemy/trunk/doc/build/content/datamapping.myt</a></li> </ul> </div> <div id="patch"> <h3>Diff</h3> <a id="sqlalchemytrunkdocbuildcontentdatamappingmyt"></a> <div class="modfile"><h4>Modified: sqlalchemy/trunk/doc/build/content/datamapping.myt (1141 => 1142)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/trunk/doc/build/content/datamapping.myt 2006-03-14 16:24:12 UTC (rev 1141) +++ sqlalchemy/trunk/doc/build/content/datamapping.myt 2006-03-14 16:49:15 UTC (rev 1142) </span><span class="lines">@@ -343,16 +343,16 @@ </span><span class="cx"> True </span><span class="cx"> </&> </span><span class="cx"> </span><del>-+<p>The backreference feature also works with many-to-many relationships, which are described later. When creating a backreference, a corresponding property is placed on the child mapper. The default arguments to this property can be overridden using the <span class="codeline">backref()</span> function: </del><ins>+<p>The backreference feature also works with many-to-many relationships, which are described later. When creating a backreference, a corresponding property is placed on the child mapper. The default arguments to this property can be overridden using the <span class="codeline">backref()</span> function: </ins><span class="cx"> <&|formatting.myt:code&> </span><span class="cx"> Address.mapper = mapper(Address, addresses) </span><span class="cx"> </span><span class="cx"> User.mapper = mapper(User, users, properties = { </span><del>- 'addresses' : relation(Address.mapper, backref=backref('user', lazy=False, private=True)) </del><ins>+ 'addresses' : relation(Address.mapper, + backref=backref('user', lazy=False, private=True)) </ins><span class="cx"> } </span><span class="cx"> ) </span><span class="cx"> </&> </span><del>-<p>Note that when overriding a backreferenced property, we re-specify the backreference as well. This will not override the existing 'addresses' property on the User class, but just sends a message to the attribute-management system that it should continue to maintain this backreference.</p> </del><span class="cx"> </&> </span><span class="cx"> <&|doclib.myt:item, name="cascade", description="Creating Relationships Automatically with cascade_mappers" &> </span><span class="cx"> <p>The mapper package has a helper function <span class="codeline">cascade_mappers()</span> which can simplify the task of linking several mappers together. Given a list of classes and/or mappers, it identifies the foreign key relationships between the given mappers or corresponding class mappers, and creates relation() objects representing those relationships, including a backreference. Attempts to find </span></span></pre> </div> </div> </body> </html> |
From: <co...@sq...> - 2006-03-14 16:24:22
|
<!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>[1141] sqlalchemy/trunk/lib/sqlalchemy/mapping/objectstore.py: removed redundant is_dirty function</title> </head> <body> <div id="msg"> <dl> <dt>Revision</dt> <dd>1141</dd> <dt>Author</dt> <dd>zzzeek</dd> <dt>Date</dt> <dd>2006-03-14 10:24:12 -0600 (Tue, 14 Mar 2006)</dd> </dl> <h3>Log Message</h3> <pre>removed redundant is_dirty function</pre> <h3>Modified Paths</h3> <ul> <li><a href="#sqlalchemytrunklibsqlalchemymappingobjectstorepy">sqlalchemy/trunk/lib/sqlalchemy/mapping/objectstore.py</a></li> </ul> </div> <div id="patch"> <h3>Diff</h3> <a id="sqlalchemytrunklibsqlalchemymappingobjectstorepy"></a> <div class="modfile"><h4>Modified: sqlalchemy/trunk/lib/sqlalchemy/mapping/objectstore.py (1140 => 1141)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/trunk/lib/sqlalchemy/mapping/objectstore.py 2006-03-13 17:39:06 UTC (rev 1140) +++ sqlalchemy/trunk/lib/sqlalchemy/mapping/objectstore.py 2006-03-14 16:24:12 UTC (rev 1141) </span><span class="lines">@@ -338,11 +338,6 @@ </span><span class="cx"> """returns True if the given key is present in this UnitOfWork's identity map.""" </span><span class="cx"> return self.identity_map.has_key(key) </span><span class="cx"> </span><del>- def is_dirty(self, obj): - """returns True if the given object is in the dirty, new, modified_lists, or deleted lists of - this UnitOfWork.""" - return obj in self.dirty or obj in self.new or obj in self.modified_lists or obj in self.deleted - </del><span class="cx"> def _remove_deleted(self, obj): </span><span class="cx"> if hasattr(obj, "_instance_key"): </span><span class="cx"> del self.identity_map[obj._instance_key] </span></span></pre> </div> </div> </body> </html> |
From: <co...@sq...> - 2006-03-13 17:39: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>[1140] sqlalchemy/tags/rel_0_1_4/: release 0.1.4</title> </head> <body> <div id="msg"> <dl> <dt>Revision</dt> <dd>1140</dd> <dt>Author</dt> <dd>zzzeek</dd> <dt>Date</dt> <dd>2006-03-13 11:39:06 -0600 (Mon, 13 Mar 2006)</dd> </dl> <h3>Log Message</h3> <pre>release 0.1.4</pre> <h3>Added Paths</h3> <ul> <li>sqlalchemy/tags/rel_0_1_4/</li> </ul> </div> <div id="patch"> <h3>Diff</h3> <a id="sqlalchemytagsrel_0_1_4fromrev1139sqlalchemytrunk"></a> <div class="copfile"><h4>Copied: sqlalchemy/tags/rel_0_1_4 (from rev 1139, sqlalchemy/trunk) ( => )</h4> <pre class="diff"><span> <span class="info"> </span></span></pre> </div> </div> </body> </html> |
From: <co...@sq...> - 2006-03-13 17:32:22
|
<!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>[1139] sqlalchemy/trunk: more notes, docs</title> </head> <body> <div id="msg"> <dl> <dt>Revision</dt> <dd>1139</dd> <dt>Author</dt> <dd>zzzeek</dd> <dt>Date</dt> <dd>2006-03-13 11:32:13 -0600 (Mon, 13 Mar 2006)</dd> </dl> <h3>Log Message</h3> <pre>more notes, docs</pre> <h3>Modified Paths</h3> <ul> <li><a href="#sqlalchemytrunkCHANGES">sqlalchemy/trunk/CHANGES</a></li> <li><a href="#sqlalchemytrunkdocbuildcontentdatamappingmyt">sqlalchemy/trunk/doc/build/content/datamapping.myt</a></li> </ul> </div> <div id="patch"> <h3>Diff</h3> <a id="sqlalchemytrunkCHANGES"></a> <div class="modfile"><h4>Modified: sqlalchemy/trunk/CHANGES (1138 => 1139)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/trunk/CHANGES 2006-03-13 17:18:03 UTC (rev 1138) +++ sqlalchemy/trunk/CHANGES 2006-03-13 17:32:13 UTC (rev 1139) </span><span class="lines">@@ -1,5 +1,6 @@ </span><span class="cx"> 0.1.4 </span><del>-- create_engine() now uses genericized parameters; host/hostname, db/dbname/database, password/passwd, etc. for all engine connections. makes engine URIs much more "universal" </del><ins>+- create_engine() now uses genericized parameters; host/hostname, db/dbname/database, +password/passwd, etc. for all engine connections. makes engine URIs much more "universal" </ins><span class="cx"> - added support for SELECT statements embedded into a column clause, using the flag </span><span class="cx"> "scalar=True" </span><span class="cx"> - another overhaul to EagerLoading when used in conjunction with mappers that </span><span class="lines">@@ -13,8 +14,8 @@ </span><span class="cx"> exception. also adds selectfirst_by (synonymous with get_by) and selectone_by </span><span class="cx"> - added onupdate parameter to Column, will exec SQL/python upon an update </span><span class="cx"> statement.Also adds "for_update=True" to all DefaultGenerator subclasses </span><del>-- added user-contributed support for Oracle table reflection; still -some bugs to work out regarding composite primary keys/dictionary selection </del><ins>+- added support for Oracle table reflection contributed by Andrija Zaric; +still some bugs to work out regarding composite primary keys/dictionary selection </ins><span class="cx"> - checked in an initial Firebird module, awaiting testing. </span><span class="cx"> - added sql.ClauseParameters dictionary object as the result for </span><span class="cx"> compiled.get_params(), does late-typeprocessing of bind parameters so </span><span class="lines">@@ -27,6 +28,19 @@ </span><span class="cx"> - added 'encoding="utf8"' parameter to engine. the given encoding will be </span><span class="cx"> used for all encode/decode calls within Unicode types as well as Strings </span><span class="cx"> when convert_unicode=True. </span><ins>+- improved support for mapping against UNIONs, added polymorph.py example +to illustrate multi-class mapping against a UNION +- fix to SQLite LIMIT/OFFSET syntax +- fix to Oracle LIMIT syntax +- added backref() function, allows backreferences to have keyword arguments +that will be passed to the backref. +- Sequences and ColumnDefault objects can do execute()/scalar() standalone +- SQL functions (i.e. func.foo()) can do execute()/scalar() standalone +- fix to SQL functions so that the ANSI-standard functions, i.e. current_timestamp +etc., do not specify parenthesis. all other functions do. +- added settattr_clean and append_clean to SmartProperty, which set +attributes without triggering a "dirty" event or any history. used as: +myclass.prop1.setattr_clean(myobject, 'hi') </ins><span class="cx"> - improved support to column defaults when used by mappers; mappers will pull </span><span class="cx"> pre-executed defaults from statement's executed bind parameters </span><span class="cx"> (pre-conversion) to populate them into a saved object's attributes; if any </span><span class="lines">@@ -37,6 +51,12 @@ </span><span class="cx"> - improvements to SQL func calls including an "engine" keyword argument so </span><span class="cx"> they can be execute()d or scalar()ed standalone, also added func accessor to </span><span class="cx"> SQLEngine </span><ins>+- fix to MySQL4 custom table engines, i.e. TYPE instead of ENGINE +- slightly enhanced logging, includes timestamps and a somewhat configurable +formatting system, in lieu of a full-blown logging system +- improvements to the ActiveMapper class from the TG gang, including +many-to-many relationships +- added Double and TinyInt support to mysql </ins><span class="cx"> </span><span class="cx"> 0.1.3 </span><span class="cx"> - completed "post_update" feature, will add a second update statement before </span></span></pre></div> <a id="sqlalchemytrunkdocbuildcontentdatamappingmyt"></a> <div class="modfile"><h4>Modified: sqlalchemy/trunk/doc/build/content/datamapping.myt (1138 => 1139)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/trunk/doc/build/content/datamapping.myt 2006-03-13 17:18:03 UTC (rev 1138) +++ sqlalchemy/trunk/doc/build/content/datamapping.myt 2006-03-13 17:32:13 UTC (rev 1139) </span><span class="lines">@@ -343,19 +343,14 @@ </span><span class="cx"> True </span><span class="cx"> </&> </span><span class="cx"> </span><del>-+<p>The backreference feature also works with many-to-many relationships, which are described later. When creating a backreference, a corresponding property is placed on the child mapper. This property can be overridden with a custom property using the <span class="codeline">add_property</span> function: </del><ins>++<p>The backreference feature also works with many-to-many relationships, which are described later. When creating a backreference, a corresponding property is placed on the child mapper. The default arguments to this property can be overridden using the <span class="codeline">backref()</span> function: </ins><span class="cx"> <&|formatting.myt:code&> </span><span class="cx"> Address.mapper = mapper(Address, addresses) </span><span class="cx"> </span><span class="cx"> User.mapper = mapper(User, users, properties = { </span><del>- 'addresses' : relation(Address.mapper, backref='user') </del><ins>+ 'addresses' : relation(Address.mapper, backref=backref('user', lazy=False, private=True)) </ins><span class="cx"> } </span><span class="cx"> ) </span><del>- - Address.mapper.add_property('user', relation( - User.mapper, lazy=False, private=True, backref='addresses' - )) - </del><span class="cx"> </&> </span><span class="cx"> <p>Note that when overriding a backreferenced property, we re-specify the backreference as well. This will not override the existing 'addresses' property on the User class, but just sends a message to the attribute-management system that it should continue to maintain this backreference.</p> </span><span class="cx"> </&> </span></span></pre> </div> </div> </body> </html> |
From: <co...@sq...> - 2006-03-13 17:18:16
|
<!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>[1138] sqlalchemy/trunk/doc/build/content/sqlconstruction.myt: tweak</title> </head> <body> <div id="msg"> <dl> <dt>Revision</dt> <dd>1138</dd> <dt>Author</dt> <dd>zzzeek</dd> <dt>Date</dt> <dd>2006-03-13 11:18:03 -0600 (Mon, 13 Mar 2006)</dd> </dl> <h3>Log Message</h3> <pre>tweak</pre> <h3>Modified Paths</h3> <ul> <li><a href="#sqlalchemytrunkdocbuildcontentsqlconstructionmyt">sqlalchemy/trunk/doc/build/content/sqlconstruction.myt</a></li> </ul> </div> <div id="patch"> <h3>Diff</h3> <a id="sqlalchemytrunkdocbuildcontentsqlconstructionmyt"></a> <div class="modfile"><h4>Modified: sqlalchemy/trunk/doc/build/content/sqlconstruction.myt (1137 => 1138)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/trunk/doc/build/content/sqlconstruction.myt 2006-03-13 17:16:52 UTC (rev 1137) +++ sqlalchemy/trunk/doc/build/content/sqlconstruction.myt 2006-03-13 17:18:03 UTC (rev 1138) </span><span class="lines">@@ -593,7 +593,7 @@ </span><span class="cx"> </span><span class="cx"> <P>The sql package supports embedding select statements into other select statements as the criterion in a WHERE condition, or as one of the "selectable" objects in the FROM list of the query. It does not at the moment directly support embedding a SELECT statement as one of the column criterion for a statement, although this can be achieved via direct text insertion, described later.</p> </span><span class="cx"> </span><del>- <&|doclib.myt:item, name="scalar", description="Scalar Column Queries"&> </del><ins>+ <&|doclib.myt:item, name="scalar", description="Scalar Column Subqueries"&> </ins><span class="cx"> <p>Subqueries can be used in the column clause of a select statement by specifying the <span class="codeline">scalar=True</span> flag:</p> </span><span class="cx"> <&|formatting.myt:code &> </span><span class="cx"> <&formatting.myt:poplink&>select([table2.c.col1, table2.c.col2, select([table1.c.col1], table1.c.col2==7, scalar=True)]) </span></span></pre> </div> </div> </body> </html> |
From: <co...@sq...> - 2006-03-13 17:17:08
|
<!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>[1137] sqlalchemy/trunk: added scalar subqueries within the column clause of another select</title> </head> <body> <div id="msg"> <dl> <dt>Revision</dt> <dd>1137</dd> <dt>Author</dt> <dd>zzzeek</dd> <dt>Date</dt> <dd>2006-03-13 11:16:52 -0600 (Mon, 13 Mar 2006)</dd> </dl> <h3>Log Message</h3> <pre>added scalar subqueries within the column clause of another select</pre> <h3>Modified Paths</h3> <ul> <li><a href="#sqlalchemytrunkCHANGES">sqlalchemy/trunk/CHANGES</a></li> <li><a href="#sqlalchemytrunkdocbuildcontentmetadatamyt">sqlalchemy/trunk/doc/build/content/metadata.myt</a></li> <li><a href="#sqlalchemytrunkdocbuildcontentsqlconstructionmyt">sqlalchemy/trunk/doc/build/content/sqlconstruction.myt</a></li> <li><a href="#sqlalchemytrunklibsqlalchemyansisqlpy">sqlalchemy/trunk/lib/sqlalchemy/ansisql.py</a></li> <li><a href="#sqlalchemytrunklibsqlalchemysqlpy">sqlalchemy/trunk/lib/sqlalchemy/sql.py</a></li> <li><a href="#sqlalchemytrunktestselectpy">sqlalchemy/trunk/test/select.py</a></li> </ul> </div> <div id="patch"> <h3>Diff</h3> <a id="sqlalchemytrunkCHANGES"></a> <div class="modfile"><h4>Modified: sqlalchemy/trunk/CHANGES (1136 => 1137)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/trunk/CHANGES 2006-03-13 07:16:53 UTC (rev 1136) +++ sqlalchemy/trunk/CHANGES 2006-03-13 17:16:52 UTC (rev 1137) </span><span class="lines">@@ -1,6 +1,7 @@ </span><span class="cx"> 0.1.4 </span><del>-- create_engine() now uses genericized parameters; host/hostname, db/dbname/database, password/passwd, etc. for all engine connections. makes -engine URIs much more "universal" </del><ins>+- create_engine() now uses genericized parameters; host/hostname, db/dbname/database, password/passwd, etc. for all engine connections. makes engine URIs much more "universal" +- added support for SELECT statements embedded into a column clause, using the flag +"scalar=True" </ins><span class="cx"> - another overhaul to EagerLoading when used in conjunction with mappers that </span><span class="cx"> inherit; improvements to eager loads figuring out their aliased queries </span><span class="cx"> correctly, also relations set up against a mapper with inherited mappers will </span></span></pre></div> <a id="sqlalchemytrunkdocbuildcontentmetadatamyt"></a> <div class="modfile"><h4>Modified: sqlalchemy/trunk/doc/build/content/metadata.myt (1136 => 1137)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/trunk/doc/build/content/metadata.myt 2006-03-13 07:16:53 UTC (rev 1136) +++ sqlalchemy/trunk/doc/build/content/metadata.myt 2006-03-13 17:16:52 UTC (rev 1137) </span><span class="lines">@@ -307,15 +307,15 @@ </span><span class="cx"> </span><span class="cx"> <&|doclib.myt:item, name="primitives", description="Non-engine primitives: TableClause/ColumnClause" &> </span><span class="cx"> </span><del>- <p>TableClause and ColumnClause are "primitive" versions of the Table and Column objects which dont use engines at all; applications that just want to generate SQL strings but not directly communicate with a database can use TableClause and ColumnClause objects, which are non-singleton and serve as the "lexical" base class of Table and Column:</p> </del><ins>+ <p>TableClause and ColumnClause are "primitive" versions of the Table and Column objects which dont use engines at all; applications that just want to generate SQL strings but not directly communicate with a database can use TableClause and ColumnClause objects (accessed via 'table' and 'column'), which are non-singleton and serve as the "lexical" base class of Table and Column:</p> </ins><span class="cx"> <&|formatting.myt:code&> </span><del>- tab1 = TableClause('table1', - ColumnClause('id'), - ColumnClause('name')) </del><ins>+ tab1 = table('table1', + column('id'), + column('name')) </ins><span class="cx"> </span><del>- tab2 = TableClause('table2', - ColumnClause('id'), - ColumnClause('email')) </del><ins>+ tab2 = table('table2', + column('id'), + column('email')) </ins><span class="cx"> </span><span class="cx"> tab1.select(tab1.c.name == 'foo') </span><span class="cx"> </&> </span></span></pre></div> <a id="sqlalchemytrunkdocbuildcontentsqlconstructionmyt"></a> <div class="modfile"><h4>Modified: sqlalchemy/trunk/doc/build/content/sqlconstruction.myt (1136 => 1137)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/trunk/doc/build/content/sqlconstruction.myt 2006-03-13 07:16:53 UTC (rev 1136) +++ sqlalchemy/trunk/doc/build/content/sqlconstruction.myt 2006-03-13 17:16:52 UTC (rev 1137) </span><span class="lines">@@ -593,9 +593,21 @@ </span><span class="cx"> </span><span class="cx"> <P>The sql package supports embedding select statements into other select statements as the criterion in a WHERE condition, or as one of the "selectable" objects in the FROM list of the query. It does not at the moment directly support embedding a SELECT statement as one of the column criterion for a statement, although this can be achieved via direct text insertion, described later.</p> </span><span class="cx"> </span><ins>+ <&|doclib.myt:item, name="scalar", description="Scalar Column Queries"&> + <p>Subqueries can be used in the column clause of a select statement by specifying the <span class="codeline">scalar=True</span> flag:</p> + <&|formatting.myt:code &> +<&formatting.myt:poplink&>select([table2.c.col1, table2.c.col2, select([table1.c.col1], table1.c.col2==7, scalar=True)]) +<&|formatting.myt:codepopper, link="sql" &> +SELECT table2.col1, table2.col2, +(SELECT table1.col1 AS col1 FROM table1 WHERE col2=:table1_col2) +FROM table2 +{'table1_col2': 7} +</&> + </&> + </&> </ins><span class="cx"> </span><span class="cx"> <&|doclib.myt:item, name="correlated", description="Correlated Subqueries" &> </span><del>- <P>When a select object is embedded inside of another select object, and both objects reference the same table, SQLAlchemy makes the assumption that the table should be correlated from the child query to the parent query. </del><ins>+ <P>When a select object is embedded inside of another select object, and both objects reference the same table, SQLAlchemy makes the assumption that the table should be correlated from the child query to the parent query. To disable this behavior, specify the flag <span class="codeline">correlate=False</span> to the Select statement.</p> </ins><span class="cx"> <&|formatting.myt:code &> </span><span class="cx"> # make an alias of a regular select. </span><span class="cx"> s = select([addresses.c.street], addresses.c.user_id==users.c.user_id).alias('s') </span></span></pre></div> <a id="sqlalchemytrunklibsqlalchemyansisqlpy"></a> <div class="modfile"><h4>Modified: sqlalchemy/trunk/lib/sqlalchemy/ansisql.py (1136 => 1137)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/trunk/lib/sqlalchemy/ansisql.py 2006-03-13 07:16:53 UTC (rev 1136) +++ sqlalchemy/trunk/lib/sqlalchemy/ansisql.py 2006-03-13 17:16:52 UTC (rev 1137) </span><span class="lines">@@ -280,7 +280,11 @@ </span><span class="cx"> </span><span class="cx"> self.select_stack.append(select) </span><span class="cx"> for c in select._raw_columns: </span><del>- if c.is_selectable(): </del><ins>+ # TODO: make this polymorphic? + if isinstance(c, sql.Select) and c._scalar: + c.accept_visitor(self) + inner_columns[self.get_str(c)] = c + elif c.is_selectable(): </ins><span class="cx"> for co in c.columns: </span><span class="cx"> if select.use_labels: </span><span class="cx"> l = co.label(co._label) </span></span></pre></div> <a id="sqlalchemytrunklibsqlalchemysqlpy"></a> <div class="modfile"><h4>Modified: sqlalchemy/trunk/lib/sqlalchemy/sql.py (1136 => 1137)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/trunk/lib/sqlalchemy/sql.py 2006-03-13 07:16:53 UTC (rev 1136) +++ sqlalchemy/trunk/lib/sqlalchemy/sql.py 2006-03-13 17:16:52 UTC (rev 1137) </span><span class="lines">@@ -55,6 +55,10 @@ </span><span class="cx"> """ </span><span class="cx"> return Select(columns, whereclause = whereclause, from_obj = from_obj, **kwargs) </span><span class="cx"> </span><ins>+def subquery(alias, *args, **kwargs): + return Select(*args, **kwargs).alias(alias) + + </ins><span class="cx"> def insert(table, values = None, **kwargs): </span><span class="cx"> """returns an INSERT clause element. </span><span class="cx"> </span><span class="lines">@@ -142,9 +146,6 @@ </span><span class="cx"> def alias(*args, **params): </span><span class="cx"> return Alias(*args, **params) </span><span class="cx"> </span><del>-def subquery(alias, *args, **params): - return Alias(Select(*args, **params), alias) - </del><span class="cx"> def literal(value, type=None): </span><span class="cx"> """returns a literal clause, bound to a bind parameter. </span><span class="cx"> </span><span class="lines">@@ -346,7 +347,8 @@ </span><span class="cx"> """base class for elements of a programmatically constructed SQL expression.""" </span><span class="cx"> def _get_from_objects(self): </span><span class="cx"> """returns objects represented in this ClauseElement that should be added to the </span><del>- FROM list of a query.""" </del><ins>+ FROM list of a query, when this ClauseElement is placed in the column clause of a Select + statement.""" </ins><span class="cx"> raise NotImplementedError(repr(self)) </span><span class="cx"> def _process_from_dict(self, data, asfrom): </span><span class="cx"> """given a dictionary attached to a Select object, places the appropriate </span><span class="lines">@@ -925,7 +927,8 @@ </span><span class="cx"> return [] </span><span class="cx"> </span><span class="cx"> def alias(self, name=None): </span><del>- return self.select(use_labels=True).alias(name) </del><ins>+ """creates a Select out of this Join clause and returns an Alias of it. The Select is not correlating.""" + return self.select(use_labels=True, correlate=False).alias(name) </ins><span class="cx"> def _process_from_dict(self, data, asfrom): </span><span class="cx"> for f in self.onclause._get_from_objects(): </span><span class="cx"> data[f.id] = f </span><span class="lines">@@ -1148,7 +1151,7 @@ </span><span class="cx"> def select(self, whereclauses = None, **params): </span><span class="cx"> return select([self], whereclauses, **params) </span><span class="cx"> def _get_from_objects(self): </span><del>- if self.is_where: </del><ins>+ if self.is_where or self._scalar: </ins><span class="cx"> return [] </span><span class="cx"> else: </span><span class="cx"> return [self] </span><span class="lines">@@ -1196,7 +1199,7 @@ </span><span class="cx"> class Select(SelectBaseMixin, FromClause): </span><span class="cx"> """represents a SELECT statement, with appendable clauses, as well as </span><span class="cx"> the ability to execute itself and return a result set.""" </span><del>- def __init__(self, columns=None, whereclause = None, from_obj = [], order_by = None, group_by=None, having=None, use_labels = False, distinct=False, engine = None, limit=None, offset=None, correlate=False): </del><ins>+ def __init__(self, columns=None, whereclause = None, from_obj = [], order_by = None, group_by=None, having=None, use_labels = False, distinct=False, engine = None, limit=None, offset=None, scalar=False, correlate=True): </ins><span class="cx"> self._froms = util.OrderedDict() </span><span class="cx"> self.use_labels = use_labels </span><span class="cx"> self.id = "Select(%d)" % id(self) </span><span class="lines">@@ -1207,14 +1210,23 @@ </span><span class="cx"> self.oid_column = None </span><span class="cx"> self.limit = limit </span><span class="cx"> self.offset = offset </span><ins>+ + # indicates that this select statement should not expand its columns + # into the column clause of an enclosing select, and should instead + # act like a single scalar column + self._scalar = scalar + + # indicates if this select statement, as a subquery, should correlate + # its FROM clause to that of an enclosing select statement </ins><span class="cx"> self.correlate = correlate </span><span class="cx"> </span><span class="cx"> # indicates if this select statement is a subquery inside another query </span><span class="cx"> self.issubquery = False </span><ins>+ </ins><span class="cx"> # indicates if this select statement is a subquery as a criterion </span><span class="cx"> # inside of a WHERE clause </span><span class="cx"> self.is_where = False </span><del>- </del><ins>+ </ins><span class="cx"> self.distinct = distinct </span><span class="cx"> self._text = None </span><span class="cx"> self._raw_columns = [] </span><span class="lines">@@ -1257,7 +1269,7 @@ </span><span class="cx"> select.is_where = self.is_where </span><span class="cx"> select.issubquery = True </span><span class="cx"> select.parens = True </span><del>- if not self.is_where and not select.correlate: </del><ins>+ if not select.correlate: </ins><span class="cx"> return </span><span class="cx"> if getattr(select, '_correlated', None) is None: </span><span class="cx"> select._correlated = self.select._froms </span><span class="lines">@@ -1268,6 +1280,11 @@ </span><span class="cx"> </span><span class="cx"> self._raw_columns.append(column) </span><span class="cx"> </span><ins>+ # if the column is a Select statement itself, + # accept visitor + column.accept_visitor(self._correlator) + + # visit the FROM objects of the column looking for more Selects </ins><span class="cx"> for f in column._get_from_objects(): </span><span class="cx"> f.accept_visitor(self._correlator) </span><span class="cx"> column._process_from_dict(self._froms, False) </span><span class="lines">@@ -1278,7 +1295,6 @@ </span><span class="cx"> return column._make_proxy(self, name=column._label) </span><span class="cx"> else: </span><span class="cx"> return column._make_proxy(self, name=column.name) </span><del>- </del><span class="cx"> def append_whereclause(self, whereclause): </span><span class="cx"> self._append_condition('whereclause', whereclause) </span><span class="cx"> def append_having(self, having): </span></span></pre></div> <a id="sqlalchemytrunktestselectpy"></a> <div class="modfile"><h4>Modified: sqlalchemy/trunk/test/select.py (1136 => 1137)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/trunk/test/select.py 2006-03-13 07:16:53 UTC (rev 1136) +++ sqlalchemy/trunk/test/select.py 2006-03-13 17:16:52 UTC (rev 1137) </span><span class="lines">@@ -75,9 +75,11 @@ </span><span class="cx"> self.runtest(select([table1, table2]), "SELECT mytable.myid, mytable.name, mytable.description, myothertable.otherid, \ </span><span class="cx"> myothertable.othername FROM mytable, myothertable") </span><span class="cx"> </span><del>- def testsubquery(self): </del><ins>+ def testselectselect(self): + """tests placing select statements in the column clause of another select, for the + purposes of selecting from the exported columns of that select.""" </ins><span class="cx"> s = select([table1], table1.c.name == 'jack') </span><del>- print [key for key in s.c.keys()] </del><ins>+ #print [key for key in s.c.keys()] </ins><span class="cx"> self.runtest( </span><span class="cx"> select( </span><span class="cx"> [s], </span><span class="lines">@@ -92,10 +94,9 @@ </span><span class="cx"> "SELECT myid, name, description FROM (SELECT mytable.myid AS myid, mytable.name AS name, mytable.description AS description FROM mytable)" </span><span class="cx"> ) </span><span class="cx"> </span><del>- sq = subquery( - 'sq', </del><ins>+ sq = select( </ins><span class="cx"> [table1], </span><del>- ) </del><ins>+ ).alias('sq') </ins><span class="cx"> </span><span class="cx"> self.runtest( </span><span class="cx"> sq.select(sq.c.myid == 7), </span><span class="lines">@@ -103,12 +104,11 @@ </span><span class="cx"> (SELECT mytable.myid AS myid, mytable.name AS name, mytable.description AS description FROM mytable) AS sq WHERE sq.myid = :sq_myid" </span><span class="cx"> ) </span><span class="cx"> </span><del>- sq = subquery( - 'sq', </del><ins>+ sq = select( </ins><span class="cx"> [table1, table2], </span><span class="cx"> and_(table1.c.myid ==7, table2.c.otherid==table1.c.myid), </span><span class="cx"> use_labels = True </span><del>- ) </del><ins>+ ).alias('sq') </ins><span class="cx"> </span><span class="cx"> sqstring = "SELECT mytable.myid AS mytable_myid, mytable.name AS mytable_name, \ </span><span class="cx"> mytable.description AS mytable_description, myothertable.otherid AS myothertable_otherid, \ </span><span class="lines">@@ -118,19 +118,75 @@ </span><span class="cx"> self.runtest(sq.select(), "SELECT sq.mytable_myid, sq.mytable_name, sq.mytable_description, sq.myothertable_otherid, \ </span><span class="cx"> sq.myothertable_othername FROM (" + sqstring + ") AS sq") </span><span class="cx"> </span><del>- sq2 = subquery( - 'sq2', </del><ins>+ sq2 = select( </ins><span class="cx"> [sq], </span><span class="cx"> use_labels = True </span><del>- ) </del><ins>+ ).alias('sq2') </ins><span class="cx"> </span><span class="cx"> self.runtest(sq2.select(), "SELECT sq2.sq_mytable_myid, sq2.sq_mytable_name, sq2.sq_mytable_description, \ </span><span class="cx"> sq2.sq_myothertable_otherid, sq2.sq_myothertable_othername FROM \ </span><span class="cx"> (SELECT sq.mytable_myid AS sq_mytable_myid, sq.mytable_name AS sq_mytable_name, \ </span><span class="cx"> sq.mytable_description AS sq_mytable_description, sq.myothertable_otherid AS sq_myothertable_otherid, \ </span><span class="cx"> sq.myothertable_othername AS sq_myothertable_othername FROM (" + sqstring + ") AS sq) AS sq2") </span><ins>+ + def testwheresubquery(self): + self.runtest( + table1.select(table1.c.myid == select([table2.c.otherid], table1.c.name == table2.c.othername)), + "SELECT mytable.myid, mytable.name, mytable.description FROM mytable WHERE mytable.myid = (SELECT myothertable.otherid AS otherid FROM myothertable WHERE mytable.name = myothertable.othername)" + ) + + self.runtest( + table1.select(exists([1], table2.c.otherid == table1.c.myid)), + "SELECT mytable.myid, mytable.name, mytable.description FROM mytable WHERE EXISTS (SELECT 1 FROM myothertable WHERE myothertable.otherid = mytable.myid)" + ) + + talias = table1.alias('ta') + s = subquery('sq2', [talias], exists([1], table2.c.otherid == talias.c.myid)) + self.runtest( + select([s, table1]) + ,"SELECT sq2.myid, sq2.name, sq2.description, mytable.myid, mytable.name, mytable.description FROM (SELECT ta.myid AS myid, ta.name AS name, ta.description AS description FROM mytable AS ta WHERE EXISTS (SELECT 1 FROM myothertable WHERE myothertable.otherid = ta.myid)) AS sq2, mytable") + + s = select([addresses.c.street], addresses.c.user_id==users.c.user_id, correlate=True).alias('s') + self.runtest( + select([users, s.c.street], from_obj=[s]), + """SELECT users.user_id, users.user_name, users.password, s.street FROM users, (SELECT addresses.street AS street FROM addresses WHERE addresses.user_id = users.user_id) AS s""") </ins><span class="cx"> </span><ins>+ def testcolumnsubquery(self): + s = select([table1.c.myid], scalar=True, correlate=False) + self.runtest(select([table1, s]), "SELECT mytable.myid, mytable.name, mytable.description, (SELECT mytable.myid AS myid FROM mytable) FROM mytable") + + s = select([table1.c.myid], scalar=True) + self.runtest(select([table2, s]), "SELECT myothertable.otherid, myothertable.othername, (SELECT mytable.myid AS myid FROM mytable) FROM myothertable") </ins><span class="cx"> </span><ins>+ + zips = table('zips', + column('zipcode'), + column('latitude'), + column('longitude'), + ) + places = table('places', + column('id'), + column('nm') + ) + zip = '12345' + qlat = select([zips.c.latitude], zips.c.zipcode == zip, scalar=True, correlate=False) + qlng = select([zips.c.longitude], zips.c.zipcode == zip, scalar=True, correlate=False) + + q = select([places.c.id, places.c.nm, zips.c.zipcode, func.latlondist(qlat, qlng).label('dist')], + zips.c.zipcode==zip, + order_by = ['dist', places.c.nm] + ) + + self.runtest(q,"SELECT places.id, places.nm, zips.zipcode, latlondist((SELECT zips.latitude AS latitude FROM zips WHERE zips.zipcode = :zips_zipcode_1), (SELECT zips.longitude AS longitude FROM zips WHERE zips.zipcode = :zips_zipcode_2)) AS dist FROM places, zips WHERE zips.zipcode = :zips_zipcode ORDER BY dist, places.nm") + + zalias = zips.alias('main_zip') + qlat = select([zips.c.latitude], zips.c.zipcode == zalias.c.zipcode, scalar=True) + qlng = select([zips.c.longitude], zips.c.zipcode == zalias.c.zipcode, scalar=True) + q = select([places.c.id, places.c.nm, zalias.c.zipcode, func.latlondist(qlat, qlng).label('dist')], + order_by = ['dist', places.c.nm] + ) + self.runtest(q, "SELECT places.id, places.nm, main_zip.zipcode, latlondist((SELECT zips.latitude AS latitude FROM zips WHERE zips.zipcode = main_zip.zipcode), (SELECT zips.longitude AS longitude FROM zips WHERE zips.zipcode = main_zip.zipcode)) AS dist FROM places, zips AS main_zip ORDER BY dist, places.nm") + </ins><span class="cx"> def testand(self): </span><span class="cx"> self.runtest( </span><span class="cx"> select(['*'], and_(table1.c.myid == 12, table1.c.name=='asdf', table2.c.othername == 'foo', "sysdate() = today()")), </span><span class="lines">@@ -410,28 +466,7 @@ </span><span class="cx"> c = s.compile(parameters = {'test' : 7}, engine=db) </span><span class="cx"> self.assert_(c.get_params() == {'test' : 7}) </span><span class="cx"> </span><del>- def testcorrelatedsubquery(self): - self.runtest( - table1.select(table1.c.myid == select([table2.c.otherid], table1.c.name == table2.c.othername)), - "SELECT mytable.myid, mytable.name, mytable.description FROM mytable WHERE mytable.myid = (SELECT myothertable.otherid AS otherid FROM myothertable WHERE mytable.name = myothertable.othername)" - ) </del><span class="cx"> </span><del>- self.runtest( - table1.select(exists([1], table2.c.otherid == table1.c.myid)), - "SELECT mytable.myid, mytable.name, mytable.description FROM mytable WHERE EXISTS (SELECT 1 FROM myothertable WHERE myothertable.otherid = mytable.myid)" - ) - - talias = table1.alias('ta') - s = subquery('sq2', [talias], exists([1], table2.c.otherid == talias.c.myid)) - self.runtest( - select([s, table1]) - ,"SELECT sq2.myid, sq2.name, sq2.description, mytable.myid, mytable.name, mytable.description FROM (SELECT ta.myid AS myid, ta.name AS name, ta.description AS description FROM mytable AS ta WHERE EXISTS (SELECT 1 FROM myothertable WHERE myothertable.otherid = ta.myid)) AS sq2, mytable") - - s = select([addresses.c.street], addresses.c.user_id==users.c.user_id, correlate=True).alias('s') - self.runtest( - select([users, s.c.street], from_obj=[s]), - """SELECT users.user_id, users.user_name, users.password, s.street FROM users, (SELECT addresses.street AS street FROM addresses WHERE addresses.user_id = users.user_id) AS s""") - </del><span class="cx"> def testin(self): </span><span class="cx"> self.runtest(select([table1], table1.c.myid.in_(1, 2, 3)), </span><span class="cx"> "SELECT mytable.myid, mytable.name, mytable.description FROM mytable WHERE mytable.myid IN (:mytable_myid, :mytable_myid_1, :mytable_myid_2)") </span></span></pre> </div> </div> </body> </html> |
From: <co...@sq...> - 2006-03-13 07:17: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>[1136] sqlalchemy/trunk/lib/sqlalchemy/mapping/mapper.py: Fix docstring and exception message in selectone_by</title> </head> <body> <div id="msg"> <dl> <dt>Revision</dt> <dd>1136</dd> <dt>Author</dt> <dd>ash</dd> <dt>Date</dt> <dd>2006-03-13 01:16:53 -0600 (Mon, 13 Mar 2006)</dd> </dl> <h3>Log Message</h3> <pre>Fix docstring and exception message in selectone_by</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 (1135 => 1136)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/trunk/lib/sqlalchemy/mapping/mapper.py 2006-03-13 07:05:00 UTC (rev 1135) +++ sqlalchemy/trunk/lib/sqlalchemy/mapping/mapper.py 2006-03-13 07:16:53 UTC (rev 1136) </span><span class="lines">@@ -399,11 +399,11 @@ </span><span class="cx"> return self.get_by(*args, **params) </span><span class="cx"> </span><span class="cx"> def selectone_by(self, *args, **params): </span><del>- """works like selectfirst(), but throws an error if not exactly one result was returned.""" </del><ins>+ """works like selectfirst_by(), but throws an error if not exactly one result was returned.""" </ins><span class="cx"> ret = self.select_by(*args, **params) </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 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></pre> </div> </div> </body> </html> |
From: <co...@sq...> - 2006-03-13 07:05: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>[1135] sqlalchemy/trunk/doc/build/content/dbengine.myt: Minor typo: encode and decode are methods.</title> </head> <body> <div id="msg"> <dl> <dt>Revision</dt> <dd>1135</dd> <dt>Author</dt> <dd>ash</dd> <dt>Date</dt> <dd>2006-03-13 01:05:00 -0600 (Mon, 13 Mar 2006)</dd> </dl> <h3>Log Message</h3> <pre>Minor typo: encode and decode are methods.</pre> <h3>Modified Paths</h3> <ul> <li><a href="#sqlalchemytrunkdocbuildcontentdbenginemyt">sqlalchemy/trunk/doc/build/content/dbengine.myt</a></li> </ul> </div> <div id="patch"> <h3>Diff</h3> <a id="sqlalchemytrunkdocbuildcontentdbenginemyt"></a> <div class="modfile"><h4>Modified: sqlalchemy/trunk/doc/build/content/dbengine.myt (1134 => 1135)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/trunk/doc/build/content/dbengine.myt 2006-03-13 06:51:47 UTC (rev 1134) +++ sqlalchemy/trunk/doc/build/content/dbengine.myt 2006-03-13 07:05:00 UTC (rev 1135) </span><span class="lines">@@ -121,7 +121,7 @@ </span><span class="cx"> <li>use_ansi=True : used only by Oracle; when False, the Oracle driver attempts to support a particular "quirk" of some Oracle databases, that the LEFT OUTER JOIN SQL syntax is not supported, and the "Oracle join" syntax of using <% "<column1>(+)=<column2>" |h%> must be used in order to achieve a LEFT OUTER JOIN. Its advised that the Oracle database be configured to have full ANSI support instead of using this feature.</li> </span><span class="cx"> <li>use_oids=False : used only by Postgres, will enable the column name "oid" as the object ID column. Postgres as of 8.1 has object IDs disabled by default.</li> </span><span class="cx"> <li>convert_unicode=False : if set to True, all String/character based types will convert Unicode values to raw byte values going into the database, and all raw byte values to Python Unicode coming out in result sets. This is an engine-wide method to provide unicode across the board. For unicode conversion on a column-by-column level, use the Unicode column type instead.</li> </span><del>- <li>encoding='utf-8' : the encoding to use for Unicode translations - passed to all encode/decode functions.</li> </del><ins>+ <li>encoding='utf-8' : the encoding to use for Unicode translations - passed to all encode/decode methods.</li> </ins><span class="cx"> <li>echo_uow=False : when True, logs unit of work commit plans to the standard output.</li> </span><span class="cx"> </ul> </span><span class="cx"> </&> </span></span></pre> </div> </div> </body> </html> |
From: <co...@sq...> - 2006-03-13 06:52:03
|
<!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>[1134] sqlalchemy/trunk/doc/build/content: Fix typos, closing #89, #91, #92</title> </head> <body> <div id="msg"> <dl> <dt>Revision</dt> <dd>1134</dd> <dt>Author</dt> <dd>ash</dd> <dt>Date</dt> <dd>2006-03-13 00:51:47 -0600 (Mon, 13 Mar 2006)</dd> </dl> <h3>Log Message</h3> <pre>Fix typos, closing #89, #91, #92</pre> <h3>Modified Paths</h3> <ul> <li><a href="#sqlalchemytrunkdocbuildcontentsqlconstructionmyt">sqlalchemy/trunk/doc/build/content/sqlconstruction.myt</a></li> <li><a href="#sqlalchemytrunkdocbuildcontentunitofworkmyt">sqlalchemy/trunk/doc/build/content/unitofwork.myt</a></li> </ul> </div> <div id="patch"> <h3>Diff</h3> <a id="sqlalchemytrunkdocbuildcontentsqlconstructionmyt"></a> <div class="modfile"><h4>Modified: sqlalchemy/trunk/doc/build/content/sqlconstruction.myt (1133 => 1134)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/trunk/doc/build/content/sqlconstruction.myt 2006-03-13 02:53:51 UTC (rev 1133) +++ sqlalchemy/trunk/doc/build/content/sqlconstruction.myt 2006-03-13 06:51:47 UTC (rev 1134) </span><span class="lines">@@ -582,7 +582,7 @@ </span><span class="cx"> s = select([users.c.user_id], users.c.user_name.like('p%')) </span><span class="cx"> </span><span class="cx"> # now select all addresses for those users </span><del>- <&formatting.myt:poplink&>addresses.select(addresses.c.address_id.in_(s)).execute() </del><ins>+ <&formatting.myt:poplink&>addresses.select(addresses.c.user_id.in_(s)).execute() </ins><span class="cx"> <&|formatting.myt:codepopper, link="sql" &> </span><span class="cx"> SELECT addresses.address_id, addresses.user_id, addresses.street, </span><span class="cx"> addresses.city, addresses.state, addresses.zip </span><span class="lines">@@ -901,9 +901,9 @@ </span><span class="cx"> # the generated SQL of the insert (i.e. what columns are present) </span><span class="cx"> # executemany() is used at the DBAPI level </span><span class="cx"> <&formatting.myt:poplink&>users.insert().execute( </span><del>- {'user_id':7, 'user_name':'jack', 'password':'asdfasdf'} - {'user_id':8, 'user_name':'ed', 'password':'asdffcadf'} - {'user_id':9, 'user_name':'fred', 'password':'asttf'} </del><ins>+ {'user_id':7, 'user_name':'jack', 'password':'asdfasdf'}, + {'user_id':8, 'user_name':'ed', 'password':'asdffcadf'}, + {'user_id':9, 'user_name':'fred', 'password':'asttf'}, </ins><span class="cx"> ) </span><span class="cx"> <&|formatting.myt:codepopper, link="sql" &> </span><span class="cx"> INSERT INTO users (user_id, user_name, password) </span></span></pre></div> <a id="sqlalchemytrunkdocbuildcontentunitofworkmyt"></a> <div class="modfile"><h4>Modified: sqlalchemy/trunk/doc/build/content/unitofwork.myt (1133 => 1134)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/trunk/doc/build/content/unitofwork.myt 2006-03-13 02:53:51 UTC (rev 1133) +++ sqlalchemy/trunk/doc/build/content/unitofwork.myt 2006-03-13 06:51:47 UTC (rev 1134) </span><span class="lines">@@ -109,7 +109,7 @@ </span><span class="cx"> </&> </span><span class="cx"> </&> </span><span class="cx"> <&|doclib.myt:item, name="identity", description="The Identity Map" &> </span><del>- <p>All object instances which are saved to the database, or loaded from the database, are given an identity by the mapper/objectstore. This identity is available via the _identity_key property attached to each object instance, and is a tuple consisting of the table's class, the SQLAlchemy-specific "hash key" of the table its persisted to, and an additional tuple of primary key values, in the order that they appear within the table definition:</p> </del><ins>+ <p>All object instances which are saved to the database, or loaded from the database, are given an identity by the mapper/objectstore. This identity is available via the _instance_key property attached to each object instance, and is a tuple consisting of the table's class, the SQLAlchemy-specific "hash key" of the table its persisted to, and an additional tuple of primary key values, in the order that they appear within the table definition:</p> </ins><span class="cx"> <&|formatting.myt:code&> </span><span class="cx"> >>> obj._instance_key </span><span class="cx"> (<class 'test.tables.User'>, "Table('users',SQLiteSQLEngine(([':memory:'], {})),schema=None)", (7,)) </span><span class="lines">@@ -155,7 +155,7 @@ </span><span class="cx"> </&> </span><span class="cx"> </&> </span><span class="cx"> <&|doclib.myt:item, name="import", description="Bringing External Instances into the UnitOfWork" &> </span><del>- <p>The _identity_key attribute is designed to work with objects that are serialized into strings and brought back again. As it contains no references to internal structures or database connections, applications that use caches or session storage which require serialization (i.e. pickling) can store SQLAlchemy-loaded objects. However, as mentioned earlier, an object with a particular database identity is only allowed to exist uniquely within the current unit-of-work scope. So, upon deserializing such an object, it has to "check in" with the current unit-of-work/identity map combination, to insure that it is the only unique instance. This is achieved via the <span class="codeline">import_instance()</span> function in objectstore:</p> </del><ins>+ <p>The _instance_key attribute is designed to work with objects that are serialized into strings and brought back again. As it contains no references to internal structures or database connections, applications that use caches or session storage which require serialization (i.e. pickling) can store SQLAlchemy-loaded objects. However, as mentioned earlier, an object with a particular database identity is only allowed to exist uniquely within the current unit-of-work scope. So, upon deserializing such an object, it has to "check in" with the current unit-of-work/identity map combination, to insure that it is the only unique instance. This is achieved via the <span class="codeline">import_instance()</span> function in objectstore:</p> </ins><span class="cx"> <&|formatting.myt:code&> </span><span class="cx"> # deserialize an object </span><span class="cx"> myobj = pickle.loads(mystring) </span><span class="lines">@@ -164,7 +164,7 @@ </span><span class="cx"> # identity map, then you get back the one from the current session. </span><span class="cx"> myobj = objectstore.import_instance(myobj) </span><span class="cx"> </&> </span><del>-<p>Note that the import_instance() function will either mark the deserialized object as the official copy in the current identity map, which includes updating its _identity_key with the current application's class instance, or it will discard it and return the corresponding object that was already present.</p> </del><ins>+<p>Note that the import_instance() function will either mark the deserialized object as the official copy in the current identity map, which includes updating its _instance_key with the current application's class instance, or it will discard it and return the corresponding object that was already present.</p> </ins><span class="cx"> </&> </span><span class="cx"> </span><span class="cx"> <&|doclib.myt:item, name="advscope", description="Advanced UnitOfWork Management"&> </span></span></pre> </div> </div> </body> </html> |
From: <co...@sq...> - 2006-03-13 02:54:04
|
<!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>[1133] sqlalchemy/trunk: added selectfirst_by/selectone_by, selectone throws exception if more than one row returned, courtesy J.Ellis</title> </head> <body> <div id="msg"> <dl> <dt>Revision</dt> <dd>1133</dd> <dt>Author</dt> <dd>zzzeek</dd> <dt>Date</dt> <dd>2006-03-12 20:53:51 -0600 (Sun, 12 Mar 2006)</dd> </dl> <h3>Log Message</h3> <pre>added selectfirst_by/selectone_by, selectone throws exception if more than one row returned, courtesy J.Ellis</pre> <h3>Modified Paths</h3> <ul> <li><a href="#sqlalchemytrunkCHANGES">sqlalchemy/trunk/CHANGES</a></li> <li><a href="#sqlalchemytrunklibsqlalchemymappingmapperpy">sqlalchemy/trunk/lib/sqlalchemy/mapping/mapper.py</a></li> </ul> </div> <div id="patch"> <h3>Diff</h3> <a id="sqlalchemytrunkCHANGES"></a> <div class="modfile"><h4>Modified: sqlalchemy/trunk/CHANGES (1132 => 1133)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/trunk/CHANGES 2006-03-13 02:39:52 UTC (rev 1132) +++ sqlalchemy/trunk/CHANGES 2006-03-13 02:53:51 UTC (rev 1133) </span><span class="lines">@@ -7,6 +7,9 @@ </span><span class="cx"> create joins against the table that is specific to the mapper itself (i.e. and </span><span class="cx"> not any tables that are inherited/are further down the inheritance chain), </span><span class="cx"> this can be overridden by using custom primary/secondary joins. </span><ins>+- added J.Ellis patch to mapper.py so that selectone() throws an exception +if query returns more than one object row, selectfirst() to not throw the +exception. also adds selectfirst_by (synonymous with get_by) and selectone_by </ins><span class="cx"> - added onupdate parameter to Column, will exec SQL/python upon an update </span><span class="cx"> statement.Also adds "for_update=True" to all DefaultGenerator subclasses </span><span class="cx"> - added user-contributed support for Oracle table reflection; still </span></span></pre></div> <a id="sqlalchemytrunklibsqlalchemymappingmapperpy"></a> <div class="modfile"><h4>Modified: sqlalchemy/trunk/lib/sqlalchemy/mapping/mapper.py (1132 => 1133)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/trunk/lib/sqlalchemy/mapping/mapper.py 2006-03-13 02:39:52 UTC (rev 1132) +++ sqlalchemy/trunk/lib/sqlalchemy/mapping/mapper.py 2006-03-13 02:53:51 UTC (rev 1133) </span><span class="lines">@@ -253,7 +253,7 @@ </span><span class="cx"> populate_existing = kwargs.get('populate_existing', False) </span><span class="cx"> </span><span class="cx"> result = util.HistoryArraySet() </span><del>- if len(mappers): </del><ins>+ if mappers: </ins><span class="cx"> otherresults = [] </span><span class="cx"> for m in mappers: </span><span class="cx"> otherresults.append(util.HistoryArraySet()) </span><span class="lines">@@ -273,10 +273,9 @@ </span><span class="cx"> for value in imap.values(): </span><span class="cx"> objectstore.get_session().register_clean(value) </span><span class="cx"> </span><del>- if len(mappers): - return [result] + otherresults - else: - return result </del><ins>+ if mappers: + result.extend(otherresults) + return result </ins><span class="cx"> </span><span class="cx"> def get(self, *ident): </span><span class="cx"> """returns an instance of the object based on the given identifier, or None </span><span class="lines">@@ -374,7 +373,7 @@ </span><span class="cx"> e.g. u = usermapper.get_by(user_name = 'fred') </span><span class="cx"> """ </span><span class="cx"> x = self.select_whereclause(self._by_clause(*args, **params), limit=1) </span><del>- if len(x): </del><ins>+ if x: </ins><span class="cx"> return x[0] </span><span class="cx"> else: </span><span class="cx"> return None </span><span class="lines">@@ -393,7 +392,19 @@ </span><span class="cx"> e.g. result = usermapper.select_by(user_name = 'fred') </span><span class="cx"> """ </span><span class="cx"> return self.select_whereclause(self._by_clause(*args, **params)) </span><ins>+ + def selectfirst_by(self, *args, **params): + """works like select_by(), but only returns the first result by itself, or None if no + objects returned. Synonymous with get_by()""" + return self.get_by(*args, **params) </ins><span class="cx"> </span><ins>+ def selectone_by(self, *args, **params): + """works like selectfirst(), but throws an error if not exactly one result was returned.""" + ret = self.select_by(*args, **params) + if len(ret) == 1: + return ret[0] + raise InvalidRequestError('Multiple rows returned for selectone') + </ins><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="cx"> The criterion is constructed in the same way as the select_by() method.""" </span><span class="lines">@@ -448,16 +459,23 @@ </span><span class="cx"> else: </span><span class="cx"> raise AttributeError(key) </span><span class="cx"> </span><del>- def selectone(self, *args, **params): </del><ins>+ def selectfirst(self, *args, **params): </ins><span class="cx"> """works like select(), but only returns the first result by itself, or None if no </span><span class="cx"> objects returned.""" </span><span class="cx"> params['limit'] = 1 </span><span class="cx"> ret = self.select(*args, **params) </span><del>- if len(ret): </del><ins>+ if ret: </ins><span class="cx"> return ret[0] </span><span class="cx"> else: </span><span class="cx"> return None </span><span class="cx"> </span><ins>+ def selectone(self, *args, **params): + """works like selectfirst(), but throws an error if not exactly one result was returned.""" + ret = self.select(*args, **params) + if len(ret) == 1: + return ret[0] + raise InvalidRequestError('Multiple rows returned for selectone') + </ins><span class="cx"> def select(self, arg = None, **kwargs): </span><span class="cx"> """selects instances of the object from the database. </span><span class="cx"> </span><span class="lines">@@ -983,12 +1001,5 @@ </span><span class="cx"> """given a class, returns the primary Mapper associated with the class.""" </span><span class="cx"> try: </span><span class="cx"> return mapper_registry[class_] </span><del>- except KeyError: - pass - except AttributeError: - pass - raise InvalidRequestError("Class '%s' has no mapper associated with it" % class_.__name__) - - - - </del><ins>+ except (KeyError, AttributeError): + raise InvalidRequestError("Class '%s' has no mapper associated with it" % class_.__name__) </ins></span></pre> </div> </div> </body> </html> |