sqlalchemy-commits Mailing List for SQLAlchemy (Page 366)
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-04-21 16:38:38
|
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head><style type="text/css"><!-- #msg dl { border: 1px #006 solid; background: #369; padding: 6px; color: #fff; } #msg dt { float: left; width: 6em; font-weight: bold; } #msg dt:after { content:':';} #msg dl, #msg dt, #msg ul, #msg li { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; } #msg dl a { font-weight: bold} #msg dl a:link { color:#fc3; } #msg dl a:active { color:#ff0; } #msg dl a:visited { color:#cc6; } h3 { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; font-weight: bold; } #msg pre { overflow: auto; background: #ffc; border: 1px #fc0 solid; padding: 6px; } #msg ul, pre { overflow: auto; } #patch { width: 100%; } #patch h4 {font-family: verdana,arial,helvetica,sans-serif;font-size:10pt;padding:8px;background:#369;color:#fff;margin:0;} #patch .propset h4, #patch .binary h4 {margin:0;} #patch pre {padding:0;line-height:1.2em;margin:0;} #patch .diff {width:100%;background:#eee;padding: 0 0 10px 0;overflow:auto;} #patch .propset .diff, #patch .binary .diff {padding:10px 0;} #patch span {display:block;padding:0 10px;} #patch .modfile, #patch .addfile, #patch .delfile, #patch .propset, #patch .binary, #patch .copfile {border:1px solid #ccc;margin:10px 0;} #patch ins {background:#dfd;text-decoration:none;display:block;padding:0 10px;} #patch del {background:#fdd;text-decoration:none;display:block;padding:0 10px;} #patch .lines, .info {color:#888;background:#fff;} --></style> <title>[1310] sqlalchemy/trunk/test/massload.py: some cleanup</title> </head> <body> <div id="msg"> <dl> <dt>Revision</dt> <dd>1310</dd> <dt>Author</dt> <dd>zzzeek</dd> <dt>Date</dt> <dd>2006-04-21 11:38:29 -0500 (Fri, 21 Apr 2006)</dd> </dl> <h3>Log Message</h3> <pre>some cleanup</pre> <h3>Modified Paths</h3> <ul> <li><a href="#sqlalchemytrunktestmassloadpy">sqlalchemy/trunk/test/massload.py</a></li> </ul> </div> <div id="patch"> <h3>Diff</h3> <a id="sqlalchemytrunktestmassloadpy"></a> <div class="modfile"><h4>Modified: sqlalchemy/trunk/test/massload.py (1309 => 1310)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/trunk/test/massload.py 2006-04-21 03:53:01 UTC (rev 1309) +++ sqlalchemy/trunk/test/massload.py 2006-04-21 16:38:29 UTC (rev 1310) </span><span class="lines">@@ -35,7 +35,7 @@ </span><span class="cx"> clear_mappers() </span><span class="cx"> for x in range(1,NUM/500+1): </span><span class="cx"> l = [] </span><del>- for y in range(x*500-500, x*500): </del><ins>+ for y in range(x*500-499, x*500 + 1): </ins><span class="cx"> l.append({'item_id':y, 'value':'this is item #%d' % y}) </span><span class="cx"> items.insert().execute(*l) </span><span class="cx"> </span><span class="lines">@@ -47,7 +47,7 @@ </span><span class="cx"> for x in range (1,NUM/100): </span><span class="cx"> # this is not needed with cpython which clears non-circular refs immediately </span><span class="cx"> #gc.collect() </span><del>- l = m.select(items.c.item_id.between(x*100 - 100, x*100 - 1)) </del><ins>+ l = m.select(items.c.item_id.between(x*100 - 99, x*100 )) </ins><span class="cx"> assert len(l) == 100 </span><span class="cx"> print "loaded ", len(l), " items " </span><span class="cx"> # modifying each object will insure that the objects get placed in the "dirty" list </span><span class="lines">@@ -59,7 +59,7 @@ </span><span class="cx"> assert len(attributes.managed_attributes) == len(l) </span><span class="cx"> print len(objectstore.get_session().dirty) </span><span class="cx"> print len(objectstore.get_session().identity_map) </span><del>- #objectstore.expunge(*l) </del><ins>+ objectstore.expunge(*l) </ins><span class="cx"> </span><span class="cx"> if __name__ == "__main__": </span><span class="cx"> testbase.main() </span></span></pre> </div> </div> </body> </html> |
From: <co...@sq...> - 2006-04-21 03:53:11
|
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head><style type="text/css"><!-- #msg dl { border: 1px #006 solid; background: #369; padding: 6px; color: #fff; } #msg dt { float: left; width: 6em; font-weight: bold; } #msg dt:after { content:':';} #msg dl, #msg dt, #msg ul, #msg li { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; } #msg dl a { font-weight: bold} #msg dl a:link { color:#fc3; } #msg dl a:active { color:#ff0; } #msg dl a:visited { color:#cc6; } h3 { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; font-weight: bold; } #msg pre { overflow: auto; background: #ffc; border: 1px #fc0 solid; padding: 6px; } #msg ul, pre { overflow: auto; } #patch { width: 100%; } #patch h4 {font-family: verdana,arial,helvetica,sans-serif;font-size:10pt;padding:8px;background:#369;color:#fff;margin:0;} #patch .propset h4, #patch .binary h4 {margin:0;} #patch pre {padding:0;line-height:1.2em;margin:0;} #patch .diff {width:100%;background:#eee;padding: 0 0 10px 0;overflow:auto;} #patch .propset .diff, #patch .binary .diff {padding:10px 0;} #patch span {display:block;padding:0 10px;} #patch .modfile, #patch .addfile, #patch .delfile, #patch .propset, #patch .binary, #patch .copfile {border:1px solid #ccc;margin:10px 0;} #patch ins {background:#dfd;text-decoration:none;display:block;padding:0 10px;} #patch del {background:#fdd;text-decoration:none;display:block;padding:0 10px;} #patch .lines, .info {color:#888;background:#fff;} --></style> <title>[1309] sqlalchemy/trunk/lib/sqlalchemy/util.py: added extend() to historyarrayset</title> </head> <body> <div id="msg"> <dl> <dt>Revision</dt> <dd>1309</dd> <dt>Author</dt> <dd>zzzeek</dd> <dt>Date</dt> <dd>2006-04-20 22:53:01 -0500 (Thu, 20 Apr 2006)</dd> </dl> <h3>Log Message</h3> <pre>added extend() to historyarrayset</pre> <h3>Modified Paths</h3> <ul> <li><a href="#sqlalchemytrunklibsqlalchemyutilpy">sqlalchemy/trunk/lib/sqlalchemy/util.py</a></li> </ul> </div> <div id="patch"> <h3>Diff</h3> <a id="sqlalchemytrunklibsqlalchemyutilpy"></a> <div class="modfile"><h4>Modified: sqlalchemy/trunk/lib/sqlalchemy/util.py (1308 => 1309)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/trunk/lib/sqlalchemy/util.py 2006-04-21 03:52:41 UTC (rev 1308) +++ sqlalchemy/trunk/lib/sqlalchemy/util.py 2006-04-21 03:53:01 UTC (rev 1309) </span><span class="lines">@@ -383,6 +383,9 @@ </span><span class="cx"> def remove(self, item): </span><span class="cx"> if self._delrecord(item): </span><span class="cx"> self.data.remove(item) </span><ins>+ def extend(self, item_list): + for item in item_list: + self.append(item) </ins><span class="cx"> def __add__(self, other): </span><span class="cx"> raise NotImplementedError() </span><span class="cx"> def __radd__(self, other): </span></span></pre> </div> </div> </body> </html> |
From: <co...@sq...> - 2006-04-21 03:52:53
|
<!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>[1308] sqlalchemy/branches/schema/lib/sqlalchemy/util.py: added extend()</title> </head> <body> <div id="msg"> <dl> <dt>Revision</dt> <dd>1308</dd> <dt>Author</dt> <dd>zzzeek</dd> <dt>Date</dt> <dd>2006-04-20 22:52:41 -0500 (Thu, 20 Apr 2006)</dd> </dl> <h3>Log Message</h3> <pre>added extend()</pre> <h3>Modified Paths</h3> <ul> <li><a href="#sqlalchemybranchesschemalibsqlalchemyutilpy">sqlalchemy/branches/schema/lib/sqlalchemy/util.py</a></li> </ul> </div> <div id="patch"> <h3>Diff</h3> <a id="sqlalchemybranchesschemalibsqlalchemyutilpy"></a> <div class="modfile"><h4>Modified: sqlalchemy/branches/schema/lib/sqlalchemy/util.py (1307 => 1308)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/schema/lib/sqlalchemy/util.py 2006-04-20 23:00:28 UTC (rev 1307) +++ sqlalchemy/branches/schema/lib/sqlalchemy/util.py 2006-04-21 03:52:41 UTC (rev 1308) </span><span class="lines">@@ -383,6 +383,9 @@ </span><span class="cx"> def remove(self, item): </span><span class="cx"> if self._delrecord(item): </span><span class="cx"> self.data.remove(item) </span><ins>+ def extend(self, item_list): + for item in item_list: + self.append(item) </ins><span class="cx"> def __add__(self, other): </span><span class="cx"> raise NotImplementedError() </span><span class="cx"> def __radd__(self, other): </span></span></pre> </div> </div> </body> </html> |
From: <co...@sq...> - 2006-04-20 23:00:41
|
<!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>[1307] sqlalchemy/branches/schema/lib/sqlalchemy: some pool/transaction mangling</title> </head> <body> <div id="msg"> <dl> <dt>Revision</dt> <dd>1307</dd> <dt>Author</dt> <dd>zzzeek</dd> <dt>Date</dt> <dd>2006-04-20 18:00:28 -0500 (Thu, 20 Apr 2006)</dd> </dl> <h3>Log Message</h3> <pre>some pool/transaction mangling</pre> <h3>Modified Paths</h3> <ul> <li><a href="#sqlalchemybranchesschemadocbuildcontenttutorialtxt">sqlalchemy/branches/schema/doc/build/content/tutorial.txt</a></li> <li><a href="#sqlalchemybranchesschemalibsqlalchemydatabasessqlitepy">sqlalchemy/branches/schema/lib/sqlalchemy/databases/sqlite.py</a></li> <li><a href="#sqlalchemybranchesschemalibsqlalchemyenginedefaultpy">sqlalchemy/branches/schema/lib/sqlalchemy/engine/default.py</a></li> <li><a href="#sqlalchemybranchesschemalibsqlalchemyenginetransactionalpy">sqlalchemy/branches/schema/lib/sqlalchemy/engine/transactional.py</a></li> <li><a href="#sqlalchemybranchesschemalibsqlalchemymappingobjectstorepy">sqlalchemy/branches/schema/lib/sqlalchemy/mapping/objectstore.py</a></li> <li><a href="#sqlalchemybranchesschemalibsqlalchemymappingunitofworkpy">sqlalchemy/branches/schema/lib/sqlalchemy/mapping/unitofwork.py</a></li> <li><a href="#sqlalchemybranchesschemalibsqlalchemypoolpy">sqlalchemy/branches/schema/lib/sqlalchemy/pool.py</a></li> </ul> </div> <div id="patch"> <h3>Diff</h3> <a id="sqlalchemybranchesschemadocbuildcontenttutorialtxt"></a> <div class="modfile"><h4>Modified: sqlalchemy/branches/schema/doc/build/content/tutorial.txt (1306 => 1307)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/schema/doc/build/content/tutorial.txt 2006-04-20 21:04:03 UTC (rev 1306) +++ sqlalchemy/branches/schema/doc/build/content/tutorial.txt 2006-04-20 23:00:28 UTC (rev 1307) </span><span class="lines">@@ -144,15 +144,15 @@ </span><span class="cx"> ['Mary', 'secure'] </span><span class="cx"> <sqlalchemy.engine.base.ResultProxy instance at 0x...> </span><span class="cx"> </span><del>- >>> i.execute({'user_name':'Tom'}, {'user_name':'Dick'}, {'user_name':'Harry'}) # doctest:+ELLIPSIS,+NORMALIZE_WHITESPACE </del><ins>+ >>> i.execute({'user_name':'Tom'}, {'user_name':'Fred'}, {'user_name':'Harry'}) # doctest:+ELLIPSIS,+NORMALIZE_WHITESPACE </ins><span class="cx"> INSERT INTO users (user_name) VALUES (?) </span><del>- [['Tom'], ['Dick'], ['Harry']] </del><ins>+ [['Tom'], ['Fred'], ['Harry']] </ins><span class="cx"> <sqlalchemy.engine.base.ResultProxy instance at 0x...> </span><span class="cx"> </span><span class="cx"> </span><span class="cx"> Note that the `VALUES` clause of each `INSERT` statement was automatically adjusted to correspond to the parameters sent to the `execute()` method. This is because the compilation step of a `ClauseElement` takes into account not just the constructed SQL object and the specifics of the type of database being used, but the execution parameters sent along as well. </span><span class="cx"> </span><del>-When constructing clause objects, SQLAlchemy will bind all literal values into bind parameters. On the construction side, bind parameters are always treated as named parameters. At compilation time, SQLAlchemy will convert them their proper format, based on the paramstyle of the underlying DBAPI. This works equally well for all named and positional bind parameter formats described in the DBAPI specification. </del><ins>+When constructing clause objects, SQLAlchemy will bind all literal values into bind parameters. On the construction side, bind parameters are always treated as named parameters. At compilation time, SQLAlchemy will convert them into their proper format, based on the paramstyle of the underlying DBAPI. This works equally well for all named and positional bind parameter formats described in the DBAPI specification. </ins><span class="cx"> </span><span class="cx"> Documentation on inserting: [sql_insert](rel:sql_insert). </span><span class="cx"> </span><span class="lines">@@ -175,15 +175,12 @@ </span><span class="cx"> {python} </span><span class="cx"> >>> r # doctest:+ELLIPSIS </span><span class="cx"> <sqlalchemy.engine.base.ResultProxy instance at 0x...> </span><del>- >>> r.keys - ['user_id', 'user_name', 'password'] - >>> row = r.fetchone() - >>> row['user_name'] - u'Mary' </del><ins>+ >>> r.fetchone() + (1, u'Mary', u'secure') </ins><span class="cx"> >>> r.fetchall() </span><del>- [(2, u'Tom', None), (3, u'Dick', None), (4, u'Harry', None)] </del><ins>+ [(2, u'Tom', None), (3, u'Fred', None), (4, u'Harry', None)] </ins><span class="cx"> </span><del>-Query criterion for the select can also be specified as regular Python expressions, using the column objects in the Table as a base: </del><ins>+Query criterion for the select can also be specified as regular Python expressions, using the `Column` objects in the `Table` as a base. All expressions constructed from `Column` objects are themselves instances of `ClauseElements`, just like the `Select`, `Insert`, and `Table` objects themselves. </ins><span class="cx"> </span><span class="cx"> {python} </span><span class="cx"> >>> r = users.select(users.c.user_name=='Harry').execute() </span><span class="lines">@@ -202,11 +199,26 @@ </span><span class="cx"> You can see that when we print out the rows returned by an execution result, it prints the rows as tuples. But in fact these rows are special, and can be used either with a list interface or a dictionary interface. The dictionary interface allows the addressing of columns by string column name, or even the original `Column` object: </span><span class="cx"> </span><span class="cx"> {python} </span><ins>+ >>> row.keys() + ['user_id', 'user_name', 'password'] </ins><span class="cx"> >>> row['user_id'], row[1], row[users.c.password] </span><span class="cx"> (4, u'Harry', None) </span><span class="cx"> </span><span class="cx"> Addressing the columns in a row based on the original `Column` object is especially handy, as it eliminates the need to work with literal column names altogether. </span><span class="cx"> </span><ins>+Result sets also support the regular Python iterator interface. We'll show this with a slightly different form of `select` that allows you to specify the specific columns to be selected: + + {python} + >>> for row in select([users.c.user_id, users.c.user_name]).execute(): # doctest:+NORMALIZE_WHITESPACE + ... print row + SELECT users.user_id, users.user_name + FROM users + [] + (1, u'Mary') + (2, u'Tom') + (3, u'Fred') + (4, u'Harry') + </ins><span class="cx"> ### Table Relationships </span><span class="cx"> </span><span class="cx"> Lets create a second table, `email_addresses`, which references the `users` table. To define the relationship between the two tables, we will use the `ForeignKey` construct. We will also issue the `CREATE` statement for the table in one step: </span><span class="lines">@@ -223,8 +235,10 @@ </span><span class="cx"> ) </span><span class="cx"> ... </span><span class="cx"> </span><del>-Then lets put a few rows in: </del><ins>+Above, the `email_addresses` table is related to the `users` table via the `ForeignKey('users.user_id')`. The `ForeignKey` constructor can take a `Column` object or a string representing the table and column name. When using the string argument, the referenced table must exist within the same `MetaData` object; thats where it looks for the other table! </ins><span class="cx"> </span><ins>+Next, lets put a few rows in: + </ins><span class="cx"> {python} </span><span class="cx"> >>> email_addresses.insert().execute({'email_address':'to...@to...', 'user_id':2},{'email_address':'ma...@ma...', 'user_id':1}) #doctest:+ELLIPSIS </span><span class="cx"> INSERT INTO email_addresses (email_address, user_id) VALUES (?, ?) </span><span class="lines">@@ -241,16 +255,16 @@ </span><span class="cx"> >>> print [row for row in r] </span><span class="cx"> [(1, u'Mary', u'secure', 2, u'ma...@ma...', 1), (2, u'Tom', None, 1, u'to...@to...', 2)] </span><span class="cx"> </span><del>-The `join` method is also a standalone function in the `sqlalchemy` namespace. The join condition is figured out from the foreign keys of the Table objects given. They can also be specified explicitly: </del><ins>+The `join` method is also a standalone function in the `sqlalchemy` namespace. The join condition is figured out from the foreign keys of the Table objects given. The condition (also called the "ON clause") can be specified explicitly, such as in this example where we locate all users that used their email address as their password: </ins><span class="cx"> </span><span class="cx"> {python} </span><del>- >>> print join(users, email_addresses, users.c.user_id==email_addresses.c.user_id) - users JOIN email_addresses ON users.user_id = email_addresses.user_id </del><ins>+ >>> print join(users, email_addresses, and_(users.c.user_id==email_addresses.c.user_id, users.c.password==email_addresses.c.email_address)) + users JOIN email_addresses ON users.user_id = email_addresses.user_id AND users.password = email_addresses.email_address </ins><span class="cx"> </span><span class="cx"> Working with Object Mappers {@name=orm} </span><span class="cx"> ----------------------------------------------- </span><span class="cx"> </span><del>-Now that we have a little bit of Table and SQL operations covered, lets look into SQLAlchemy's ORM (object relational mapper). With the ORM, you associate Tables (and other *Selectable* units, like queries and table aliases) with Python classes, into units called *Mappers*. Then you can execute queries that return lists of object instances, instead of result sets. The object instances themselves are associated with an object called a *Session*, which automatically tracks changes on each object and supports a "save all at once" operation called a *flush*. </del><ins>+Now that we have a little bit of Table and SQL operations covered, lets look into SQLAlchemy's ORM (object relational mapper). With the ORM, you associate Tables (and other *Selectable* units, like queries and table aliases) with Python classes, into units called *Mappers*. Then you can execute queries that return lists of object instances, instead of result sets. The object instances themselves are associated with an object called a *Session*, which automatically tracks changes on each object and supports a "save all at once" operation called a *flush*. </ins><span class="cx"> </span><span class="cx"> ### Creating a Mapper {@name=mapper} </span><span class="cx"> </span><span class="lines">@@ -261,7 +275,7 @@ </span><span class="cx"> ... def __repr__(self): </span><span class="cx"> ... return "(User %s,password:%s)" % (self.user_name, self.password) </span><span class="cx"> </span><del>-The class is a new style class (i.e. it extends `object`) and does not require a constructor (although one may be provided if desired). We just have one `__repr__` method on it which will display basic information about the User. Note that the `__repr__` method references the instance variables `user_name` and `password` which otherwise aren't defined. While we are free to explicitly define these attributes and treat them normally, this is optional; as SQLAlchemy's `Mapper` construct will manage them for us, as their names correspond to the names of columns in the `users` table. Lets create a mapper, and observe that these attributes are now defined: </del><ins>+The class is a new style class (i.e. it extends `object`) and does not require a constructor (although one may be provided if desired). We just have one `__repr__` method on it which will display basic information about the User. Note that the `__repr__` method references the instance variables `user_name` and `password` which otherwise aren't defined. While we are free to explicitly define these attributes and treat them normally, this is optional; as SQLAlchemy's `Mapper` construct will manage them for us, since their names correspond to the names of columns in the `users` table. Lets create a mapper, and observe that these attributes are now defined: </ins><span class="cx"> </span><span class="cx"> {python} </span><span class="cx"> >>> usermapper = mapper(User, users) </span><span class="lines">@@ -283,13 +297,13 @@ </span><span class="cx"> FROM users ORDER BY users.oid </span><span class="cx"> [] </span><span class="cx"> >>> l </span><del>- [(User Mary,password:secure), (User Tom,password:None), (User Dick,password:None), (User Harry,password:None)] </del><ins>+ [(User Mary,password:secure), (User Tom,password:None), (User Fred,password:None), (User Harry,password:None)] </ins><span class="cx"> </span><span class="cx"> ### Obtaining a Session {@name=session} </span><span class="cx"> </span><span class="cx"> After you create a Mapper, all operations with that Mapper require the usage of an important object called a `Session`. All objects loaded or saved by the Mapper must be *bound* to a `Session` object, which represents a kind of "workspace" of objects that are loaded into memory. A particular object instance can only be bound to one `Session` at a time. </span><span class="cx"> </span><del>-By default, you have to create a `Session` object explicitly before you can use a `Mapper`, and when loading objects you need to specify the `Session` that will be used to keep track of those objects. But recall that we imported a special *mod* called `threadlocal`, which has made life easier for us by creating a `Session` that is automatically associated with the current thread. Because of that, the `Mapper` was able to use the `Session` that was already associated with the current thread, without us needing to say anything. But now, lets get a handle to that `Session` and deal with it directly. To locate the `Session` corresponding to the current thread, just use `get_session()': </del><ins>+By default, you have to create a `Session` object explicitly before you can use a `Mapper`, and when loading objects you need to specify the `Session` that will be used to keep track of those objects. But recall that we imported a special *mod* called `threadlocal`, which has made life easier for us by creating a `Session` that is automatically associated with the current thread. Because of that, the `Mapper` was able to use the `Session` that was already associated with the current thread, without us needing to say anything. But now, lets get a handle to that `Session` and deal with it directly. To locate the `Session` corresponding to the current thread, just use `get_session()`: </ins><span class="cx"> </span><span class="cx"> {python} </span><span class="cx"> >>> session = get_session() </span><span class="lines">@@ -316,7 +330,7 @@ </span><span class="cx"> {python} </span><span class="cx"> >>> metadata.engine.echo = False </span><span class="cx"> >>> print query.select(User.c.user_id==3) </span><del>- [(User Dick,password:None)] </del><ins>+ [(User Fred,password:None)] </ins><span class="cx"> >>> print query.get(2) </span><span class="cx"> (User Tom,password:None) </span><span class="cx"> >>> print query.get_by(user_name='Mary') </span><span class="lines">@@ -328,6 +342,63 @@ </span><span class="cx"> </span><span class="cx"> Notice that our `User` class has a special attribute `c` attached to it. This 'c' represents the columns on the User's mapper's Table object. Saying `User.c.user_name` is synonymous with saying `users.c.user_name`, recalling that `User` is the Python class and `users` is our `Table` object. </span><span class="cx"> </span><ins>+### Making Changes {@name=changes} + +With a little experience in loading objects, lets see what its like to make changes. First, lets create a new user "Ed". We do this by just constructing the new object. The `Mapper` for the `User` class will be called when we create the object, which will then automatically add it to the current thread's Session, if one is available: + + {python} + >>> ed = User() + >>> ed.user_name = 'ed' + >>> ed.password = 'edspassword' + >>> ed in session + True + +Lets also make a few changes on some of the objects in the database. We will load them with our `Query` object, and then change some things. + + {python} + >>> mary = query.get_by(user_name='Mary') + >>> harry = query.get_by(user_name='Harry') + >>> mary.password = 'marysnewpassword' + >>> harry.password = 'harrysnewpassword' + +At the moment, nothing has been saved to the database; all of our changes are in memory only. What happens if some other part of the application also tries to load 'Mary' from the database and make some changes before we had a chance to save it ? Assuming that the same `Session` is used, loading 'Mary' from the database a second time will issue a second query in order locate the primary key of 'Mary', but will *return the same object instance as the one already loaded*. This behavior is due to an important property of the `Session` known as the **identity map**: + + {python} + >>> mary2 = query.get_by(user_name='Mary') + >>> mary is mary2 + True + +With the identity map, a single `Session` can be relied upon to keep all loaded instances straight, and when a thread-local Session is used, it becomes pretty hard for an application to lose track of the changes made on objects. + +As far as the issue of the same object being modified in two different Sessions, that's an issue of concurrency detection; SQLAlchemy does some basic concurrency checks when saving objects, with the option for a stronger check using version ids. See [adv_datamapping](rel:adv_datamapping) for more details. + +### Saving {@name=saving} + +With a new user "ed" and some changes made on "Mary" and "Harry", lets also mark "Fred" as deleted: + + {python} + >>> fred = query.get_by(user_name='Fred') + >>> session.delete(fred) + +Then to send all of our changes to the database, we `flush()` the Session. Lets turn echo back on to see this happen!: + + {python} + >>> metadata.engine.echo = True + >>> session.flush() + BEGIN + UPDATE users SET password=? WHERE users.user_id = ? + ['marysnewpassword', 1] + UPDATE users SET password=? WHERE users.user_id = ? + ['harrysnewpassword', 4] + INSERT INTO users (user_name, password) VALUES (?, ?) + [None, None] + INSERT INTO users (user_name, password) VALUES (?, ?) + ['ed', 'edspassword'] + DELETE FROM users WHERE users.user_id = ? + [[3]] + COMMIT + + </ins><span class="cx"> ### Transactions </span><span class="cx"> </span><span class="cx"> Main documentation: [unitofwork](rel:unitofwork), [dbengine_transactions](rel:dbengine_transactions). </span></span></pre></div> <a id="sqlalchemybranchesschemalibsqlalchemydatabasessqlitepy"></a> <div class="modfile"><h4>Modified: sqlalchemy/branches/schema/lib/sqlalchemy/databases/sqlite.py (1306 => 1307)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/schema/lib/sqlalchemy/databases/sqlite.py 2006-04-20 21:04:03 UTC (rev 1306) +++ sqlalchemy/branches/schema/lib/sqlalchemy/databases/sqlite.py 2006-04-20 23:00:28 UTC (rev 1307) </span><span class="lines">@@ -227,8 +227,6 @@ </span><span class="cx"> else: </span><span class="cx"> return ansisql.ANSICompiler.binary_operator_string(self, binary) </span><span class="cx"> </span><del>- - </del><span class="cx"> class SQLiteSchemaGenerator(ansisql.ANSISchemaGenerator): </span><span class="cx"> def get_column_specification(self, column, override_pk=False, **kwargs): </span><span class="cx"> colspec = column.name + " " + column.type.engine_impl(self.engine).get_col_spec() </span></span></pre></div> <a id="sqlalchemybranchesschemalibsqlalchemyenginedefaultpy"></a> <div class="modfile"><h4>Modified: sqlalchemy/branches/schema/lib/sqlalchemy/engine/default.py (1306 => 1307)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/schema/lib/sqlalchemy/engine/default.py 2006-04-20 21:04:03 UTC (rev 1306) +++ sqlalchemy/branches/schema/lib/sqlalchemy/engine/default.py 2006-04-20 23:00:28 UTC (rev 1307) </span><span class="lines">@@ -19,10 +19,13 @@ </span><span class="cx"> if pool is None: </span><span class="cx"> kwargs.setdefault('echo', False) </span><span class="cx"> kwargs.setdefault('use_threadlocal',True) </span><del>- if poolclass is not None: - kwargs['poolclass'] = poolclass - self._dbproxy = sqlalchemy.pool.manage(dialect.dbapi(), **kwargs) - self._pool = self._dbproxy.get_pool(*cargs, **cparams) </del><ins>+ if poolclass is None: + poolclass = sqlalchemy.pool.QueuePool + dbapi = dialect.dbapi() + if dbapi is None: + raise exceptions.InvalidRequestException("Cant get DBAPI module for dialect '%s'" % dialect) + + self._pool = poolclass(lambda: dbapi.connect(*cargs, **cparams), **kwargs) </ins><span class="cx"> else: </span><span class="cx"> if isinstance(pool, sqlalchemy.pool.DBProxy): </span><span class="cx"> self._pool = pool.get_pool(*cargs, **cparams) </span></span></pre></div> <a id="sqlalchemybranchesschemalibsqlalchemyenginetransactionalpy"></a> <div class="modfile"><h4>Modified: sqlalchemy/branches/schema/lib/sqlalchemy/engine/transactional.py (1306 => 1307)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/schema/lib/sqlalchemy/engine/transactional.py 2006-04-20 21:04:03 UTC (rev 1306) +++ sqlalchemy/branches/schema/lib/sqlalchemy/engine/transactional.py 2006-04-20 23:00:28 UTC (rev 1307) </span><span class="lines">@@ -11,7 +11,10 @@ </span><span class="cx"> try: </span><span class="cx"> base.Transaction.rollback(self) </span><span class="cx"> finally: </span><del>- del self.connection.engine.context.transaction </del><ins>+ try: + del self.connection.engine.context.transaction + except AttributeError: + pass </ins><span class="cx"> def commit(self): </span><span class="cx"> try: </span><span class="cx"> base.Transaction.commit(self) </span><span class="lines">@@ -20,7 +23,10 @@ </span><span class="cx"> if len(stack) == 0: </span><span class="cx"> del self.connection.engine.context.transaction </span><span class="cx"> except: </span><del>- del self.connection.engine.context.transaction </del><ins>+ try: + del self.connection.engine.context.transaction + except AttributeError: + pass </ins><span class="cx"> raise </span><span class="cx"> </span><span class="cx"> class TLConnection(base.Connection): </span></span></pre></div> <a id="sqlalchemybranchesschemalibsqlalchemymappingobjectstorepy"></a> <div class="modfile"><h4>Modified: sqlalchemy/branches/schema/lib/sqlalchemy/mapping/objectstore.py (1306 => 1307)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/schema/lib/sqlalchemy/mapping/objectstore.py 2006-04-20 21:04:03 UTC (rev 1306) +++ sqlalchemy/branches/schema/lib/sqlalchemy/mapping/objectstore.py 2006-04-20 23:00:28 UTC (rev 1307) </span><span class="lines">@@ -13,10 +13,11 @@ </span><span class="cx"> </span><span class="cx"> </span><span class="cx"> class SessionTransaction(object): </span><del>- def __init__(self, session, parent=None): </del><ins>+ def __init__(self, session, parent=None, autoflush=True): </ins><span class="cx"> self.session = session </span><span class="cx"> self.connections = {} </span><span class="cx"> self.parent = parent </span><ins>+ self.autoflush = autoflush </ins><span class="cx"> def connection(self, mapper): </span><span class="cx"> if self.parent is not None: </span><span class="cx"> return self.parent.connection(mapper) </span><span class="lines">@@ -32,6 +33,8 @@ </span><span class="cx"> def commit(self): </span><span class="cx"> if self.parent is not None: </span><span class="cx"> return </span><ins>+ if self.autoflush: + self.session.flush() </ins><span class="cx"> for t in self.connections.values(): </span><span class="cx"> t[1].commit() </span><span class="cx"> self.close() </span><span class="lines">@@ -39,7 +42,7 @@ </span><span class="cx"> if self.parent is not None: </span><span class="cx"> self.parent.rollback() </span><span class="cx"> return </span><del>- for t in self.connections.values(): </del><ins>+ for k, t in self.connections.iteritems(): </ins><span class="cx"> t[1].rollback() </span><span class="cx"> self.close() </span><span class="cx"> def close(self): </span><span class="lines">@@ -67,14 +70,14 @@ </span><span class="cx"> self.hash_key = hash_key </span><span class="cx"> _sessions[self.hash_key] = self </span><span class="cx"> </span><del>- def create_transaction(self): </del><ins>+ def create_transaction(self, **kwargs): </ins><span class="cx"> """returns a new SessionTransaction corresponding to an existing or new transaction. </span><span class="cx"> if the transaction is new, the returned SessionTransaction will have commit control </span><span class="cx"> over the underlying transaction, else will have rollback control only.""" </span><span class="cx"> if self.transaction is not None: </span><span class="cx"> return self.transaction._begin() </span><span class="cx"> else: </span><del>- self.transaction = SessionTransaction(self) </del><ins>+ self.transaction = SessionTransaction(self, **kwargs) </ins><span class="cx"> return self.transaction </span><span class="cx"> def connect(self, mapper=None, **kwargs): </span><span class="cx"> """returns a unique connection corresponding to the given mapper. this connection </span><span class="lines">@@ -254,7 +257,9 @@ </span><span class="cx"> obj._sa_session_id = self.hash_key </span><span class="cx"> def _is_bound(self, obj): </span><span class="cx"> return getattr(obj, '_sa_session_id', None) == self.hash_key </span><del>- </del><ins>+ def __contains__(self, obj): + return self._is_bound(obj) and (obj in self.uow.new or self.uow.has_key(obj._instance_key)) + </ins><span class="cx"> def _get(self, key): </span><span class="cx"> return self.uow._get(key) </span><span class="cx"> def has_key(self, key): </span></span></pre></div> <a id="sqlalchemybranchesschemalibsqlalchemymappingunitofworkpy"></a> <div class="modfile"><h4>Modified: sqlalchemy/branches/schema/lib/sqlalchemy/mapping/unitofwork.py (1306 => 1307)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/schema/lib/sqlalchemy/mapping/unitofwork.py 2006-04-20 21:04:03 UTC (rev 1306) +++ sqlalchemy/branches/schema/lib/sqlalchemy/mapping/unitofwork.py 2006-04-20 23:00:28 UTC (rev 1307) </span><span class="lines">@@ -245,7 +245,7 @@ </span><span class="cx"> continue </span><span class="cx"> flush_context.register_object(obj, isdelete=True) </span><span class="cx"> </span><del>- trans = session.create_transaction() </del><ins>+ trans = session.create_transaction(autoflush=False) </ins><span class="cx"> flush_context.transaction = trans </span><span class="cx"> echo_commit = False </span><span class="cx"> try: </span></span></pre></div> <a id="sqlalchemybranchesschemalibsqlalchemypoolpy"></a> <div class="modfile"><h4>Modified: sqlalchemy/branches/schema/lib/sqlalchemy/pool.py (1306 => 1307)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/schema/lib/sqlalchemy/pool.py 2006-04-20 21:04:03 UTC (rev 1306) +++ sqlalchemy/branches/schema/lib/sqlalchemy/pool.py 2006-04-20 23:00:28 UTC (rev 1307) </span><span class="lines">@@ -73,7 +73,7 @@ </span><span class="cx"> def __init__(self, echo = False, use_threadlocal = True, logger=None, **kwargs): </span><span class="cx"> self._threadconns = {} #weakref.WeakValueDictionary() </span><span class="cx"> self._use_threadlocal = use_threadlocal </span><del>- self._echo = echo </del><ins>+ self.echo = echo </ins><span class="cx"> self._logger = logger or util.Logger(origin='pool') </span><span class="cx"> </span><span class="cx"> def unique_connection(self): </span><span class="lines">@@ -91,8 +91,6 @@ </span><span class="cx"> return agent </span><span class="cx"> </span><span class="cx"> def return_conn(self, agent): </span><del>- if self._echo: - self.log("return connection to pool") </del><span class="cx"> if self._use_threadlocal: </span><span class="cx"> try: </span><span class="cx"> del self._threadconns[thread.get_ident()] </span><span class="lines">@@ -101,15 +99,9 @@ </span><span class="cx"> self.do_return_conn(agent.connection) </span><span class="cx"> </span><span class="cx"> def get(self): </span><del>- if self._echo: - self.log("get connection from pool") - self.log(self.status()) </del><span class="cx"> return self.do_get() </span><span class="cx"> </span><span class="cx"> def return_invalid(self): </span><del>- if self._echo: - self.log("return invalid connection to pool") - self.log(self.status()) </del><span class="cx"> self.do_return_invalid() </span><span class="cx"> </span><span class="cx"> def do_get(self): </span><span class="lines">@@ -140,7 +132,11 @@ </span><span class="cx"> self.connection = None </span><span class="cx"> self.pool.return_invalid() </span><span class="cx"> raise </span><ins>+ if self.pool.echo: + self.pool.log("Connection %s checked out from pool" % repr(self.connection)) </ins><span class="cx"> def invalidate(self): </span><ins>+ if self.pool.echo: + self.pool.log("Invalidate connection %s" % repr(self.connection)) </ins><span class="cx"> self.connection = None </span><span class="cx"> self.pool.return_invalid() </span><span class="cx"> def cursor(self): </span><span class="lines">@@ -160,6 +156,8 @@ </span><span class="cx"> self._close() </span><span class="cx"> def _close(self): </span><span class="cx"> if self.connection is not None: </span><ins>+ if self.pool.echo: + self.pool.log("Connection %s being returned to pool" % repr(self.connection)) </ins><span class="cx"> self.pool.return_conn(self) </span><span class="cx"> self.pool = None </span><span class="cx"> self.connection = None </span><span class="lines">@@ -173,19 +171,18 @@ </span><span class="cx"> </span><span class="cx"> class SingletonThreadPool(Pool): </span><span class="cx"> """Maintains one connection per each thread, never moving to another thread. this is </span><del>- used for SQLite and other databases with a similar restriction.""" </del><ins>+ used for SQLite.""" </ins><span class="cx"> def __init__(self, creator, **params): </span><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 thread:%d size: %d" % (thread.get_ident(), len(self._conns)) </del><ins>+ return "SingletonThreadPool id:%d thread:%d size: %d" % (id(self), thread.get_ident(), len(self._conns)) </ins><span class="cx"> </span><span class="cx"> def do_return_conn(self, conn): </span><del>- if self._conns.get(thread.get_ident(), None) is None: - self._conns[thread.get_ident()] = conn - </del><ins>+ pass + </ins><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">@@ -194,13 +191,11 @@ </span><span class="cx"> </span><span class="cx"> def do_get(self): </span><span class="cx"> try: </span><del>- c = self._conns[thread.get_ident()] - if c is None: - return self._creator() </del><ins>+ return self._conns[thread.get_ident()] </ins><span class="cx"> except KeyError: </span><span class="cx"> c = self._creator() </span><del>- self._conns[thread.get_ident()] = None - return c </del><ins>+ self._conns[thread.get_ident()] = c + 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 class="lines">@@ -212,23 +207,16 @@ </span><span class="cx"> self._max_overflow = max_overflow </span><span class="cx"> </span><span class="cx"> def do_return_conn(self, conn): </span><del>- if self._echo: - self.log("return QP connection to pool") </del><span class="cx"> try: </span><span class="cx"> self._pool.put(conn, False) </span><span class="cx"> except Queue.Full: </span><span class="cx"> self._overflow -= 1 </span><span class="cx"> </span><span class="cx"> def do_return_invalid(self): </span><del>- if self._echo: - self.log("return invalid connection") </del><span class="cx"> if self._pool.full(): </span><span class="cx"> self._overflow -= 1 </span><span class="cx"> </span><span class="cx"> def do_get(self): </span><del>- if self._echo: - self.log("get QP connection from pool") - self.log(self.status()) </del><span class="cx"> try: </span><span class="cx"> return self._pool.get(self._max_overflow > -1 and self._overflow >= self._max_overflow) </span><span class="cx"> except Queue.Empty: </span></span></pre> </div> </div> </body> </html> |
From: <co...@sq...> - 2006-04-20 21:04:13
|
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head><style type="text/css"><!-- #msg dl { border: 1px #006 solid; background: #369; padding: 6px; color: #fff; } #msg dt { float: left; width: 6em; font-weight: bold; } #msg dt:after { content:':';} #msg dl, #msg dt, #msg ul, #msg li { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; } #msg dl a { font-weight: bold} #msg dl a:link { color:#fc3; } #msg dl a:active { color:#ff0; } #msg dl a:visited { color:#cc6; } h3 { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; font-weight: bold; } #msg pre { overflow: auto; background: #ffc; border: 1px #fc0 solid; padding: 6px; } #msg ul, pre { overflow: auto; } #patch { width: 100%; } #patch h4 {font-family: verdana,arial,helvetica,sans-serif;font-size:10pt;padding:8px;background:#369;color:#fff;margin:0;} #patch .propset h4, #patch .binary h4 {margin:0;} #patch pre {padding:0;line-height:1.2em;margin:0;} #patch .diff {width:100%;background:#eee;padding: 0 0 10px 0;overflow:auto;} #patch .propset .diff, #patch .binary .diff {padding:10px 0;} #patch span {display:block;padding:0 10px;} #patch .modfile, #patch .addfile, #patch .delfile, #patch .propset, #patch .binary, #patch .copfile {border:1px solid #ccc;margin:10px 0;} #patch ins {background:#dfd;text-decoration:none;display:block;padding:0 10px;} #patch del {background:#fdd;text-decoration:none;display:block;padding:0 10px;} #patch .lines, .info {color:#888;background:#fff;} --></style> <title>[1306] sqlalchemy/trunk/lib/sqlalchemy/ext/activemapper.py: Updates to ActiveMapper contributed by Gabriel Jacobo.</title> </head> <body> <div id="msg"> <dl> <dt>Revision</dt> <dd>1306</dd> <dt>Author</dt> <dd>cleverdevil</dd> <dt>Date</dt> <dd>2006-04-20 16:04:03 -0500 (Thu, 20 Apr 2006)</dd> </dl> <h3>Log Message</h3> <pre>Updates to ActiveMapper contributed by Gabriel Jacobo. The main purpose of the changes is to ensure that relationships are properly detected and built in the proper order. This should fix some problems that people were having with ActiveMapper requiring classes to be declared in a specific order.</pre> <h3>Modified Paths</h3> <ul> <li><a href="#sqlalchemytrunklibsqlalchemyextactivemapperpy">sqlalchemy/trunk/lib/sqlalchemy/ext/activemapper.py</a></li> </ul> </div> <div id="patch"> <h3>Diff</h3> <a id="sqlalchemytrunklibsqlalchemyextactivemapperpy"></a> <div class="modfile"><h4>Modified: sqlalchemy/trunk/lib/sqlalchemy/ext/activemapper.py (1305 => 1306)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/trunk/lib/sqlalchemy/ext/activemapper.py 2006-04-20 20:38:02 UTC (rev 1305) +++ sqlalchemy/trunk/lib/sqlalchemy/ext/activemapper.py 2006-04-20 21:04:03 UTC (rev 1306) </span><span class="lines">@@ -65,15 +65,44 @@ </span><span class="cx"> # </span><span class="cx"> </span><span class="cx"> __deferred_classes__ = [] </span><del>-def process_relationships(klass, was_deferred=False): </del><ins>+__processed_classes__ = [] + +def check_relationships(klass): + #Check the class for foreign_keys recursively. If some foreign table is not found, the processing of the table + #must be defered. + for keyname in klass.table._foreign_keys: + xtable = keyname._colspec[:keyname._colspec.find('.')] + tablefound = False + for xclass in ActiveMapperMeta.classes: + if ActiveMapperMeta.classes[xclass].table.from_name == xtable: + tablefound = True + break + if tablefound==False: + #The refered table has not yet been created. + return False + + return True + + +def process_relationships(klass): </ins><span class="cx"> defer = False </span><span class="cx"> for propname, reldesc in klass.relations.items(): </span><del>- if not reldesc.classname in ActiveMapperMeta.classes: - if not was_deferred: __deferred_classes__.append(klass) </del><ins>+ #We require that every related table has been processed first + if not reldesc.classname in __processed_classes__: + if not klass._classname in __deferred_classes__: __deferred_classes__.append(klass._classname) </ins><span class="cx"> defer = True </span><del>- </del><ins>+ + + #Check every column item to see if it points to an existing table + #if it does not, defer... </ins><span class="cx"> if not defer: </span><ins>+ if not check_relationships(klass): + if not klass._classname in __deferred_classes__: __deferred_classes__.append(klass._classname) + defer = True + + if not defer: </ins><span class="cx"> relations = {} </span><ins>+ __processed_classes__.append(klass._classname) </ins><span class="cx"> for propname, reldesc in klass.relations.items(): </span><span class="cx"> relclass = ActiveMapperMeta.classes[reldesc.classname] </span><span class="cx"> relations[propname] = relation(relclass.mapper, </span><span class="lines">@@ -82,38 +111,39 @@ </span><span class="cx"> private=reldesc.private, </span><span class="cx"> lazy=reldesc.lazy, </span><span class="cx"> uselist=reldesc.uselist) </span><del>- assign_mapper(klass, klass.table, properties=relations, - inherits=getattr(klass, "_base_mapper", None)) - if was_deferred: __deferred_classes__.remove(klass) - - if not was_deferred: </del><ins>+ if len(relations)>0: + assign_mapper(klass, klass.table, properties=relations) + + if klass._classname in __deferred_classes__: __deferred_classes__.remove(klass._classname) + </ins><span class="cx"> for deferred_class in __deferred_classes__: </span><del>- process_relationships(deferred_class, was_deferred=True) </del><ins>+ process_relationships(ActiveMapperMeta.classes[deferred_class]) </ins><span class="cx"> </span><span class="cx"> </span><span class="cx"> </span><ins>+ </ins><span class="cx"> class ActiveMapperMeta(type): </span><span class="cx"> classes = {} </span><del>- </del><ins>+ </ins><span class="cx"> def __init__(cls, clsname, bases, dict): </span><span class="cx"> table_name = clsname.lower() </span><span class="cx"> columns = [] </span><span class="cx"> relations = {} </span><span class="cx"> _engine = getattr( sys.modules[cls.__module__], "__engine__", engine ) </span><del>- </del><ins>+ </ins><span class="cx"> if 'mapping' in dict: </span><span class="cx"> members = inspect.getmembers(dict.get('mapping')) </span><span class="cx"> for name, value in members: </span><span class="cx"> if name == '__table__': </span><span class="cx"> table_name = value </span><span class="cx"> continue </span><del>- </del><ins>+ </ins><span class="cx"> if '__engine__' == name: </span><span class="cx"> _engine= value </span><span class="cx"> continue </span><del>- </del><ins>+ </ins><span class="cx"> if name.startswith('__'): continue </span><del>- </del><ins>+ </ins><span class="cx"> if isinstance(value, column): </span><span class="cx"> if value.foreign_key: </span><span class="cx"> col = Column(value.colname or name, </span><span class="lines">@@ -128,7 +158,7 @@ </span><span class="cx"> *value.args, **value.kwargs) </span><span class="cx"> columns.append(col) </span><span class="cx"> continue </span><del>- </del><ins>+ </ins><span class="cx"> if isinstance(value, relationship): </span><span class="cx"> relations[name] = value </span><span class="cx"> assert _engine is not None, "No engine specified" </span><span class="lines">@@ -140,16 +170,16 @@ </span><span class="cx"> else: </span><span class="cx"> assign_mapper(cls, cls.table) </span><span class="cx"> cls.relations = relations </span><ins>+ cls._classname = clsname </ins><span class="cx"> ActiveMapperMeta.classes[clsname] = cls </span><del>- </del><span class="cx"> process_relationships(cls) </span><del>- </del><ins>+ </ins><span class="cx"> super(ActiveMapperMeta, cls).__init__(clsname, bases, dict) </span><span class="cx"> </span><span class="cx"> </span><span class="cx"> class ActiveMapper(object): </span><span class="cx"> __metaclass__ = ActiveMapperMeta </span><del>- </del><ins>+ </ins><span class="cx"> def set(self, **kwargs): </span><span class="cx"> for key, value in kwargs.items(): </span><span class="cx"> setattr(self, key, value) </span><span class="lines">@@ -161,5 +191,4 @@ </span><span class="cx"> </span><span class="cx"> def create_tables(): </span><span class="cx"> for klass in ActiveMapperMeta.classes.values(): </span><del>- klass.table.create() - </del><ins>+ klass.table.create() </ins><span class="cx">\ No newline at end of file </span></span></pre> </div> </div> </body> </html> |
From: <co...@sq...> - 2006-04-20 20:38:23
|
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head><style type="text/css"><!-- #msg dl { border: 1px #006 solid; background: #369; padding: 6px; color: #fff; } #msg dt { float: left; width: 6em; font-weight: bold; } #msg dt:after { content:':';} #msg dl, #msg dt, #msg ul, #msg li { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; } #msg dl a { font-weight: bold} #msg dl a:link { color:#fc3; } #msg dl a:active { color:#ff0; } #msg dl a:visited { color:#cc6; } h3 { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; font-weight: bold; } #msg pre { overflow: auto; background: #ffc; border: 1px #fc0 solid; padding: 6px; } #msg ul, pre { overflow: auto; } #patch { width: 100%; } #patch h4 {font-family: verdana,arial,helvetica,sans-serif;font-size:10pt;padding:8px;background:#369;color:#fff;margin:0;} #patch .propset h4, #patch .binary h4 {margin:0;} #patch pre {padding:0;line-height:1.2em;margin:0;} #patch .diff {width:100%;background:#eee;padding: 0 0 10px 0;overflow:auto;} #patch .propset .diff, #patch .binary .diff {padding:10px 0;} #patch span {display:block;padding:0 10px;} #patch .modfile, #patch .addfile, #patch .delfile, #patch .propset, #patch .binary, #patch .copfile {border:1px solid #ccc;margin:10px 0;} #patch ins {background:#dfd;text-decoration:none;display:block;padding:0 10px;} #patch del {background:#fdd;text-decoration:none;display:block;padding:0 10px;} #patch .lines, .info {color:#888;background:#fff;} --></style> <title>[1305] sqlalchemy/branches/schema/doc: progress on the tutorial...cleanup of session interface</title> </head> <body> <div id="msg"> <dl> <dt>Revision</dt> <dd>1305</dd> <dt>Author</dt> <dd>zzzeek</dd> <dt>Date</dt> <dd>2006-04-20 15:38:02 -0500 (Thu, 20 Apr 2006)</dd> </dl> <h3>Log Message</h3> <pre>progress on the tutorial...cleanup of session interface</pre> <h3>Modified Paths</h3> <ul> <li><a href="#sqlalchemybranchesschemadocbuildcomponentsformattingmyt">sqlalchemy/branches/schema/doc/build/components/formatting.myt</a></li> <li><a href="#sqlalchemybranchesschemadocbuildcontentdatamappingtxt">sqlalchemy/branches/schema/doc/build/content/datamapping.txt</a></li> <li><a href="#sqlalchemybranchesschemadocbuildcontentsqlconstructiontxt">sqlalchemy/branches/schema/doc/build/content/sqlconstruction.txt</a></li> <li><a href="#sqlalchemybranchesschemadocbuildcontenttutorialtxt">sqlalchemy/branches/schema/doc/build/content/tutorial.txt</a></li> <li><a href="#sqlalchemybranchesschemadocbuildcontentunitofworktxt">sqlalchemy/branches/schema/doc/build/content/unitofwork.txt</a></li> <li><a href="#sqlalchemybranchesschemadocbuildtxt2mytpy">sqlalchemy/branches/schema/doc/build/txt2myt.py</a></li> <li><a href="#sqlalchemybranchesschemadocdocscss">sqlalchemy/branches/schema/doc/docs.css</a></li> <li><a href="#sqlalchemybranchesschemalibsqlalchemy__init__py">sqlalchemy/branches/schema/lib/sqlalchemy/__init__.py</a></li> <li><a href="#sqlalchemybranchesschemalibsqlalchemymappingobjectstorepy">sqlalchemy/branches/schema/lib/sqlalchemy/mapping/objectstore.py</a></li> <li><a href="#sqlalchemybranchesschemalibsqlalchemymodsthreadlocalpy">sqlalchemy/branches/schema/lib/sqlalchemy/mods/threadlocal.py</a></li> </ul> </div> <div id="patch"> <h3>Diff</h3> <a id="sqlalchemybranchesschemadocbuildcomponentsformattingmyt"></a> <div class="modfile"><h4>Modified: sqlalchemy/branches/schema/doc/build/components/formatting.myt (1304 => 1305)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/schema/doc/build/components/formatting.myt 2006-04-20 18:04:16 UTC (rev 1304) +++ sqlalchemy/branches/schema/doc/build/components/formatting.myt 2006-04-20 20:38:02 UTC (rev 1305) </span><span class="lines">@@ -261,6 +261,7 @@ </span><span class="cx"> title = None </span><span class="cx"> syntaxtype = 'python' </span><span class="cx"> html_escape = False </span><ins>+ use_sliders = False </ins><span class="cx"> </%args> </span><span class="cx"> </span><span class="cx"> <%init> </span><span class="lines">@@ -289,7 +290,7 @@ </span><span class="cx"> return "<pre>" + highlight.highlight(fix_indent(match.group(1)), html_escape = html_escape, syntaxtype = syntaxtype) + "</pre>" </span><span class="cx"> content = p.sub(hlight, "<pre>" + m.content() + "</pre>") </span><span class="cx"> </%init> </span><del>-<div class="code"> </del><ins>+<div class="<% use_sliders and "sliding_code" or "code" %>"> </ins><span class="cx"> % if title is not None: </span><span class="cx"> <div class="codetitle"><% title %></div> </span><span class="cx"> % </span></span></pre></div> <a id="sqlalchemybranchesschemadocbuildcontentdatamappingtxt"></a> <div class="modfile"><h4>Modified: sqlalchemy/branches/schema/doc/build/content/datamapping.txt (1304 => 1305)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/schema/doc/build/content/datamapping.txt 2006-04-20 18:04:16 UTC (rev 1304) +++ sqlalchemy/branches/schema/doc/build/content/datamapping.txt 2006-04-20 20:38:02 UTC (rev 1305) </span><span class="lines">@@ -1,4 +1,4 @@ </span><del>-Data Mapping </del><ins>+Data Mapping {@name=datamapping} </ins><span class="cx"> ============ </span><span class="cx"> </span><span class="cx"> ### Basic Data Mapping {@name=datamapping} </span></span></pre></div> <a id="sqlalchemybranchesschemadocbuildcontentsqlconstructiontxt"></a> <div class="modfile"><h4>Modified: sqlalchemy/branches/schema/doc/build/content/sqlconstruction.txt (1304 => 1305)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/schema/doc/build/content/sqlconstruction.txt 2006-04-20 18:04:16 UTC (rev 1304) +++ sqlalchemy/branches/schema/doc/build/content/sqlconstruction.txt 2006-04-20 20:38:02 UTC (rev 1305) </span><span class="lines">@@ -1,4 +1,4 @@ </span><del>-Constructing SQL Queries via Python Expressions {@name=sqlconstruction} </del><ins>+Constructing SQL Queries via Python Expressions {@name=sql} </ins><span class="cx"> =============================================== </span><span class="cx"> </span><span class="cx"> *Note:* This section describes how to use SQLAlchemy to construct SQL queries and receive result sets. It does *not* cover the object relational mapping capabilities of SQLAlchemy; that is covered later on in [datamapping](rel:datamapping). However, both areas of functionality work similarly in how selection criterion is constructed, so if you are interested just in ORM, you should probably skim through basic [sql_select_whereclause](rel:sql_select_whereclause) construction before moving on. </span></span></pre></div> <a id="sqlalchemybranchesschemadocbuildcontenttutorialtxt"></a> <div class="modfile"><h4>Modified: sqlalchemy/branches/schema/doc/build/content/tutorial.txt (1304 => 1305)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/schema/doc/build/content/tutorial.txt 2006-04-20 18:04:16 UTC (rev 1304) +++ sqlalchemy/branches/schema/doc/build/content/tutorial.txt 2006-04-20 20:38:02 UTC (rev 1305) </span><span class="lines">@@ -19,6 +19,10 @@ </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><ins>+Otherwise, you can install from the distribution using the `setup.py` script: + + $ python setup.py install + </ins><span class="cx"> ### Installing a Database API {@name=dbms} </span><span class="cx"> </span><span class="cx"> 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. </span><span class="lines">@@ -61,10 +65,10 @@ </span><span class="cx"> Working with Database Objects {@name=schemasql} </span><span class="cx"> ----------------------------------------------- </span><span class="cx"> </span><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. Later, we will look into SQLAlchemy's Object Relational Mapper (ORM), which provides an additional layer of abstraction onto table metadata, allowing us to load and save objects of any arbitrary Python class. + </ins><span class="cx"> ### Defining Metadata, Binding to Engines {@name=metadata} </span><span class="cx"> </span><del>-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. - </del><span class="cx"> Firstly, your Tables have to belong to a collection called `MetaData`. We will create a handy form of `MetaData` that automatically connects to our `Engine` (connecting a schema object to an Engine is called *binding*): </span><span class="cx"> </span><span class="cx"> {python} </span><span class="lines">@@ -100,7 +104,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 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, by setting the `echo` flag on the `Engine` associated with our `BoundMetaData`: </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 doesn't necessarily 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 echo the SQL statements it sends to the database, by setting the `echo` flag on the `Engine` associated with our `BoundMetaData`: </ins><span class="cx"> </span><span class="cx"> {python} </span><span class="cx"> >>> metadata.engine.echo = True </span><span class="lines">@@ -132,31 +136,25 @@ </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>-Since we created this insert statement object from the `users` table which is bound to our `Engine`, the statement itself is also bound to the `Engine`, and supports executing itself. The `execute()` method of the clause object will *compile* the object into a string according to the underlying *dialect* of the Engine to which the statement is bound, and then executes the resulting statement. </del><ins>+Since we created this insert statement object from the `users` table which is bound to our `Engine`, the statement itself is also bound to the `Engine`, and supports executing itself. The `execute()` method of the clause object will *compile* the object into a string according to the underlying *dialect* of the Engine to which the statement is bound, and will then execute the resulting statement. </ins><span class="cx"> </span><span class="cx"> {python} </span><del>- >>> for name in ['Tom', 'Dick', 'Harry']: # doctest:+ELLIPSIS,+NORMALIZE_WHITESPACE - ... i.execute(user_name = name) - INSERT INTO users (user_name) VALUES (?) - ['Tom'] </del><ins>+ >>> i.execute(user_name='Mary', password='secure') # doctest:+ELLIPSIS + INSERT INTO users (user_name, password) VALUES (?, ?) + ['Mary', 'secure'] </ins><span class="cx"> <sqlalchemy.engine.base.ResultProxy instance at 0x...> </span><ins>+ + >>> i.execute({'user_name':'Tom'}, {'user_name':'Dick'}, {'user_name':'Harry'}) # doctest:+ELLIPSIS,+NORMALIZE_WHITESPACE </ins><span class="cx"> INSERT INTO users (user_name) VALUES (?) </span><del>- ['Dick'] </del><ins>+ [['Tom'], ['Dick'], ['Harry']] </ins><span class="cx"> <sqlalchemy.engine.base.ResultProxy instance at 0x...> </span><del>- INSERT INTO users (user_name) VALUES (?) - ['Harry'] - <sqlalchemy.engine.base.ResultProxy instance at 0x...> </del><span class="cx"> </span><del>- >>> i.execute(user_name = 'Mary', password = 'secure') # doctest:+ELLIPSIS - INSERT INTO users (user_name, password) VALUES (?, ?) - ['Mary', 'secure'] - <sqlalchemy.engine.base.ResultProxy instance at 0x...> </del><span class="cx"> </span><span class="cx"> Note that the `VALUES` clause of each `INSERT` statement was automatically adjusted to correspond to the parameters sent to the `execute()` method. This is because the compilation step of a `ClauseElement` takes into account not just the constructed SQL object and the specifics of the type of database being used, but the execution parameters sent along as well. </span><span class="cx"> </span><span class="cx"> When constructing clause objects, SQLAlchemy will bind all literal values into bind parameters. On the construction side, bind parameters are always treated as named parameters. At compilation time, SQLAlchemy will convert them their proper format, based on the paramstyle of the underlying DBAPI. This works equally well for all named and positional bind parameter formats described in the DBAPI specification. </span><span class="cx"> </span><del>-Documentation on inserting: [sqlconstruction_insert](rel:sqlconstruction_insert). </del><ins>+Documentation on inserting: [sql_insert](rel:sql_insert). </ins><span class="cx"> </span><span class="cx"> ### Selecting </span><span class="cx"> </span><span class="lines">@@ -181,9 +179,9 @@ </span><span class="cx"> ['user_id', 'user_name', 'password'] </span><span class="cx"> >>> row = r.fetchone() </span><span class="cx"> >>> row['user_name'] </span><del>- u'Tom' </del><ins>+ u'Mary' </ins><span class="cx"> >>> r.fetchall() </span><del>- [(2, u'Dick', None), (3, u'Harry', None), (4, u'Mary', u'secure')] </del><ins>+ [(2, u'Tom', None), (3, u'Dick', None), (4, u'Harry', None)] </ins><span class="cx"> </span><span class="cx"> Query criterion for the select can also be specified as regular Python expressions, using the column objects in the Table as a base: </span><span class="cx"> </span><span class="lines">@@ -193,11 +191,22 @@ </span><span class="cx"> FROM users </span><span class="cx"> WHERE users.user_name = ? </span><span class="cx"> ['Harry'] </span><del>- >>> r.fetchall() - [(3, u'Harry', None)] </del><ins>+ >>> row = r.fetchone() + >>> print row + (4, u'Harry', None) </ins><span class="cx"> </span><del>-Pretty much the full range of standard SQL operations are supported as constructed Python expressions, including joins, ordering, grouping, functions, correlated subqueries, unions, etc. Documentation on selecting: [sqlconstruction_select](rel:sqlconstruction_select). </del><ins>+Pretty much the full range of standard SQL operations are supported as constructed Python expressions, including joins, ordering, grouping, functions, correlated subqueries, unions, etc. Documentation on selecting: [sql_select](rel:sql_select). </ins><span class="cx"> </span><ins>+### Working with Rows + +You can see that when we print out the rows returned by an execution result, it prints the rows as tuples. But in fact these rows are special, and can be used either with a list interface or a dictionary interface. The dictionary interface allows the addressing of columns by string column name, or even the original `Column` object: + + {python} + >>> row['user_id'], row[1], row[users.c.password] + (4, u'Harry', None) + +Addressing the columns in a row based on the original `Column` object is especially handy, as it eliminates the need to work with literal column names altogether. + </ins><span class="cx"> ### Table Relationships </span><span class="cx"> </span><span class="cx"> Lets create a second table, `email_addresses`, which references the `users` table. To define the relationship between the two tables, we will use the `ForeignKey` construct. We will also issue the `CREATE` statement for the table in one step: </span><span class="lines">@@ -214,6 +223,14 @@ </span><span class="cx"> ) </span><span class="cx"> ... </span><span class="cx"> </span><ins>+Then lets put a few rows in: + + {python} + >>> email_addresses.insert().execute({'email_address':'to...@to...', 'user_id':2},{'email_address':'ma...@ma...', 'user_id':1}) #doctest:+ELLIPSIS + INSERT INTO email_addresses (email_address, user_id) VALUES (?, ?) + [['to...@to...', 2], ['ma...@ma...', 1]] + <sqlalchemy.engine.base.ResultProxy instance at 0x...> + </ins><span class="cx"> With two related tables, we can now construct a join amongst them, like this: </span><span class="cx"> </span><span class="cx"> {python} </span><span class="lines">@@ -221,12 +238,96 @@ </span><span class="cx"> SELECT users.user_id, users.user_name, users.password, email_addresses.address_id, email_addresses.email_address, email_addresses.user_id </span><span class="cx"> FROM users JOIN email_addresses ON users.user_id = email_addresses.user_id </span><span class="cx"> [] </span><ins>+ >>> print [row for row in r] + [(1, u'Mary', u'secure', 2, u'ma...@ma...', 1), (2, u'Tom', None, 1, u'to...@to...', 2)] </ins><span class="cx"> </span><ins>+The `join` method is also a standalone function in the `sqlalchemy` namespace. The join condition is figured out from the foreign keys of the Table objects given. They can also be specified explicitly: + + {python} + >>> print join(users, email_addresses, users.c.user_id==email_addresses.c.user_id) + users JOIN email_addresses ON users.user_id = email_addresses.user_id + +Working with Object Mappers {@name=orm} +----------------------------------------------- + +Now that we have a little bit of Table and SQL operations covered, lets look into SQLAlchemy's ORM (object relational mapper). With the ORM, you associate Tables (and other *Selectable* units, like queries and table aliases) with Python classes, into units called *Mappers*. Then you can execute queries that return lists of object instances, instead of result sets. The object instances themselves are associated with an object called a *Session*, which automatically tracks changes on each object and supports a "save all at once" operation called a *flush*. + +### Creating a Mapper {@name=mapper} + +A Mapper is usually created once per Python class, and at its core primarily means to say, "objects of this class are to be stored as rows in this table". Lets create a class called `User`, which will represent a user object that is stored in our `users` table: + + {python} + >>> class User(object): + ... def __repr__(self): + ... return "(User %s,password:%s)" % (self.user_name, self.password) + +The class is a new style class (i.e. it extends `object`) and does not require a constructor (although one may be provided if desired). We just have one `__repr__` method on it which will display basic information about the User. Note that the `__repr__` method references the instance variables `user_name` and `password` which otherwise aren't defined. While we are free to explicitly define these attributes and treat them normally, this is optional; as SQLAlchemy's `Mapper` construct will manage them for us, as their names correspond to the names of columns in the `users` table. Lets create a mapper, and observe that these attributes are now defined: + + {python} + >>> usermapper = mapper(User, users) + >>> u1 = User() + >>> print u1.user_name + None + >>> print u1.password + None </ins><span class="cx"> </span><del>-### Data Mapping {@name=mapping} </del><ins>+When you create a Mapper for a class, that Mapper is now known as the classes' *primary mapper*. SA's ORM can now automatically locate this Mapper when it deals with the class, or instances of that class. </ins><span class="cx"> </span><del>-Main documentation: [datamapping](rel:datamapping), [adv_datamapping](rel:adv_datamapping). </del><ins>+### Querying Objects {@name=querying} </ins><span class="cx"> </span><ins>+We have assigned the new Mapper we created to the instance variable `usermapper`. Using this object, we can issue queries to load objects from the database. For example, to load all of our existing User objects from the database, we just use the method `select()`, which will return a list containing all the objects. Keep in mind that query echoing is still turned on, so we will also see the SQL queries issued: + + {python} + >>> l = usermapper.select() + SELECT users.user_name AS users_user_name, users.password AS users_password, users.user_id AS users_user_id + FROM users ORDER BY users.oid + [] + >>> l + [(User Mary,password:secure), (User Tom,password:None), (User Dick,password:None), (User Harry,password:None)] + +### Obtaining a Session {@name=session} + +After you create a Mapper, all operations with that Mapper require the usage of an important object called a `Session`. All objects loaded or saved by the Mapper must be *bound* to a `Session` object, which represents a kind of "workspace" of objects that are loaded into memory. A particular object instance can only be bound to one `Session` at a time. + +By default, you have to create a `Session` object explicitly before you can use a `Mapper`, and when loading objects you need to specify the `Session` that will be used to keep track of those objects. But recall that we imported a special *mod* called `threadlocal`, which has made life easier for us by creating a `Session` that is automatically associated with the current thread. Because of that, the `Mapper` was able to use the `Session` that was already associated with the current thread, without us needing to say anything. But now, lets get a handle to that `Session` and deal with it directly. To locate the `Session` corresponding to the current thread, just use `get_session()': + + {python} + >>> session = get_session() + >>> session # doctest:+ELLIPSIS + <sqlalchemy.mapping.objectstore.Session object at 0x...> + +### The Query Object {@name=query} + +The Session has all kinds of methods on it to retrieve and store objects, and also to view their current status. The Session also provides an easy interface which can be used to query the database, by giving you an instance to a `Query` object corresponding to a particular Python class: + + {python} + >>> query = session.query(User) + >>> print query.select_by(user_name='Harry') + SELECT users.user_name AS users_user_name, users.password AS users_password, users.user_id AS users_user_id + FROM users + WHERE users.user_name = ? ORDER BY users.oid + ['Harry'] + [(User Harry,password:None)] + +All querying for objects is performed via an instance of `Query`. The various `select` methods on an instance of `Mapper` also use an underlying `Query` object to perform the operation. A `Query` can be bound to a specific `Session`, or it can also use `get_session()` to locate the session bound to the current thread, if one is available. + +Lets turn off the database echoing for a moment, and try out a few methods on `Query`. Methods that end with the suffix `_by` primarily take keyword arguments which correspond to properties on the object. Other methods take `ClauseElement` objects, which are constructed by using `Column` objects inside of Python expressions, in the same way as we did with our SQL select example in the previous section of this tutorial. Using `ClauseElement` structures to query objects is more verbose but more flexible: + + {python} + >>> metadata.engine.echo = False + >>> print query.select(User.c.user_id==3) + [(User Dick,password:None)] + >>> print query.get(2) + (User Tom,password:None) + >>> print query.get_by(user_name='Mary') + (User Mary,password:secure) + >>> print query.selectfirst(User.c.password==None) + (User Tom,password:None) + >>> print query.count() + 4 + +Notice that our `User` class has a special attribute `c` attached to it. This 'c' represents the columns on the User's mapper's Table object. Saying `User.c.user_name` is synonymous with saying `users.c.user_name`, recalling that `User` is the Python class and `users` is our `Table` object. + </ins><span class="cx"> ### Transactions </span><span class="cx"> </span><span class="cx"> Main documentation: [unitofwork](rel:unitofwork), [dbengine_transactions](rel:dbengine_transactions). </span></span></pre></div> <a id="sqlalchemybranchesschemadocbuildcontentunitofworktxt"></a> <div class="modfile"><h4>Modified: sqlalchemy/branches/schema/doc/build/content/unitofwork.txt (1304 => 1305)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/schema/doc/build/content/unitofwork.txt 2006-04-20 18:04:16 UTC (rev 1304) +++ sqlalchemy/branches/schema/doc/build/content/unitofwork.txt 2006-04-20 20:38:02 UTC (rev 1305) </span><span class="lines">@@ -1,15 +1,15 @@ </span><del>-Unit of Work </del><ins>+Unit of Work {@name=unitofwork} </ins><span class="cx"> ============ </span><span class="cx"> </span><span class="cx"> ### Overview {@name=overview} </span><span class="cx"> </span><del>-The concept behind Unit of Work is to track modifications to a field of objects, and then be able to commit those changes to the database in a single operation. Theres a lot of advantages to this, including that your application doesn't need to worry about individual save operations on objects, nor about the required order for those operations, nor about excessive repeated calls to save operations that would be more efficiently aggregated into one step. It also simplifies database transactions, providing a neat package with which to insert into the traditional database begin/commit phase. </del><ins>+The concept behind Unit of Work is to track modifications to a field of objects, and then be able to flush those changes to the database in a single operation. Theres a lot of advantages to this, including that your application doesn't need to worry about individual save operations on objects, nor about the required order for those operations, nor about excessive repeated calls to save operations that would be more efficiently aggregated into one step. It also simplifies database transactions, providing a neat package with which to insert into the traditional database begin/commit phase. </ins><span class="cx"> </span><span class="cx"> SQLAlchemy's unit of work includes these functions: </span><span class="cx"> </span><span class="cx"> * The ability to monitor scalar and list attributes on object instances, as well as object creates. This is handled via the attributes package. </span><span class="cx"> * The ability to maintain and process a list of modified objects, and based on the relationships set up by the mappers for those objects as well as the foreign key relationships of the underlying tables, figure out the proper order of operations so that referential integrity is maintained, and also so that on-the-fly values such as newly created primary keys can be propigated to dependent objects that need them before they are saved. The central algorithm for this is the *topological sort*. </span><del>-* The ability to define custom functionality that occurs within the unit-of-work commit phase, such as "before insert", "after insert", etc. This is accomplished via MapperExtension. </del><ins>+* The ability to define custom functionality that occurs within the unit-of-work flush phase, such as "before insert", "after insert", etc. This is accomplished via MapperExtension. </ins><span class="cx"> * an Identity Map, which is a dictionary storing the one and only instance of an object for a particular table/primary key combination. This allows many parts of an application to get a handle to a particular object without any chance of modifications going to two different places. </span><span class="cx"> * Thread-local operation. the Identity map as well as its enclosing Unit of Work are normally instantiated and accessed in a manner that is local to the current thread, within an object called a Session. Another concurrently executing thread will therefore have its own Session, so unless an application explicitly shares objects between threads, the operation of the object relational mapping is automatically threadsafe. Session objects can also be constructed manually to allow any user-defined scoping. </span><span class="cx"> </span><span class="lines">@@ -21,13 +21,13 @@ </span><span class="cx"> # get the current thread's session </span><span class="cx"> session = objectstore.get_session() </span><span class="cx"> </span><del>-The Session object acts as a proxy to an underlying UnitOfWork object. Common methods include commit(), begin(), clear(), and delete(). Most of these methods are available at the module level in the objectstore module, which operate upon the Session returned by the get_session() function: </del><ins>+The Session object acts as a proxy to an underlying UnitOfWork object. Common methods include flush(), clear(), and delete(). Most of these methods are available at the module level in the objectstore module, which operate upon the Session returned by the get_session() function: </ins><span class="cx"> </span><span class="cx"> {python}# this... </span><del>- objectstore.get_session().commit() </del><ins>+ objectstore.get_session().flush() </ins><span class="cx"> </span><span class="cx"> # is the same as this: </span><del>- objectstore.commit() </del><ins>+ objectstore.flush() </ins><span class="cx"> </span><span class="cx"> A description of the most important methods and concepts follows. </span><span class="cx"> </span><span class="lines">@@ -99,7 +99,7 @@ </span><span class="cx"> </span><span class="cx"> #### Whats Changed ? {@name=changed} </span><span class="cx"> </span><del>-The next concept is that in addition to the Session storing a record of all objects loaded or saved, it also stores records of all *newly created* objects, records of all objects whose attributes have been *modified*, records of all objects that have been marked as *deleted*, and records of all *modified list-based attributes* where additions or deletions have occurred. These lists are used when a `commit()` call is issued to save all changes. After the commit occurs, these lists are all cleared out. </del><ins>+The next concept is that in addition to the Session storing a record of all objects loaded or saved, it also stores records of all *newly created* objects, records of all objects whose attributes have been *modified*, records of all objects that have been marked as *deleted*, and records of all *modified list-based attributes* where additions or deletions have occurred. These lists are used when a `flush()` call is issued to save all changes. After the flush occurs, these lists are all cleared out. </ins><span class="cx"> </span><span class="cx"> These records are all tracked by a collection of `Set` objects (which are a SQLAlchemy-specific instance called a `HashSet`) that are also viewable off the Session: </span><span class="cx"> </span><span class="lines">@@ -143,8 +143,8 @@ </span><span class="cx"> >>> ["%s %s" % (l.__class__, id(l)) for l in session.modified_lists] </span><span class="cx"> ['sqlalchemy.mapping.unitofwork.UOWListElement 7391872'] </span><span class="cx"> </span><del>- >>> # now commit - >>> session.commit() </del><ins>+ >>> # now flush + >>> session.flush() </ins><span class="cx"> </span><span class="cx"> >>> # the "new" list is now empty </span><span class="cx"> >>> session.new </span><span class="lines">@@ -170,8 +170,8 @@ </span><span class="cx"> >>> session.deleted </span><span class="cx"> [<__main__.Address object at 0x713a70>] </span><span class="cx"> </span><del>- >>> # commit - >>> session.commit() </del><ins>+ >>> # flush + >>> session.flush() </ins><span class="cx"> </span><span class="cx"> >>> # all lists are cleared out </span><span class="cx"> >>> session.new, session.dirty, session.modified_lists, session.deleted </span><span class="lines">@@ -181,38 +181,36 @@ </span><span class="cx"> >>> session.identity_map.values() </span><span class="cx"> [<__main__.Address object at 0x713b30>, <__main__.User object at 0x713630>] </span><span class="cx"> </span><del>-Unlike the identity map, the `new`, `dirty`, `modified_lists`, and `deleted` lists are *not weak referencing.* This means if you abandon all references to new or modified objects within a session, *they are still present* and will be saved on the next commit operation, unless they are removed from the Session explicitly (more on that later). The `new` list may change in a future release to be weak-referencing, however for the `deleted` list, one can see that its quite natural for a an object marked as deleted to have no references in the application, yet a DELETE operation is still required. </del><ins>+Unlike the identity map, the `new`, `dirty`, `modified_lists`, and `deleted` lists are *not weak referencing.* This means if you abandon all references to new or modified objects within a session, *they are still present* and will be saved on the next flush operation, unless they are removed from the Session explicitly (more on that later). The `new` list may change in a future release to be weak-referencing, however for the `deleted` list, one can see that its quite natural for a an object marked as deleted to have no references in the application, yet a DELETE operation is still required. </ins><span class="cx"> </span><del>-#### Commit {@name=commit} </del><ins>+#### Flush {@name=flush} </ins><span class="cx"> </span><del>-This is the main gateway to what the Unit of Work does best, which is save everything ! It should be clear by now that a commit looks like: </del><ins>+This is the main gateway to what the Unit of Work does best, which is save everything ! It should be clear by now that a flush looks like: </ins><span class="cx"> </span><span class="cx"> {python} </span><del>- objectstore.get_session().commit() </del><ins>+ objectstore.get_session().flush() </ins><span class="cx"> </span><del>-It also can be called with a list of objects; in this form, the commit operation will be limited only to the objects specified in the list, as well as any child objects within `private` relationships for a delete operation: </del><ins>+It also can be called with a list of objects; in this form, the flush operation will be limited only to the objects specified in the list, as well as any child objects within `private` relationships for a delete operation: </ins><span class="cx"> </span><span class="cx"> {python} </span><span class="cx"> # saves only user1 and address2. all other modified </span><span class="cx"> # objects remain present in the session. </span><del>- objectstore.get_session().commit(user1, address2) </del><ins>+ objectstore.get_session().flush(user1, address2) </ins><span class="cx"> </span><del>-This second form of commit should be used more carefully as it will not necessarily locate other dependent objects within the session, whose database representation may have foreign constraint relationships with the objects being operated upon. </del><ins>+This second form of flush should be used more carefully as it will not necessarily locate other dependent objects within the session, whose database representation may have foreign constraint relationships with the objects being operated upon. </ins><span class="cx"> </span><del>-##### What Commit is, and Isn't {@name=whatis} </del><ins>+##### What Flush is, and Isn't {@name=whatis} </ins><span class="cx"> </span><del>-The purpose of the Commit operation, as defined by the `objectstore` package, is to instruct the Unit of Work to analyze its lists of modified objects, assemble them into a dependency graph, fire off the appopriate INSERT, UPDATE, and DELETE statements via the mappers related to those objects, and to synchronize column-based object attributes that correspond directly to updated/inserted database columns. </del><ins>+The purpose of the flush operation is to instruct the Unit of Work to analyze its lists of modified objects, assemble them into a dependency graph, fire off the appopriate INSERT, UPDATE, and DELETE statements via the mappers related to those objects, and to synchronize column-based object attributes that correspond directly to updated/inserted database columns. </ins><span class="cx"> </span><del>-Its important to note that the *objectstore.get_session().commit() operation is not the same as the commit() operation on SQLEngine.* A `SQLEngine`, described in [database](rel:database), has its own `begin` and `commit` statements which deal directly with transactions opened on DBAPI connections. While the `session.commit()` makes use of these calls in order to issue its own SQL within a database transaction, it is only dealing with "committing" its own in-memory changes and only has an indirect relationship with database connection objects. - -The `session.commit()` operation also does not affect any `relation`-based object attributes, that is attributes that reference other objects or lists of other objects, in any way. A brief list of what will *not* happen includes: </del><ins>+The `session.flush()` operation also does not affect any `relation`-based object attributes, that is attributes that reference other objects or lists of other objects, in any way. A brief list of what will *not* happen includes: </ins><span class="cx"> </span><span class="cx"> * It will not append or delete any object instances to/from any list-based object attributes. Any objects that have been created or marked as deleted will be updated as such in the database, but if a newly deleted object instance is still attached to a parent object's list, the object itself will remain in that list. </span><del>-* It will not set or remove any scalar references to other objects, even if the corresponding database identifier columns have been committed. </del><ins>+* It will not set or remove any scalar references to other objects, even if the corresponding database identifier columns have been flushed. </ins><span class="cx"> </span><del>-This means, if you set `address.user_id` to 5, that integer attribute will be saved, but it will not place an `Address` object in the `addresses` attribute of the corresponding `User` object. In some cases there may be a lazy-loader still attached to an object attribute which when first accesed performs a fresh load from the database and creates the appearance of this behavior, but this behavior should not be relied upon as it is specific to lazy loading and also may disappear in a future release. Similarly, if the `Address` object is marked as deleted and a commit is issued, the correct DELETE statements will be issued, but if the object instance itself is still attached to the `User`, it will remain. </del><ins>+This means, if you set `address.user_id` to 5, that integer attribute will be saved, but it will not place an `Address` object in the `addresses` attribute of the corresponding `User` object. In some cases there may be a lazy-loader still attached to an object attribute which when first accesed performs a fresh load from the database and creates the appearance of this behavior, but this behavior should not be relied upon as it is specific to lazy loading and also may disappear in a future release. Similarly, if the `Address` object is marked as deleted and a flush is issued, the correct DELETE statements will be issued, but if the object instance itself is still attached to the `User`, it will remain. </ins><span class="cx"> </span><del>-So the primary guideline for dealing with commit() is, *the developer is responsible for maintaining in-memory objects and their relationships to each other, the unit of work is responsible for maintaining the database representation of the in-memory objects.* The typical pattern is that the manipulation of objects *is* the way that changes get communicated to the unit of work, so that when the commit occurs, the objects are already in their correct in-memory representation and problems dont arise. The manipulation of identifier attributes like integer key values as well as deletes in particular are a frequent source of confusion. </del><ins>+So the primary guideline for dealing with flush() is, *the developer is responsible for maintaining in-memory objects and their relationships to each other, the unit of work is responsible for maintaining the database representation of the in-memory objects.* The typical pattern is that the manipulation of objects *is* the way that changes get communicated to the unit of work, so that when the flush occurs, the objects are already in their correct in-memory representation and problems dont arise. The manipulation of identifier attributes like integer key values as well as deletes in particular are a frequent source of confusion. </ins><span class="cx"> </span><span class="cx"> A terrific feature of SQLAlchemy which is also a supreme source of confusion is the backreference feature, described in [datamapping_relations_backreferences](rel:datamapping_relations_backreferences). This feature allows two types of objects to maintain attributes that reference each other, typically one object maintaining a list of elements of the other side, which contains a scalar reference to the list-holding object. When you append an element to the list, the element gets a "backreference" back to the object which has the list. When you attach the list-holding element to the child element, the child element gets attached to the list. *This feature has nothing to do whatsoever with the Unit of Work.*`*` It is strictly a small convenience feature intended to support the developer's manual manipulation of in-memory objects, and the backreference operation happens at the moment objects are attached or removed to/from each other, inde! pendent of any kind of database operation. It does not change the golden rule, that the developer is reponsible for maintaining in-memory object relationships. </span><span class="cx"> </span><span class="lines">@@ -226,8 +224,8 @@ </span><span class="cx"> # mark three objects to be deleted </span><span class="cx"> objectstore.get_session().delete(obj1, obj2, obj3) </span><span class="cx"> </span><del>- # commit - objectstore.get_session().commit() </del><ins>+ # flush + objectstore.get_session().flush() </ins><span class="cx"> </span><span class="cx"> When objects which contain references to other objects are deleted, the mappers for those related objects will issue UPDATE statements for those objects that should no longer contain references to the deleted object, setting foreign key identifiers to NULL. Similarly, when a mapper contains relations with the `private=True` option, DELETE statements will be issued for objects within that relationship in addition to that of the primary deleted object; this is called a *cascading delete*. </span><span class="cx"> </span><span class="lines">@@ -265,7 +263,7 @@ </span><span class="cx"> {python} </span><span class="cx"> session.expunge(obj1) </span><span class="cx"> </span><del>-Use `expunge` when youd like to remove an object altogether from memory, such as before calling `del` on it, which will prevent any "ghost" operations occuring when the session is committed. </del><ins>+Use `expunge` when youd like to remove an object altogether from memory, such as before calling `del` on it, which will prevent any "ghost" operations occuring when the session is flushed. </ins><span class="cx"> </span><span class="cx"> #### Import Instance {@name=import} </span><span class="cx"> </span><span class="lines">@@ -281,31 +279,11 @@ </span><span class="cx"> </span><span class="cx"> 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. Thats why its important to receive the return results from the method and use the result as the official object instance. </span><span class="cx"> </span><del>-#### Begin {@name=begin} - -The "scope" of the unit of work commit can be controlled further by issuing a begin(). A begin operation constructs a new UnitOfWork object and sets it as the currently used UOW. It maintains a reference to the original UnitOfWork as its "parent", and shares the same identity map of objects that have been loaded from the database within the scope of the parent UnitOfWork. However, the "new", "dirty", and "deleted" lists are empty. This has the effect that only changes that take place after the begin() operation get logged to the current UnitOfWork, and therefore those are the only changes that get commit()ted. When the commit is complete, the "begun" UnitOfWork removes itself and places the parent UnitOfWork as the current one again. -The begin() method returns a transactional object, upon which you can call commit() or rollback(). *Only this transactional object controls the transaction* - commit() upon the Session will do nothing until commit() or rollback() is called upon the transactional object. - - {python} - # modify an object - myobj1.foo = "something new" - - # begin - trans = session.begin() - - # modify another object - myobj2.lala = "something new" - - # only 'myobj2' is saved - trans.commit() - -begin/commit supports the same "nesting" behavior as the SQLEngine (note this behavior is not the original "nested" behavior), meaning that many begin() calls can be made, but only the outermost transactional object will actually perform a commit(). Similarly, calls to the commit() method on the Session, which might occur in function calls within the transaction, will not do anything; this allows an external function caller to control the scope of transactions used within the functions. - </del><span class="cx"> ### Advanced UnitOfWork Management {@name=advscope} </span><span class="cx"> </span><span class="cx"> #### Nesting UnitOfWork in a Database Transaction {@name=transactionnesting} </span><span class="cx"> </span><del>-The UOW commit operation places its INSERT/UPDATE/DELETE operations within the scope of a database transaction controlled by a SQLEngine: </del><ins>+The UOW flush operation places its INSERT/UPDATE/DELETE operations within the scope of a database transaction controlled by a SQLEngine: </ins><span class="cx"> </span><span class="cx"> {python} </span><span class="cx"> engine.begin() </span><span class="lines">@@ -314,7 +292,7 @@ </span><span class="cx"> except: </span><span class="cx"> engine.rollback() </span><span class="cx"> raise </span><del>- engine.commit() </del><ins>+ engine.flush() </ins><span class="cx"> </span><span class="cx"> If you recall from the [dbengine_transactions](rel:dbengine_transactions) section, the engine's begin()/commit() methods support reentrant behavior. This means you can nest begin and commits and only have the outermost begin/commit pair actually take effect (rollbacks however, abort the whole operation at any stage). From this it follows that the UnitOfWork commit operation can be nested within a transaction as well: </span><span class="cx"> </span><span class="lines">@@ -322,7 +300,7 @@ </span><span class="cx"> engine.begin() </span><span class="cx"> try: </span><span class="cx"> # perform custom SQL operations </span><del>- objectstore.commit() </del><ins>+ objectstore.flush() </ins><span class="cx"> # perform custom SQL operations </span><span class="cx"> except: </span><span class="cx"> engine.rollback() </span><span class="lines">@@ -347,15 +325,15 @@ </span><span class="cx"> # get the session that corresponds to an instance </span><span class="cx"> s = objectstore.get_session(x) </span><span class="cx"> </span><del>- # commit - s.commit() </del><ins>+ # flush + s.flush() </ins><span class="cx"> </span><span class="cx"> # perform a block of operations with this session set within the current scope </span><span class="cx"> objectstore.push_session(s) </span><span class="cx"> try: </span><span class="cx"> r = mapper.select_by(id=12) </span><span class="cx"> x = new MyObj() </span><del>- objectstore.commit() </del><ins>+ objectstore.flush() </ins><span class="cx"> finally: </span><span class="cx"> objectstore.pop_session() </span><span class="cx"> </span></span></pre></div> <a id="sqlalchemybranchesschemadocbuildtxt2mytpy"></a> <div class="modfile"><h4>Modified: sqlalchemy/branches/schema/doc/build/txt2myt.py (1304 => 1305)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/schema/doc/build/txt2myt.py 2006-04-20 18:04:16 UTC (rev 1304) +++ sqlalchemy/branches/schema/doc/build/txt2myt.py 2006-04-20 20:38:02 UTC (rev 1305) </span><span class="lines">@@ -91,20 +91,30 @@ </span><span class="cx"> text = re.compile(r'^(?!<&)', re.M).sub(' ', text) </span><span class="cx"> </span><span class="cx"> sqlre = re.compile(r'{sql}(.*?)((?:SELECT|INSERT|DELETE|UPDATE|CREATE|DROP).*?)\n\s*(\n|$)', re.S) </span><ins>+ if sqlre.search(text) is not None: + use_sliders = False + else: + use_sliders = True + </ins><span class="cx"> text = sqlre.sub(r"<&formatting.myt:poplink&>\1\n<&|formatting.myt:codepopper, link='sql'&>\2</&>\n\n", text) </span><span class="cx"> </span><span class="cx"> sqlre2 = re.compile(r'{opensql}(.*?)((?:SELECT|INSERT|DELETE|UPDATE|CREATE|DROP).*?)\n\s*(\n|$)', re.S) </span><span class="cx"> text = sqlre2.sub(r"<&|formatting.myt:poppedcode &>\1\n\2</&>\n\n", text) </span><span class="cx"> </span><span class="cx"> pre_parent = parent[pre] </span><ins>+ opts = {} </ins><span class="cx"> if type == 'python': </span><del>- syntype = 'python' </del><ins>+ opts['syntaxtype'] = 'python' </ins><span class="cx"> else: </span><del>- syntype = None </del><ins>+ opts['syntaxtype'] = None + </ins><span class="cx"> if title is not None: </span><del>- tag = MyghtyTag(CODE_BLOCK, {'title':title, 'syntaxtype':syntype}) - else: - tag = MyghtyTag(CODE_BLOCK, {'syntaxtype':syntype}) </del><ins>+ opts['title'] = title + + if use_sliders: + opts['use_sliders'] = True + + tag = MyghtyTag(CODE_BLOCK, opts) </ins><span class="cx"> tag.text = text </span><span class="cx"> tag.tail = pre.tail </span><span class="cx"> pre_parent[index(pre_parent, pre)] = tag </span></span></pre></div> <a id="sqlalchemybranchesschemadocdocscss"></a> <div class="modfile"><h4>Modified: sqlalchemy/branches/schema/doc/docs.css (1304 => 1305)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/schema/doc/docs.css 2006-04-20 18:04:16 UTC (rev 1304) +++ sqlalchemy/branches/schema/doc/docs.css 2006-04-20 20:38:02 UTC (rev 1305) </span><span class="lines">@@ -197,6 +197,16 @@ </span><span class="cx"> line-height:1.2em; </span><span class="cx"> } </span><span class="cx"> </span><ins>+.sliding_code { + font-family:courier, serif; + font-size:12px; + background-color: #E2E2EB; + padding:2px 2px 2px 10px; + margin: 5px 5px 5px 5px; + line-height:1.2em; + overflow:auto; +} + </ins><span class="cx"> .codepop { </span><span class="cx"> font-weight:bold; </span><span class="cx"> font-family: verdana, sans-serif; </span></span></pre></div> <a id="sqlalchemybranchesschemalibsqlalchemy__init__py"></a> <div class="modfile"><h4>Modified: sqlalchemy/branches/schema/lib/sqlalchemy/__init__.py (1304 => 1305)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/schema/lib/sqlalchemy/__init__.py 2006-04-20 18:04:16 UTC (rev 1304) +++ sqlalchemy/branches/schema/lib/sqlalchemy/__init__.py 2006-04-20 20:38:02 UTC (rev 1305) </span><span class="lines">@@ -14,7 +14,7 @@ </span><span class="cx"> from sqlalchemy.mapping import * </span><span class="cx"> import sqlalchemy.ext.proxy </span><span class="cx"> </span><del>-from sqlalchemy.mapping.objectstore import Session </del><ins>+from sqlalchemy.mapping.objectstore import Session, get_session </ins><span class="cx"> </span><span class="cx"> create_engine = sqlalchemy.engine.create_engine </span><span class="cx"> </span></span></pre></div> <a id="sqlalchemybranchesschemalibsqlalchemymappingobjectstorepy"></a> <div class="modfile"><h4>Modified: sqlalchemy/branches/schema/lib/sqlalchemy/mapping/objectstore.py (1304 => 1305)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/schema/lib/sqlalchemy/mapping/objectstore.py 2006-04-20 18:04:16 UTC (rev 1304) +++ sqlalchemy/branches/schema/lib/sqlalchemy/mapping/objectstore.py 2006-04-20 20:38:02 UTC (rev 1305) </span><span class="lines">@@ -355,26 +355,32 @@ </span><span class="cx"> to any Session, then an error is raised (or None is returned if raiseerror=False). This behavior can be changed </span><span class="cx"> using the "threadlocal" mod, which will add an additional step to return a Session that is bound to the current </span><span class="cx"> thread.""" </span><ins>+ if obj is not None: + # does it have a hash key ? + hashkey = getattr(obj, '_sa_session_id', None) + if hashkey is not None: + # ok, return that + try: + return _sessions[hashkey] + except KeyError: + if raiseerror: + raise InvalidRequestError("Session '%s' referenced by object '%s' no longer exists" % (hashkey, repr(obj))) + else: + return None + + return _default_session(obj=obj, raiseerror=raiseerror) + +def _default_session(obj=None, raiseerror=True): </ins><span class="cx"> if obj is None: </span><span class="cx"> if raiseerror: </span><span class="cx"> raise InvalidRequestError("Thread-local Sessions are disabled by default. Use 'import sqlalchemy.mods.threadlocal' to enable.") </span><span class="cx"> else: </span><span class="cx"> return None </span><del>- # does it have a hash key ? - hashkey = getattr(obj, '_sa_session_id', None) - if hashkey is not None: - # ok, return that - try: - return _sessions[hashkey] - except KeyError: - if raiseerror: - raise InvalidRequestError("Session '%s' referenced by object '%s' no longer exists" % (hashkey, repr(obj))) - else: - return None </del><span class="cx"> else: </span><span class="cx"> if raiseerror: </span><span class="cx"> raise InvalidRequestError("Object '%s' not bound to any Session" % (repr(obj))) </span><span class="cx"> else: </span><span class="cx"> return None </span><ins>+ </ins><span class="cx"> unitofwork.get_session = get_session </span><span class="cx"> </span></span></pre></div> <a id="sqlalchemybranchesschemalibsqlalchemymodsthreadlocalpy"></a> <div class="modfile"><h4>Modified: sqlalchemy/branches/schema/lib/sqlalchemy/mods/threadlocal.py (1304 => 1305)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/schema/lib/sqlalchemy/mods/threadlocal.py 2006-04-20 18:04:16 UTC (rev 1304) +++ sqlalchemy/branches/schema/lib/sqlalchemy/mods/threadlocal.py 2006-04-20 20:38:02 UTC (rev 1305) </span><span class="lines">@@ -19,23 +19,8 @@ </span><span class="cx"> explicit Session objects when creating instances and creating queries. </span><span class="cx"> """ </span><span class="cx"> </span><del>-def get_session(obj=None, raiseerror=True): - # object-specific session ? - if obj is not None: - # does it have a hash key ? - hashkey = getattr(obj, '_sa_session_id', None) - if hashkey is not None: - # ok, return that - try: - return objectstore._sessions[hashkey] - except KeyError: - if raiseerror: - raise InvalidRequestError("Session '%s' referenced by object '%s' no longer exists" % (hashkey, repr(obj))) - else: - return None </del><ins>+get_session = objectstore.get_session </ins><span class="cx"> </span><del>- return objectstore.session_registry() - </del><span class="cx"> def begin(*obj): </span><span class="cx"> return get_session().begin(*obj) </span><span class="cx"> def commit(*obj): </span><span class="lines">@@ -95,11 +80,10 @@ </span><span class="cx"> return get_session().import_instance(instance) </span><span class="cx"> </span><span class="cx"> def install_plugin(): </span><del>- unitofwork.get_session = get_session </del><span class="cx"> mod = sys.modules[__name__] </span><del>- for name in ['get_session', 'import_instance', 'instance_key', 'has_instance', 'is_dirty', 'has_key', 'delete', 'expunge', 'expire', 'refresh', 'clear', 'flush', 'begin', 'commit']: </del><ins>+ for name in ['import_instance', 'instance_key', 'has_instance', 'is_dirty', 'has_key', 'delete', 'expunge', 'expire', 'refresh', 'clear', 'flush', 'begin', 'commit']: </ins><span class="cx"> setattr(objectstore, name, getattr(mod, name)) </span><del>- - objectstore.session_registry = util.ScopedRegistry(objectstore.Session) # Default session registry </del><ins>+ reg = util.ScopedRegistry(objectstore.Session) + objectstore._default_session = lambda *args, **kwargs: reg() </ins><span class="cx"> engine.default_strategy = 'threadlocal' </span><span class="cx"> install_plugin() </span></span></pre> </div> </div> </body> </html> |
From: <co...@sq...> - 2006-04-20 18:04: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>[1304] sqlalchemy/branches/schema/test: session interface....</title> </head> <body> <div id="msg"> <dl> <dt>Revision</dt> <dd>1304</dd> <dt>Author</dt> <dd>zzzeek</dd> <dt>Date</dt> <dd>2006-04-20 13:04:16 -0500 (Thu, 20 Apr 2006)</dd> </dl> <h3>Log Message</h3> <pre>session interface....</pre> <h3>Modified Paths</h3> <ul> <li><a href="#sqlalchemybranchesschemalibsqlalchemymappingmapperpy">sqlalchemy/branches/schema/lib/sqlalchemy/mapping/mapper.py</a></li> <li><a href="#sqlalchemybranchesschemalibsqlalchemymappingobjectstorepy">sqlalchemy/branches/schema/lib/sqlalchemy/mapping/objectstore.py</a></li> <li><a href="#sqlalchemybranchesschemalibsqlalchemymappingunitofworkpy">sqlalchemy/branches/schema/lib/sqlalchemy/mapping/unitofwork.py</a></li> <li><a href="#sqlalchemybranchesschematestobjectstorepy">sqlalchemy/branches/schema/test/objectstore.py</a></li> </ul> </div> <div id="patch"> <h3>Diff</h3> <a id="sqlalchemybranchesschemalibsqlalchemymappingmapperpy"></a> <div class="modfile"><h4>Modified: sqlalchemy/branches/schema/lib/sqlalchemy/mapping/mapper.py (1303 => 1304)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/schema/lib/sqlalchemy/mapping/mapper.py 2006-04-20 02:30:21 UTC (rev 1303) +++ sqlalchemy/branches/schema/lib/sqlalchemy/mapping/mapper.py 2006-04-20 18:04:16 UTC (rev 1304) </span><span class="lines">@@ -49,8 +49,7 @@ </span><span class="cx"> allow_column_override = False, </span><span class="cx"> entity_name = None, </span><span class="cx"> always_refresh = False, </span><del>- version_id_col = None, - **kwargs): </del><ins>+ version_id_col = None): </ins><span class="cx"> </span><span class="cx"> if primarytable is not None: </span><span class="cx"> sys.stderr.write("'primarytable' argument to mapper is deprecated\n") </span><span class="lines">@@ -333,6 +332,11 @@ </span><span class="cx"> not only on class assignment but the optional "entity_name" parameter as well.""" </span><span class="cx"> return instance.__class__ is self.class_ and getattr(instance, '_entity_name', None) == self.entity_name </span><span class="cx"> </span><ins>+ def _assign_entity_name(self, instance): + """assigns this Mapper's entity name to the given instance. subsequent Mapper lookups for this + instance will return the primary mapper corresponding to this Mapper's class and entity name.""" + instance._entity_name = self.entity_name + </ins><span class="cx"> def _init_class(self): </span><span class="cx"> """sets up our classes' overridden __init__ method, this mappers hash key as its </span><span class="cx"> '_mapper' property, and our columns as its 'c' property. if the class already had a </span><span class="lines">@@ -355,7 +359,7 @@ </span><span class="cx"> # register new with the correct session, before the object's </span><span class="cx"> # constructor is called, since further assignments within the </span><span class="cx"> # constructor would otherwise bind it to whatever get_session() is. </span><del>- session.register_new(self) </del><ins>+ session._register_new(self) </ins><span class="cx"> else: </span><span class="cx"> session._bind_to(self) </span><span class="cx"> if oldinit is not None: </span><span class="lines">@@ -403,7 +407,7 @@ </span><span class="cx"> </span><span class="cx"> # store new stuff in the identity map </span><span class="cx"> for value in imap.values(): </span><del>- session.register_clean(value) </del><ins>+ session._register_clean(value) </ins><span class="cx"> </span><span class="cx"> if mappers: </span><span class="cx"> result = [result] + otherresults </span><span class="lines">@@ -955,13 +959,16 @@ </span><span class="cx"> else: </span><span class="cx"> return repr(obj) </span><span class="cx"> </span><del>-def object_mapper(object): </del><ins>+def object_mapper(object, raiseerror=True): </ins><span class="cx"> """given an object, returns the primary Mapper associated with the object </span><span class="cx"> or the object's class.""" </span><span class="cx"> try: </span><span class="cx"> return mapper_registry[ClassKey(object.__class__, getattr(object, '_entity_name', None))] </span><span class="cx"> except KeyError: </span><del>- raise InvalidRequestError("Class '%s' entity name '%s' has no mapper associated with it" % (object.__class__.__name__, getattr(object, '_entity_name', None))) </del><ins>+ if raiseerror: + raise InvalidRequestError("Class '%s' entity name '%s' has no mapper associated with it" % (object.__class__.__name__, getattr(object, '_entity_name', None))) + else: + return None </ins><span class="cx"> </span><span class="cx"> def class_mapper(class_, entity_name=None): </span><span class="cx"> """given a ClassKey, returns the primary Mapper associated with the key.""" </span></span></pre></div> <a id="sqlalchemybranchesschemalibsqlalchemymappingobjectstorepy"></a> <div class="modfile"><h4>Modified: sqlalchemy/branches/schema/lib/sqlalchemy/mapping/objectstore.py (1303 => 1304)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/schema/lib/sqlalchemy/mapping/objectstore.py 2006-04-20 02:30:21 UTC (rev 1303) +++ sqlalchemy/branches/schema/lib/sqlalchemy/mapping/objectstore.py 2006-04-20 18:04:16 UTC (rev 1304) </span><span class="lines">@@ -80,27 +80,59 @@ </span><span class="cx"> """returns a unique connection corresponding to the given mapper. this connection </span><span class="cx"> will not be part of any pre-existing transactional context.""" </span><span class="cx"> return self.get_bind(mapper).connect(**kwargs) </span><del>- def connection(self, mapper): - """returns a connection corresponding to the given mapper. used by the execute() </del><ins>+ def connection(self, mapper, **kwargs): + """returns a Connection corresponding to the given mapper. used by the execute() </ins><span class="cx"> method which performs select operations for Mapper and Query. </span><span class="cx"> if this Session is transactional, </span><span class="cx"> the connection will be in the context of this session's transaction. otherwise, the connection </span><del>- will be unique, and will also have the close_with_result flag set to True so that the connection - can be closed out using the result alone.""" </del><ins>+ will be unique. + + the given **kwargs will be sent to the engine's connect() method, if no transaction is in progress.""" </ins><span class="cx"> if self.transaction is not None: </span><span class="cx"> return self.transaction.connection(mapper) </span><span class="cx"> else: </span><del>- return self.connect(mapper, close_with_result=True) </del><ins>+ return self.connect(mapper, **kwargs) </ins><span class="cx"> def execute(self, mapper, clause, params, **kwargs): </span><del>- return self.connection(mapper).execute(clause, params, **kwargs) </del><ins>+ """using the given mapper to identify the appropriate Engine or Connection to be used for statement execution, + executes the given ClauseElement using the provided parameter dictionary. Returns a ResultProxy corresponding + to the execution's results. If this method allocates a new Connection for the operation, then the ResultProxy's close() + method will release the resources of the underlying Connection, otherwise its a no-op. + """ + return self.connection(mapper, close_with_result=True).execute(clause, params, **kwargs) </ins><span class="cx"> def close(self): </span><ins>+ """closes this Session. + + TODO: what should we do here ? + """ </ins><span class="cx"> if self.transaction is not None: </span><span class="cx"> self.transaction.close() </span><span class="cx"> def bind_mapper(self, mapper, bindto): </span><ins>+ """binds the given Mapper to the given Engine or Connection. All subsequent operations involving this + Mapper will use the given bindto.""" </ins><span class="cx"> self.binds[mapper] = bindto </span><span class="cx"> def bind_table(self, table, bindto): </span><ins>+ """binds the given Table to the given Engine or Connection. All subsequent operations involving this + Table will use the given bindto.""" </ins><span class="cx"> self.binds[table] = bindto </span><span class="cx"> def get_bind(self, mapper): </span><ins>+ """given a Mapper, returns the Engine or Connection which is used to execute statements on behalf of this + Mapper. Calling connect() on the return result will always result in a Connection object. This method + disregards any SessionTransaction that may be in progress. + + The order of searching is as follows: + + if an Engine or Connection was bound to this Mapper specifically within this Session, returns that + Engine or Connection. + + if an Engine or Connection was bound to this Mapper's underlying Table within this Session + (i.e. not to the Table directly), returns that Engine or Conneciton. + + if an Engine or Connection was bound to this Session, returns that Engine or Connection. + + finally, returns the Engine which was bound directly to the Table's MetaData object. + + If no Engine is bound to the Table, an exception is raised. + """ </ins><span class="cx"> if mapper is None: </span><span class="cx"> return self.bind_to </span><span class="cx"> elif self.binds.has_key(mapper): </span><span class="lines">@@ -112,6 +144,7 @@ </span><span class="cx"> else: </span><span class="cx"> return mapper.table.engine </span><span class="cx"> def query(self, mapper_or_class): </span><ins>+ """given a mapper or Class, returns a new Query object corresponding to this Session and the mapper, or the classes' primary mapper.""" </ins><span class="cx"> if isinstance(mapper_or_class, type): </span><span class="cx"> return query.Query(class_mapper(mapper_or_class), self) </span><span class="cx"> else: </span><span class="lines">@@ -158,11 +191,15 @@ </span><span class="cx"> get_row_key = staticmethod(get_row_key) </span><span class="cx"> </span><span class="cx"> def begin(self, *obj): </span><ins>+ """deprecated""" </ins><span class="cx"> raise InvalidRequestError("Session.begin() is deprecated. use install_mod('legacy_session') to enable the old behavior") </span><span class="cx"> def commit(self, *obj): </span><ins>+ """deprecated""" </ins><span class="cx"> raise InvalidRequestError("Session.commit() is deprecated. use install_mod('legacy_session') to enable the old behavior") </span><span class="cx"> </span><span class="cx"> def flush(self, *obj): </span><ins>+ """flushes all the object modifications present in this session to the database. if object + arguments are given, then only those objects (and immediate dependencies) are flushed.""" </ins><span class="cx"> self.uow.flush(self, *obj) </span><span class="cx"> </span><span class="cx"> def refresh(self, *obj): </span><span class="lines">@@ -178,97 +215,149 @@ </span><span class="cx"> self.uow.expire(o) </span><span class="cx"> </span><span class="cx"> def expunge(self, *obj): </span><ins>+ """removes the given objects from this Session. this will free all internal references to the objects.""" </ins><span class="cx"> for o in obj: </span><span class="cx"> self.uow.expunge(o) </span><span class="cx"> </span><del>- def register_clean(self, *obj): </del><ins>+ def add(self, *obj, **kwargs): + """adds unsaved objects to this Session. + + The 'entity_name' keyword argument can also be given which will be assigned + to the instances if given. + """ </ins><span class="cx"> for o in obj: </span><del>- self._bind_to(o) - self.uow.register_clean(o) - - def add(self, *obj): - """given some objects, if they have no identity they will be registered as new in this session. - if they have an identity, its verified that they are already part of this session.""" - for o in obj: </del><span class="cx"> if hasattr(o, '_instance_key'): </span><span class="cx"> if not self.uow.has_key(o._instance_key): </span><del>- raise InvalidRequestError("Instance '%s' is not bound to this Session" % repr(o)) </del><ins>+ raise InvalidRequestError("Instance '%s' is not bound to this Session; use session.import(instance)" % repr(o)) </ins><span class="cx"> else: </span><del>- self.register_new(o) </del><ins>+ entity_name = kwargs.get('entity_name', None) + if entity_name is not None: + m = class_mapper(o.__class__, entity_name=entity_name) + m._assign_entity_name(o) + self._register_new(o) </ins><span class="cx"> </span><del>- def register_new(self, *obj): - """registers the given objects as "new" and binds them to this session.""" - for o in obj: - self._bind_to(o) - self.uow.register_new(o) - </del><ins>+ def _register_new(self, obj): + self._bind_to(obj) + self.uow.register_new(obj) + def _register_dirty(self, obj): + self._bind_to(obj) + self.uow.register_dirty(obj) + def _register_clean(self, obj): + self._bind_to(obj) + self.uow.register_clean(obj) + def _register_deleted(self, obj): + self._bind_to(obj) + self.uow.register_deleted(obj) </ins><span class="cx"> def _bind_to(self, obj): </span><span class="cx"> """given an object, binds it to this session. changes on the object will affect </span><span class="cx"> the currently scoped UnitOfWork maintained by this session.""" </span><span class="cx"> obj._sa_session_id = self.hash_key </span><del>- - def __getattr__(self, key): - """proxy other methods to our underlying UnitOfWork""" - return getattr(self.uow, key) - </del><ins>+ def _is_bound(self, obj): + return getattr(obj, '_sa_session_id', None) == self.hash_key + + def _get(self, key): + return self.uow._get(key) + def has_key(self, key): + return self.uow.has_key(key) + def is_expired(self, instance, **kwargs): + return self.uow.is_expired(instance, **kwargs) + + dirty = property(lambda s:s.uow.dirty) + deleted = property(lambda s:s.uow.deleted) + new = property(lambda s:s.uow.new) + modified_lists = property(lambda s:s.uow.modified_lists) + identity_map = property(lambda s:s.uow.identity_map) + </ins><span class="cx"> def clear(self): </span><ins>+ """removes all object instances from this Session. this is equivalent to calling expunge() for all + objects in this Session.""" </ins><span class="cx"> self.uow = unitofwork.UnitOfWork() </span><span class="cx"> </span><del>- def delete(self, *obj): - """registers the given objects as to be deleted upon the next commit""" </del><ins>+ def delete(self, *obj, **kwargs): + """registers the given objects to be deleted upon the next flush(). If the given objects are not part of this + Session, they will be imported. the objects are expected to either have an _instance_key + attribute or have all of their primary key attributes populated. + + the keyword argument 'entity_name' can also be provided which will be used by the import.""" </ins><span class="cx"> for o in obj: </span><ins>+ if not self._is_bound(o): + o = self.import_(o, **kwargs) </ins><span class="cx"> self.uow.register_deleted(o) </span><span class="cx"> </span><del>- def import_instance(self, instance): - """places the given instance in the current thread's unit of work context, - either in the current IdentityMap or marked as "new". Returns either the object - or the current corresponding version in the Identity Map. </del><ins>+ def import_(self, instance, entity_name=None): + """given an instance that represents a saved item, adds it to this session. + the return value is either the given instance, or if an instance corresponding to the + identity of the given instance already exists within this session, then that instance is returned; + the returned instance should always be used following this method. + + if the given instance does not have an _instance_key and also does not have all + of its primary key attributes populated, an exception is raised. similarly, if no + mapper can be located for the given instance, an exception is raised. </ins><span class="cx"> </span><span class="cx"> this method should be used for any object instance that is coming from a serialized </span><del>- storage, from another thread (assuming the regular threaded unit of work model), or any - case where the instance was loaded/created corresponding to a different base unitofwork - than the current one.""" </del><ins>+ storage, or was loaded by a Session other than this one. + + the keyword parameter entity_name is optional and is used to locate a Mapper for this + class which also specifies the given entity name. + """ </ins><span class="cx"> if instance is None: </span><span class="cx"> return None </span><span class="cx"> key = getattr(instance, '_instance_key', None) </span><del>- mapper = object_mapper(instance) </del><ins>+ mapper = object_mapper(instance, raiseerror=False) + if mapper is None: + mapper = class_mapper(instance, entity_name=entity_name) + if key is None: + ident = mapper.identity(instance) + for k in ident: + if k is None: + raise InvalidRequestError("Instance '%s' does not have a full set of identity values, and does not represent a saved entity in the database. Use the add() method to add unsaved instances to this Session." % str(instance)) + key = mapper.identity_key(*ident) </ins><span class="cx"> u = self.uow </span><del>- if key is not None: - if u.identity_map.has_key(key): - return u.identity_map[key] - else: - instance._instance_key = key - u.identity_map[key] = instance - self._bind_to(instance) </del><ins>+ if u.identity_map.has_key(key): + return u.identity_map[key] </ins><span class="cx"> else: </span><del>- u.register_new(instance) - return instance </del><ins>+ instance._instance_key = key + u.identity_map[key] = instance + self._bind_to(instance) + return instance + + def import_instance(self, *args, **kwargs): + """deprecated; a synynom for import()""" + return self.import_(*args, **kwargs) </ins><span class="cx"> </span><del>- </del><span class="cx"> def get_id_key(ident, class_, entity_name=None): </span><span class="cx"> return Session.get_id_key(ident, class_, entity_name) </span><span class="cx"> </span><span class="cx"> def get_row_key(row, class_, primary_key, entity_name=None): </span><span class="cx"> return Session.get_row_key(row, class_, primary_key, entity_name) </span><span class="cx"> </span><del>- </del><span class="cx"> def mapper(*args, **params): </span><span class="cx"> return sqlalchemy.mapping.mapper(*args, **params) </span><span class="cx"> </span><span class="cx"> def object_mapper(obj): </span><span class="cx"> return sqlalchemy.mapping.object_mapper(obj) </span><span class="cx"> </span><del>-def class_mapper(class_): - return sqlalchemy.mapping.class_mapper(class_) </del><ins>+def class_mapper(class_, **kwargs): + return sqlalchemy.mapping.class_mapper(class_, **kwargs) </ins><span class="cx"> </span><ins>+# this is the AttributeManager instance used to provide attribute behavior on objects. +# to all the "global variable police" out there: its a stateless object. </ins><span class="cx"> global_attributes = unitofwork.global_attributes </span><span class="cx"> </span><del>-_sessions = weakref.WeakValueDictionary() # all referenced sessions (including user-created) </del><ins>+# this dictionary maps the hash key of a Session to the Session itself, and +# acts as a Registry with which to locate Sessions. this is to enable +# object instances to be associated with Sessions without having to attach the +# actual Session object directly to the object instance. +_sessions = weakref.WeakValueDictionary() </ins><span class="cx"> </span><span class="cx"> def get_session(obj=None, raiseerror=True): </span><ins>+ """returns the Session corrseponding to the given object instance. By default, if the object is not bound + to any Session, then an error is raised (or None is returned if raiseerror=False). This behavior can be changed + using the "threadlocal" mod, which will add an additional step to return a Session that is bound to the current + thread.""" </ins><span class="cx"> if obj is None: </span><span class="cx"> if raiseerror: </span><del>- raise InvalidRequestError("Thread-local Sessions are disabled by default. Use install_mods('threadlocal') to enable.") </del><ins>+ raise InvalidRequestError("Thread-local Sessions are disabled by default. Use 'import sqlalchemy.mods.threadlocal' to enable.") </ins><span class="cx"> else: </span><span class="cx"> return None </span><span class="cx"> # does it have a hash key ? </span></span></pre></div> <a id="sqlalchemybranchesschemalibsqlalchemymappingunitofworkpy"></a> <div class="modfile"><h4>Modified: sqlalchemy/branches/schema/lib/sqlalchemy/mapping/unitofwork.py (1303 => 1304)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/schema/lib/sqlalchemy/mapping/unitofwork.py 2006-04-20 02:30:21 UTC (rev 1303) +++ sqlalchemy/branches/schema/lib/sqlalchemy/mapping/unitofwork.py 2006-04-20 18:04:16 UTC (rev 1304) </span><span class="lines">@@ -53,7 +53,7 @@ </span><span class="cx"> del sess.deleted[item] </span><span class="cx"> sess.modified_lists.append(self) </span><span class="cx"> if self.deleteremoved and isdelete: </span><del>- sess.register_deleted(item) </del><ins>+ sess._register_deleted(item) </ins><span class="cx"> def append(self, item, _mapper_nohistory = False): </span><span class="cx"> if _mapper_nohistory: </span><span class="cx"> self.append_nohistory(item) </span><span class="lines">@@ -69,9 +69,9 @@ </span><span class="cx"> sess = get_session(obj, raiseerror=False) </span><span class="cx"> if sess is not None: </span><span class="cx"> if hasattr(obj, '_instance_key'): </span><del>- sess.register_dirty(obj) </del><ins>+ sess._register_dirty(obj) </ins><span class="cx"> else: </span><del>- sess.register_new(obj) </del><ins>+ sess._register_new(obj) </ins><span class="cx"> </span><span class="cx"> def create_prop(self, class_, key, uselist, callable_, **kwargs): </span><span class="cx"> return UOWProperty(class_, self, key, uselist, callable_, **kwargs) </span></span></pre></div> <a id="sqlalchemybranchesschematestobjectstorepy"></a> <div class="modfile"><h4>Modified: sqlalchemy/branches/schema/test/objectstore.py (1303 => 1304)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/schema/test/objectstore.py 2006-04-20 02:30:21 UTC (rev 1303) +++ sqlalchemy/branches/schema/test/objectstore.py 2006-04-20 18:04:16 UTC (rev 1304) </span><span class="lines">@@ -43,7 +43,7 @@ </span><span class="cx"> self.assert_result([u], data[0], *data[1:]) </span><span class="cx"> </span><span class="cx"> self.echo(repr(u.addresses)) </span><del>- objectstore.get_session().rollback_object(u) </del><ins>+ objectstore.get_session().uow.rollback_object(u) </ins><span class="cx"> data = [User, </span><span class="cx"> {'user_name' : None, </span><span class="cx"> 'addresses' : (Address, []) </span><span class="lines">@@ -62,7 +62,7 @@ </span><span class="cx"> </span><span class="cx"> u = User() </span><span class="cx"> a = Address() </span><del>- s.register_new(u, a) </del><ins>+ s.add(u, a) </ins><span class="cx"> a.user = u </span><span class="cx"> #print repr(a.__class__._attribute_manager.get_history(a, 'user').added_items()) </span><span class="cx"> #print repr(u.addresses.added_items()) </span><span class="lines">@@ -421,7 +421,7 @@ </span><span class="cx"> u2 = User() </span><span class="cx"> u2.user_name = 'savetester2' </span><span class="cx"> </span><del>- objectstore.get_session().register_new(u) </del><ins>+ objectstore.get_session().add(u) </ins><span class="cx"> </span><span class="cx"> objectstore.get_session().flush(u) </span><span class="cx"> objectstore.get_session().flush() </span><span class="lines">@@ -549,7 +549,7 @@ </span><span class="cx"> u.address.email_address = 'myo...@fo...' </span><span class="cx"> objectstore.get_session().flush() </span><span class="cx"> self.echo("\n\n\n") </span><del>- objectstore.get_session().register_deleted(u) </del><ins>+ objectstore.get_session().delete(u) </ins><span class="cx"> objectstore.get_session().flush() </span><span class="cx"> self.assert_(a.address_id is not None and a.user_id is None and not objectstore.get_session().identity_map.has_key(u._instance_key) and objectstore.get_session().identity_map.has_key(a._instance_key)) </span><span class="cx"> </span><span class="lines">@@ -613,8 +613,7 @@ </span><span class="cx"> self.assert_result(l, data[0], *data[1:]) </span><span class="cx"> </span><span class="cx"> self.echo("\n\n\n") </span><del>- objectstore.get_session().register_deleted(l[0]) - objectstore.get_session().register_deleted(l[2]) </del><ins>+ objectstore.get_session().delete(l[0], l[2]) </ins><span class="cx"> objectstore.flush() </span><span class="cx"> return </span><span class="cx"> res = self.capture_exec(db, lambda: objectstore.get_session().flush()) </span></span></pre> </div> </div> </body> </html> |
From: <co...@sq...> - 2006-04-20 02:30: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>[1303] sqlalchemy/branches/schema/lib/sqlalchemy/mapping/mapper.py: factored add_property into one method, added add_properties</title> </head> <body> <div id="msg"> <dl> <dt>Revision</dt> <dd>1303</dd> <dt>Author</dt> <dd>zzzeek</dd> <dt>Date</dt> <dd>2006-04-19 21:30:21 -0500 (Wed, 19 Apr 2006)</dd> </dl> <h3>Log Message</h3> <pre>factored add_property into one method, added add_properties</pre> <h3>Modified Paths</h3> <ul> <li><a href="#sqlalchemybranchesschemalibsqlalchemymappingmapperpy">sqlalchemy/branches/schema/lib/sqlalchemy/mapping/mapper.py</a></li> </ul> </div> <div id="patch"> <h3>Diff</h3> <a id="sqlalchemybranchesschemalibsqlalchemymappingmapperpy"></a> <div class="modfile"><h4>Modified: sqlalchemy/branches/schema/lib/sqlalchemy/mapping/mapper.py (1302 => 1303)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/schema/lib/sqlalchemy/mapping/mapper.py 2006-04-20 01:49:31 UTC (rev 1302) +++ sqlalchemy/branches/schema/lib/sqlalchemy/mapping/mapper.py 2006-04-20 02:30:21 UTC (rev 1303) </span><span class="lines">@@ -158,25 +158,7 @@ </span><span class="cx"> # load custom properties </span><span class="cx"> if properties is not None: </span><span class="cx"> for key, prop in properties.iteritems(): </span><del>- if sql.is_column(prop): - try: - prop = self.table._get_col_by_original(prop) - except KeyError: - raise ArgumentError("Column '%s' is not represented in mapper's table" % prop._label) - self.columns[key] = prop - prop = ColumnProperty(prop) - elif isinstance(prop, list) and sql.is_column(prop[0]): - try: - prop = [self.table._get_col_by_original(p) for p in prop] - except KeyError, e: - raise ArgumentError("Column '%s' is not represented in mapper's table" % e.args[0]) - self.columns[key] = prop[0] - prop = ColumnProperty(*prop) - self.props[key] = prop - if isinstance(prop, ColumnProperty): - for col in prop.columns: - proplist = self.columntoproperty.setdefault(col.original, []) - proplist.append(prop) </del><ins>+ self.add_property(key, prop, False) </ins><span class="cx"> </span><span class="cx"> # load properties from the main table object, </span><span class="cx"> # not overriding those set up in the 'properties' argument </span><span class="lines">@@ -299,22 +281,41 @@ </span><span class="cx"> </span><span class="cx"> def select_text(self, text, **params): </span><span class="cx"> return self.query.select_text(text, **params) </span><ins>+ + def add_properties(self, dict_of_properties): + """adds the given dictionary of properties to this mapper, using add_property.""" + for key, value in dict_of_properties.iteritems(): + self.add_property(key, value, True) </ins><span class="cx"> </span><del>- def add_property(self, key, prop): </del><ins>+ def add_property(self, key, prop, init=True): </ins><span class="cx"> """adds an additional property to this mapper. this is the same as if it were </span><span class="cx"> specified within the 'properties' argument to the constructor. if the named </span><span class="cx"> property already exists, this will replace it. Useful for </span><span class="cx"> circular relationships, or overriding the parameters of auto-generated properties </span><span class="cx"> such as backreferences.""" </span><ins>+ </ins><span class="cx"> if sql.is_column(prop): </span><ins>+ try: + prop = self.table._get_col_by_original(prop) + except KeyError: + raise ArgumentError("Column '%s' is not represented in mapper's table" % prop._label) </ins><span class="cx"> self.columns[key] = prop </span><span class="cx"> prop = ColumnProperty(prop) </span><ins>+ elif isinstance(prop, list) and sql.is_column(prop[0]): + try: + prop = [self.table._get_col_by_original(p) for p in prop] + except KeyError, e: + raise ArgumentError("Column '%s' is not represented in mapper's table" % e.args[0]) + self.columns[key] = prop[0] + prop = ColumnProperty(*prop) </ins><span class="cx"> self.props[key] = prop </span><span class="cx"> if isinstance(prop, ColumnProperty): </span><span class="cx"> for col in prop.columns: </span><span class="cx"> proplist = self.columntoproperty.setdefault(col.original, []) </span><span class="cx"> proplist.append(prop) </span><del>- prop.init(key, self) </del><ins>+ + if init: + prop.init(key, self) </ins><span class="cx"> </span><span class="cx"> def __str__(self): </span><span class="cx"> return "Mapper|" + self.class_.__name__ + "|" + (self.entity_name is not None and "/%s" % self.entity_name or "") + self.primarytable.name </span></span></pre> </div> </div> </body> </html> |
From: <co...@sq...> - 2006-04-20 01:49: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>[1302] sqlalchemy/branches/schema/test: added non_primary flag to mapper, so you have to explicitly say "i want a non-primary mapper"</title> </head> <body> <div id="msg"> <dl> <dt>Revision</dt> <dd>1302</dd> <dt>Author</dt> <dd>zzzeek</dd> <dt>Date</dt> <dd>2006-04-19 20:49:31 -0500 (Wed, 19 Apr 2006)</dd> </dl> <h3>Log Message</h3> <pre>added non_primary flag to mapper, so you have to explicitly say "i want a non-primary mapper"</pre> <h3>Modified Paths</h3> <ul> <li><a href="#sqlalchemybranchesschemalibsqlalchemymappingmapperpy">sqlalchemy/branches/schema/lib/sqlalchemy/mapping/mapper.py</a></li> <li><a href="#sqlalchemybranchesschemalibsqlalchemymappingpropertiespy">sqlalchemy/branches/schema/lib/sqlalchemy/mapping/properties.py</a></li> <li><a href="#sqlalchemybranchesschemalibsqlalchemymappingunitofworkpy">sqlalchemy/branches/schema/lib/sqlalchemy/mapping/unitofwork.py</a></li> <li><a href="#sqlalchemybranchesschematestmapperpy">sqlalchemy/branches/schema/test/mapper.py</a></li> <li><a href="#sqlalchemybranchesschematestobjectstorepy">sqlalchemy/branches/schema/test/objectstore.py</a></li> </ul> </div> <div id="patch"> <h3>Diff</h3> <a id="sqlalchemybranchesschemalibsqlalchemymappingmapperpy"></a> <div class="modfile"><h4>Modified: sqlalchemy/branches/schema/lib/sqlalchemy/mapping/mapper.py (1301 => 1302)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/schema/lib/sqlalchemy/mapping/mapper.py 2006-04-19 23:07:23 UTC (rev 1301) +++ sqlalchemy/branches/schema/lib/sqlalchemy/mapping/mapper.py 2006-04-20 01:49:31 UTC (rev 1302) </span><span class="lines">@@ -41,6 +41,7 @@ </span><span class="cx"> properties = None, </span><span class="cx"> primary_key = None, </span><span class="cx"> is_primary = False, </span><ins>+ non_primary = False, </ins><span class="cx"> inherits = None, </span><span class="cx"> inherit_condition = None, </span><span class="cx"> extension = None, </span><span class="lines">@@ -69,6 +70,7 @@ </span><span class="cx"> self.entity_name = entity_name </span><span class="cx"> self.class_key = ClassKey(class_, entity_name) </span><span class="cx"> self.is_primary = is_primary </span><ins>+ self.non_primary = non_primary </ins><span class="cx"> self.order_by = order_by </span><span class="cx"> self._options = {} </span><span class="cx"> self.always_refresh = always_refresh </span><span class="lines">@@ -210,7 +212,9 @@ </span><span class="cx"> if not mapper_registry.has_key(self.class_key) or self.is_primary or (inherits is not None and inherits._is_primary_mapper()): </span><span class="cx"> objectstore.global_attributes.reset_class_managed(self.class_) </span><span class="cx"> self._init_class() </span><del>- </del><ins>+ elif not non_primary: + raise ArgumentError("Class '%s' already has a primary mapper defined. Use is_primary=True to assign a new primary mapper to the class, or use non_primary=True to create a non primary Mapper" % self.class_) + </ins><span class="cx"> if inherits is not None: </span><span class="cx"> for key, prop in inherits.props.iteritems(): </span><span class="cx"> if not self.props.has_key(key): </span></span></pre></div> <a id="sqlalchemybranchesschemalibsqlalchemymappingpropertiespy"></a> <div class="modfile"><h4>Modified: sqlalchemy/branches/schema/lib/sqlalchemy/mapping/properties.py (1301 => 1302)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/schema/lib/sqlalchemy/mapping/properties.py 2006-04-19 23:07:23 UTC (rev 1301) +++ sqlalchemy/branches/schema/lib/sqlalchemy/mapping/properties.py 2006-04-20 01:49:31 UTC (rev 1302) </span><span class="lines">@@ -220,7 +220,7 @@ </span><span class="cx"> if self.backref is not None: </span><span class="cx"> self.backref.compile(self) </span><span class="cx"> elif not objectstore.global_attributes.is_class_managed(parent.class_, key): </span><del>- raise ArgumentError("Non-primary property created for attribute '%s' on class '%s', but that attribute is not managed! Insure that the primary mapper for this class defines this property" % (key, parent.class_.__name__)) </del><ins>+ raise ArgumentError("Attempting to assign a new relation '%s' to a non-primary mapper on class '%s'. New relations can only be added to the primary mapper, i.e. the very first mapper created for class '%s' " % (key, parent.class_.__name__, parent.class_.__name__)) </ins><span class="cx"> </span><span class="cx"> self.do_init_subclass(key, parent) </span><span class="cx"> </span></span></pre></div> <a id="sqlalchemybranchesschemalibsqlalchemymappingunitofworkpy"></a> <div class="modfile"><h4>Modified: sqlalchemy/branches/schema/lib/sqlalchemy/mapping/unitofwork.py (1301 => 1302)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/schema/lib/sqlalchemy/mapping/unitofwork.py 2006-04-19 23:07:23 UTC (rev 1301) +++ sqlalchemy/branches/schema/lib/sqlalchemy/mapping/unitofwork.py 2006-04-20 01:49:31 UTC (rev 1302) </span><span class="lines">@@ -569,7 +569,11 @@ </span><span class="cx"> of its object list contain dependencies on each other. </span><span class="cx"> </span><span class="cx"> this is not the normal case; this logic only kicks in when something like </span><del>- a hierarchical tree is being represented.""" </del><ins>+ a hierarchical tree is being represented. + + TODO: dont understand this code ? well neither do I ! it takes me + an hour to re-understand this code completely, which is definitely an issue. + """ </ins><span class="cx"> </span><span class="cx"> allobjects = [] </span><span class="cx"> for task in cycles: </span><span class="lines">@@ -584,6 +588,7 @@ </span><span class="cx"> # get put here </span><span class="cx"> extradeplist = [] </span><span class="cx"> </span><ins>+ # this creates a map of UOWTasks mapped to individual objects. </ins><span class="cx"> def get_object_task(parent, obj): </span><span class="cx"> try: </span><span class="cx"> return objecttotask[obj] </span><span class="lines">@@ -593,6 +598,8 @@ </span><span class="cx"> objecttotask[obj] = t </span><span class="cx"> return t </span><span class="cx"> </span><ins>+ # this creates a map of UOWTasks mapped to a particular object + # and a particular dependency processor. </ins><span class="cx"> dependencies = {} </span><span class="cx"> def get_dependency_task(obj, depprocessor): </span><span class="cx"> try: </span></span></pre></div> <a id="sqlalchemybranchesschematestmapperpy"></a> <div class="modfile"><h4>Modified: sqlalchemy/branches/schema/test/mapper.py (1301 => 1302)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/schema/test/mapper.py 2006-04-19 23:07:23 UTC (rev 1301) +++ sqlalchemy/branches/schema/test/mapper.py 2006-04-20 01:49:31 UTC (rev 1302) </span><span class="lines">@@ -257,12 +257,14 @@ </span><span class="cx"> self.assert_(False, "should have raised ArgumentError") </span><span class="cx"> except ArgumentError, e: </span><span class="cx"> self.assert_(True) </span><del>- </del><ins>+ + clear_mappers() </ins><span class="cx"> # assert that allow_column_override cancels the error </span><span class="cx"> m = mapper(User, users, properties = { </span><span class="cx"> 'user_name' : relation(mapper(Address, addresses)) </span><span class="cx"> }, allow_column_override=True) </span><span class="cx"> </span><ins>+ clear_mappers() </ins><span class="cx"> # assert that the column being named else where also cancels the error </span><span class="cx"> m = mapper(User, users, properties = { </span><span class="cx"> 'user_name' : relation(mapper(Address, addresses)), </span><span class="lines">@@ -563,8 +565,8 @@ </span><span class="cx"> closedorders = alias(orders, 'closedorders') </span><span class="cx"> m = mapper(User, users, properties = dict( </span><span class="cx"> addresses = relation(mapper(Address, addresses), lazy = False), </span><del>- open_orders = relation(mapper(Order, openorders), primaryjoin = and_(openorders.c.isopen == 1, users.c.user_id==openorders.c.user_id), lazy = True), - closed_orders = relation(mapper(Order, closedorders), primaryjoin = and_(closedorders.c.isopen == 0, users.c.user_id==closedorders.c.user_id), lazy = True) </del><ins>+ open_orders = relation(mapper(Order, openorders, non_primary=True), primaryjoin = and_(openorders.c.isopen == 1, users.c.user_id==openorders.c.user_id), lazy = True), + closed_orders = relation(mapper(Order, closedorders,non_primary=True), primaryjoin = and_(closedorders.c.isopen == 0, users.c.user_id==closedorders.c.user_id), lazy = True) </ins><span class="cx"> )) </span><span class="cx"> l = m.select() </span><span class="cx"> self.assert_result(l, User, </span><span class="lines">@@ -746,8 +748,8 @@ </span><span class="cx"> ordermapper = mapper(Order, orders) </span><span class="cx"> m = mapper(User, users, properties = dict( </span><span class="cx"> addresses = relation(mapper(Address, addresses), lazy = False), </span><del>- open_orders = relation(mapper(Order, openorders), primaryjoin = and_(openorders.c.isopen == 1, users.c.user_id==openorders.c.user_id), lazy = False), - closed_orders = relation(mapper(Order, closedorders), primaryjoin = and_(closedorders.c.isopen == 0, users.c.user_id==closedorders.c.user_id), lazy = False) </del><ins>+ open_orders = relation(mapper(Order, openorders, non_primary=True), primaryjoin = and_(openorders.c.isopen == 1, users.c.user_id==openorders.c.user_id), lazy = False), + closed_orders = relation(mapper(Order, closedorders, non_primary=True), primaryjoin = and_(closedorders.c.isopen == 0, users.c.user_id==closedorders.c.user_id), lazy = False) </ins><span class="cx"> )) </span><span class="cx"> l = m.select() </span><span class="cx"> self.assert_result(l, User, </span></span></pre></div> <a id="sqlalchemybranchesschematestobjectstorepy"></a> <div class="modfile"><h4>Modified: sqlalchemy/branches/schema/test/objectstore.py (1301 => 1302)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/schema/test/objectstore.py 2006-04-19 23:07:23 UTC (rev 1301) +++ sqlalchemy/branches/schema/test/objectstore.py 2006-04-20 01:49:31 UTC (rev 1302) </span><span class="lines">@@ -870,12 +870,13 @@ </span><span class="cx"> def testmanytomany(self): </span><span class="cx"> items = orderitems </span><span class="cx"> </span><ins>+ keywordmapper = mapper(Keyword, keywords) + </ins><span class="cx"> items.select().execute() </span><span class="cx"> m = mapper(Item, items, properties = dict( </span><del>- keywords = relation(mapper(Keyword, keywords), itemkeywords, lazy = False), </del><ins>+ keywords = relation(keywordmapper, itemkeywords, lazy = False), </ins><span class="cx"> )) </span><span class="cx"> </span><del>- keywordmapper = mapper(Keyword, keywords) </del><span class="cx"> </span><span class="cx"> data = [Item, </span><span class="cx"> {'item_name': 'mm_item1', 'keywords' : (Keyword,[{'name': 'big'},{'name': 'green'}, {'name': 'purple'},{'name': 'round'}])}, </span><span class="lines">@@ -971,7 +972,7 @@ </span><span class="cx"> # the reorganization of mapper construction affected this, but was fixed again </span><span class="cx"> m = mapper(Item, items, properties = dict( </span><span class="cx"> keywords = relation(mapper(IKAssociation, itemkeywords, properties = dict( </span><del>- keyword = relation(mapper(Keyword, keywords), lazy = False, uselist = False) </del><ins>+ keyword = relation(mapper(Keyword, keywords, non_primary=True), lazy = False, uselist = False) </ins><span class="cx"> ), primary_key = [itemkeywords.c.item_id, itemkeywords.c.keyword_id]), </span><span class="cx"> lazy = False) </span><span class="cx"> )) </span></span></pre> </div> </div> </body> </html> |
From: <co...@sq...> - 2006-04-19 23:07:37
|
<!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>[1301] sqlalchemy/branches/schema/doc/build: doc stuff</title> </head> <body> <div id="msg"> <dl> <dt>Revision</dt> <dd>1301</dd> <dt>Author</dt> <dd>zzzeek</dd> <dt>Date</dt> <dd>2006-04-19 18:07:23 -0500 (Wed, 19 Apr 2006)</dd> </dl> <h3>Log Message</h3> <pre>doc stuff</pre> <h3>Modified Paths</h3> <ul> <li><a href="#sqlalchemybranchesschemadocbuildcontentsqlconstructiontxt">sqlalchemy/branches/schema/doc/build/content/sqlconstruction.txt</a></li> <li><a href="#sqlalchemybranchesschemadocbuildcontenttutorialtxt">sqlalchemy/branches/schema/doc/build/content/tutorial.txt</a></li> <li><a href="#sqlalchemybranchesschemadocbuildtestdocspy">sqlalchemy/branches/schema/doc/build/testdocs.py</a></li> <li><a href="#sqlalchemybranchesschemalibsqlalchemyenginebasepy">sqlalchemy/branches/schema/lib/sqlalchemy/engine/base.py</a></li> <li><a href="#sqlalchemybranchesschemalibsqlalchemyenginedefaultpy">sqlalchemy/branches/schema/lib/sqlalchemy/engine/default.py</a></li> <li><a href="#sqlalchemybranchesschemalibsqlalchemyschemapy">sqlalchemy/branches/schema/lib/sqlalchemy/schema.py</a></li> </ul> </div> <div id="patch"> <h3>Diff</h3> <a id="sqlalchemybranchesschemadocbuildcontentsqlconstructiontxt"></a> <div class="modfile"><h4>Modified: sqlalchemy/branches/schema/doc/build/content/sqlconstruction.txt (1300 => 1301)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/schema/doc/build/content/sqlconstruction.txt 2006-04-19 20:41:27 UTC (rev 1300) +++ sqlalchemy/branches/schema/doc/build/content/sqlconstruction.txt 2006-04-19 23:07:23 UTC (rev 1301) </span><span class="lines">@@ -1,4 +1,4 @@ </span><del>-Constructing SQL Queries via Python Expressions </del><ins>+Constructing SQL Queries via Python Expressions {@name=sqlconstruction} </ins><span class="cx"> =============================================== </span><span class="cx"> </span><span class="cx"> *Note:* This section describes how to use SQLAlchemy to construct SQL queries and receive result sets. It does *not* cover the object relational mapping capabilities of SQLAlchemy; that is covered later on in [datamapping](rel:datamapping). However, both areas of functionality work similarly in how selection criterion is constructed, so if you are interested just in ORM, you should probably skim through basic [sql_select_whereclause](rel:sql_select_whereclause) construction before moving on. </span></span></pre></div> <a id="sqlalchemybranchesschemadocbuildcontenttutorialtxt"></a> <div class="modfile"><h4>Modified: sqlalchemy/branches/schema/doc/build/content/tutorial.txt (1300 => 1301)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/schema/doc/build/content/tutorial.txt 2006-04-19 20:41:27 UTC (rev 1300) +++ sqlalchemy/branches/schema/doc/build/content/tutorial.txt 2006-04-19 23:07:23 UTC (rev 1301) </span><span class="lines">@@ -44,6 +44,7 @@ </span><span class="cx"> </span><span class="cx"> SQLAlchemy provides the entire namespace of everything you'll need under the module name `sqlalchemy`. For the purposes of this tutorial, we will import its full list of symbols into our own local namespace. We also will be using a *mod* that provides access to thread-managed connection and session objects, which will greatly simplifies our code. A *mod* is a module that augments the core functionality of SQLAlchemy with additional functionality, and only needs to be imported once within an application. </span><span class="cx"> </span><ins>+ {python} </ins><span class="cx"> >>> from sqlalchemy import * </span><span class="cx"> >>> import sqlalchemy.mods.threadlocal </span><span class="cx"> </span><span class="lines">@@ -51,25 +52,48 @@ </span><span class="cx"> </span><span class="cx"> After our imports, the next thing we need is a handle to the desired database, represented by an `Engine` 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". </span><span class="cx"> </span><ins>+ {python} </ins><span class="cx"> >>> db = create_engine('sqlite:///tutorial.db') </span><span class="cx"> </span><span class="cx"> </span><span class="cx"> For full information on creating database engines, including those for SQLite and others, see [dbengine](rel:dbengine). </span><span class="cx"> </span><del>-### Creating a Table {@name=table} </del><ins>+Working with Database Objects {@name=schemasql} +----------------------------------------------- </ins><span class="cx"> </span><ins>+### Defining Metadata, Binding to Engines {@name=metadata} + </ins><span class="cx"> 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. </span><span class="cx"> </span><span class="cx"> Firstly, your Tables have to belong to a collection called `MetaData`. We will create a handy form of `MetaData` that automatically connects to our `Engine` (connecting a schema object to an Engine is called *binding*): </span><span class="cx"> </span><ins>+ {python} </ins><span class="cx"> >>> metadata = BoundMetaData(db) </span><span class="cx"> </span><span class="cx"> An equivalent operation is to create the `BoundMetaData` object directly with an Engine URL, which calls the `create_engine` call for us: </span><span class="cx"> </span><ins>+ {python} </ins><span class="cx"> >>> metadata = BoundMetaData('sqlite:///tutorial.db') </span><span class="cx"> </span><ins>+When creating MetaData objects that are bound to Engines, all objects within the MetaData or which are derived from it are similarly bound to that engine; this includes tables and SQL statement constructs. A SQL construct that is bound to an Engine supports "connectionless execution", that is, each object knows how to retrieve connections and use them automatically for its own execution, without you having to worry about it. However, note that the "binding" of schema and SQL constructs to engines is **entirely optional**. SQLAlchemy includes full support for explicit Connections used with schema and SQL constructs that are entirely unbound to any Engine. + +For the purposes of this tutorial, we will stick with "bound" objects. There is also a more flexible "dynamic" metadata object that supports runtime binding to multiple engines: + + {python} + >>> dynamic = DynamicMetaData() # create a Dynamic metadata object + >>> dynamic.connect('sqlite:///:memory:') # connect it to SQLite + >>> dynamic.connect('postgres:///scott:tiger@localhost/mydb') # connect it to PostGres + + >>> myengine = create_engine('mysql:///127.0.0.1') + >>> dynamic.connect(myengine) # connect to an externally-defined engine + +The `DynamicMetaData` object binds to different engines on a thread local basis. This means that one thread of your application can be connected to one database, while another is connected to a different database. The `DynamicMetaData` object also keeps a reference to each bound engine internally, so that each connection string is only initialized once. + +### Creating a Table {@name=table} + </ins><span class="cx"> With `metadata` as our established home for tables, lets make a Table for it: </span><span class="cx"> </span><ins>+ {python} </ins><span class="cx"> >>> users = Table('users', metadata, </span><span class="cx"> ... Column('user_id', Integer, primary_key=True), </span><span class="cx"> ... Column('user_name', String(40)), </span><span class="lines">@@ -78,6 +102,7 @@ </span><span class="cx"> </span><span class="cx"> 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, by setting the `echo` flag on the `Engine` associated with our `BoundMetaData`: </span><span class="cx"> </span><ins>+ {python} </ins><span class="cx"> >>> metadata.engine.echo = True </span><span class="cx"> >>> users.create() # doctest:+ELLIPSIS,+NORMALIZE_WHITESPACE </span><span class="cx"> CREATE TABLE users( </span><span class="lines">@@ -86,10 +111,10 @@ </span><span class="cx"> password VARCHAR(80) </span><span class="cx"> ) </span><span class="cx"> ... </span><del>- >>> metadata.engine.echo = False # you can skip this if you want to keep logging SQL statements </del><span class="cx"> </span><span class="cx"> 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: </span><span class="cx"> </span><ins>+ {python} </ins><span class="cx"> >>> users = Table('users', metadata, autoload=True) </span><span class="cx"> >>> list(users.columns)[0].name </span><span class="cx"> 'user_id' </span><span class="lines">@@ -98,41 +123,58 @@ </span><span class="cx"> </span><span class="cx"> ### Inserting Rows </span><span class="cx"> </span><del>-Inserting is achieved via the `insert()` method, which defines a *clause object* representing an INSERT statement: </del><ins>+Inserting is achieved via the `insert()` method, which defines a *clause object* (known as a `ClauseElement`) representing an INSERT statement: </ins><span class="cx"> </span><ins>+ {python} </ins><span class="cx"> >>> i = users.insert() </span><span class="cx"> >>> i # doctest:+ELLIPSIS </span><span class="cx"> <sqlalchemy.sql.Insert object at 0x...> </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>-The `execute()` method of the clause object executes the statement at the database level: </del><ins>+Since we created this insert statement object from the `users` table which is bound to our `Engine`, the statement itself is also bound to the `Engine`, and supports executing itself. The `execute()` method of the clause object will *compile* the object into a string according to the underlying *dialect* of the Engine to which the statement is bound, and then executes the resulting statement. </ins><span class="cx"> </span><del>- >>> for name in ['Tom', 'Dick', 'Harry']: # doctest:+ELLIPSIS </del><ins>+ {python} + >>> for name in ['Tom', 'Dick', 'Harry']: # doctest:+ELLIPSIS,+NORMALIZE_WHITESPACE </ins><span class="cx"> ... i.execute(user_name = name) </span><ins>+ INSERT INTO users (user_name) VALUES (?) + ['Tom'] </ins><span class="cx"> <sqlalchemy.engine.base.ResultProxy instance at 0x...> </span><del>- ... </del><ins>+ INSERT INTO users (user_name) VALUES (?) + ['Dick'] + <sqlalchemy.engine.base.ResultProxy instance at 0x...> + INSERT INTO users (user_name) VALUES (?) + ['Harry'] + <sqlalchemy.engine.base.ResultProxy instance at 0x...> + </ins><span class="cx"> >>> i.execute(user_name = 'Mary', password = 'secure') # doctest:+ELLIPSIS </span><ins>+ INSERT INTO users (user_name, password) VALUES (?, ?) + ['Mary', 'secure'] </ins><span class="cx"> <sqlalchemy.engine.base.ResultProxy instance at 0x...> </span><span class="cx"> </span><del>-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. </del><ins>+Note that the `VALUES` clause of each `INSERT` statement was automatically adjusted to correspond to the parameters sent to the `execute()` method. This is because the compilation step of a `ClauseElement` takes into account not just the constructed SQL object and the specifics of the type of database being used, but the execution parameters sent along as well. </ins><span class="cx"> </span><del>-[SQL injection]: http://en.wikipedia.org/wiki/SQL_injection </del><ins>+When constructing clause objects, SQLAlchemy will bind all literal values into bind parameters. On the construction side, bind parameters are always treated as named parameters. At compilation time, SQLAlchemy will convert them their proper format, based on the paramstyle of the underlying DBAPI. This works equally well for all named and positional bind parameter formats described in the DBAPI specification. </ins><span class="cx"> </span><del>-Documentation on inserting: [sql_insert](rel:sql_insert). </del><ins>+Documentation on inserting: [sqlconstruction_insert](rel:sqlconstruction_insert). </ins><span class="cx"> </span><del>-### Constructing Queries </del><ins>+### Selecting </ins><span class="cx"> </span><span class="cx"> 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: </span><span class="cx"> </span><ins>+ {python} </ins><span class="cx"> >>> s = users.select() </span><span class="cx"> >>> print s </span><span class="cx"> SELECT users.user_id, users.user_name, users.password </span><span class="cx"> FROM users </span><span class="cx"> >>> r = s.execute() </span><del>- </del><ins>+ SELECT users.user_id, users.user_name, users.password + FROM users + [] + </ins><span class="cx"> This time, we won't ignore the return value of `execute()`: </span><span class="cx"> </span><ins>+ {python} </ins><span class="cx"> >>> r # doctest:+ELLIPSIS </span><span class="cx"> <sqlalchemy.engine.base.ResultProxy instance at 0x...> </span><span class="cx"> >>> r.keys </span><span class="lines">@@ -143,16 +185,44 @@ </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>-Documentation on selecting: [sql_select](rel:sql_select). </del><ins>+Query criterion for the select can also be specified as regular Python expressions, using the column objects in the Table as a base: </ins><span class="cx"> </span><del>-### Related Table </del><ins>+ {python} + >>> r = users.select(users.c.user_name=='Harry').execute() + SELECT users.user_id, users.user_name, users.password + FROM users + WHERE users.user_name = ? + ['Harry'] + >>> r.fetchall() + [(3, u'Harry', None)] + +Pretty much the full range of standard SQL operations are supported as constructed Python expressions, including joins, ordering, grouping, functions, correlated subqueries, unions, etc. Documentation on selecting: [sqlconstruction_select](rel:sqlconstruction_select). </ins><span class="cx"> </span><del>-Main documentation: [sql](rel:sql). </del><ins>+### Table Relationships </ins><span class="cx"> </span><del>-### Fancier Querying {@name=fancyquery} </del><ins>+Lets create a second table, `email_addresses`, which references the `users` table. To define the relationship between the two tables, we will use the `ForeignKey` construct. We will also issue the `CREATE` statement for the table in one step: </ins><span class="cx"> </span><del>-Main documentation: [sql](rel:sql). </del><ins>+ {python} + >>> email_addresses = Table('email_addresses', metadata, + ... Column('address_id', Integer, primary_key=True), + ... Column('email_address', String(100), nullable=False), + ... Column('user_id', Integer, ForeignKey('users.user_id'))).create() # doctest:+ELLIPSIS,+NORMALIZE_WHITESPACE + CREATE TABLE email_addresses( + address_id INTEGER NOT NULL PRIMARY KEY, + email_address VARCHAR(100) NOT NULL, + user_id INTEGER REFERENCES users(user_id) + ) + ... </ins><span class="cx"> </span><ins>+With two related tables, we can now construct a join amongst them, like this: + + {python} + >>> r = users.join(email_addresses).select().execute() + SELECT users.user_id, users.user_name, users.password, email_addresses.address_id, email_addresses.email_address, email_addresses.user_id + FROM users JOIN email_addresses ON users.user_id = email_addresses.user_id + [] + + </ins><span class="cx"> ### Data Mapping {@name=mapping} </span><span class="cx"> </span><span class="cx"> Main documentation: [datamapping](rel:datamapping), [adv_datamapping](rel:adv_datamapping). </span></span></pre></div> <a id="sqlalchemybranchesschemadocbuildtestdocspy"></a> <div class="modfile"><h4>Modified: sqlalchemy/branches/schema/doc/build/testdocs.py (1300 => 1301)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/schema/doc/build/testdocs.py 2006-04-19 20:41:27 UTC (rev 1300) +++ sqlalchemy/branches/schema/doc/build/testdocs.py 2006-04-19 23:07:23 UTC (rev 1301) </span><span class="lines">@@ -46,16 +46,16 @@ </span><span class="cx"> </span><span class="cx"> return runner.failures, runner.tries </span><span class="cx"> </span><del>-def replace_file(s, oldfile, newfile): - engine = r"sqlite:///" + oldfile </del><ins>+def replace_file(s, newfile): + engine = r"'(sqlite|postgres|mysql):///.*'" </ins><span class="cx"> engine = re.compile(engine, re.MULTILINE) </span><del>- s, n = re.subn(engine, "sqlite:///" + newfile, s) </del><ins>+ s, n = re.subn(engine, "'sqlite:///" + newfile + "'", s) </ins><span class="cx"> if not n: </span><span class="cx"> raise ValueError("Couldn't find suitable create_engine call to replace '%s' in it" % oldfile) </span><span class="cx"> return s </span><span class="cx"> </span><span class="cx"> filename = 'content/tutorial.txt' </span><span class="cx"> s = open(filename).read() </span><del>-s = replace_file(s, 'tutorial.db', ':memory:') </del><ins>+s = replace_file(s, ':memory:') </ins><span class="cx"> teststring(s, filename) </span><span class="cx"> </span></span></pre></div> <a id="sqlalchemybranchesschemalibsqlalchemyenginebasepy"></a> <div class="modfile"><h4>Modified: sqlalchemy/branches/schema/lib/sqlalchemy/engine/base.py (1300 => 1301)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/schema/lib/sqlalchemy/engine/base.py 2006-04-19 20:41:27 UTC (rev 1300) +++ sqlalchemy/branches/schema/lib/sqlalchemy/engine/base.py 2006-04-19 23:07:23 UTC (rev 1301) </span><span class="lines">@@ -26,6 +26,11 @@ </span><span class="cx"> </span><span class="cx"> paramstyle - the paramstyle to be used (some DBAPIs support multiple paramstyles) </span><span class="cx"> </span><ins>+ supports_autoclose_results - usually True; if False, indicates that rows returned by fetchone() + might not be just plain tuples, and may be "live" proxy objects which still require the cursor + to be open in order to be read (such as pyPgSQL which has active filehandles for BLOBs). in that + case, an auto-closing ResultProxy cannot automatically close itself after results are consumed. + </ins><span class="cx"> convert_unicode - True if unicode conversion should be applied to all str types </span><span class="cx"> </span><span class="cx"> encoding - type of encoding to use for unicode, usually defaults to 'utf-8' </span><span class="lines">@@ -451,7 +456,7 @@ </span><span class="cx"> def close(self): </span><span class="cx"> if not self.closed: </span><span class="cx"> self.closed = True </span><del>- if self.connection.close_with_result: </del><ins>+ if self.connection.close_with_result and self.dialect.supports_autoclose_results: </ins><span class="cx"> self.connection.close() </span><span class="cx"> def _get_col(self, row, key): </span><span class="cx"> if isinstance(key, schema.Column) or isinstance(key, sql.ColumnElement): </span><span class="lines">@@ -504,6 +509,9 @@ </span><span class="cx"> if self.echo: self.engine.log(repr(row)) </span><span class="cx"> return RowProxy(self, row) </span><span class="cx"> else: </span><ins>+ # controversy! can we auto-close the cursor after results are consumed ? + # what if the returned rows are still hanging around, and are "live" objects + # and not just plain tuples ? </ins><span class="cx"> self.close() </span><span class="cx"> return None </span><span class="cx"> </span></span></pre></div> <a id="sqlalchemybranchesschemalibsqlalchemyenginedefaultpy"></a> <div class="modfile"><h4>Modified: sqlalchemy/branches/schema/lib/sqlalchemy/engine/default.py (1300 => 1301)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/schema/lib/sqlalchemy/engine/default.py 2006-04-19 20:41:27 UTC (rev 1300) +++ sqlalchemy/branches/schema/lib/sqlalchemy/engine/default.py 2006-04-19 23:07:23 UTC (rev 1301) </span><span class="lines">@@ -40,6 +40,7 @@ </span><span class="cx"> def __init__(self, default_ordering=False, convert_unicode=False, encoding='utf-8', **kwargs): </span><span class="cx"> self.default_ordering=default_ordering </span><span class="cx"> self.convert_unicode = convert_unicode </span><ins>+ self.supports_autoclose_results = True </ins><span class="cx"> self.encoding = encoding </span><span class="cx"> self.positional = False </span><span class="cx"> self.paramstyle = 'named' </span></span></pre></div> <a id="sqlalchemybranchesschemalibsqlalchemyschemapy"></a> <div class="modfile"><h4>Modified: sqlalchemy/branches/schema/lib/sqlalchemy/schema.py (1300 => 1301)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/schema/lib/sqlalchemy/schema.py 2006-04-19 20:41:27 UTC (rev 1300) +++ sqlalchemy/branches/schema/lib/sqlalchemy/schema.py 2006-04-19 23:07:23 UTC (rev 1301) </span><span class="lines">@@ -183,7 +183,6 @@ </span><span class="cx"> def accept_schema_visitor(self, visitor): </span><span class="cx"> """traverses the given visitor across the Column objects inside this Table, </span><span class="cx"> then calls the visit_table method on the visitor.""" </span><del>- print "TABLE ACCEPT VISITOR, C IS", [c for c in self.columns] </del><span class="cx"> for c in self.columns: </span><span class="cx"> c.accept_schema_visitor(visitor) </span><span class="cx"> return visitor.visit_table(self) </span><span class="lines">@@ -627,9 +626,15 @@ </span><span class="cx"> self.__engines[engine_or_url] = e </span><span class="cx"> self.context._engine = e </span><span class="cx"> else: </span><ins>+ if not self.__engines.has_key(engine_or_url): + self.__engines[engine_or_url] = engine_or_url </ins><span class="cx"> self.context._engine = engine_or_url </span><span class="cx"> def is_bound(self): </span><span class="cx"> return s.context._engine is not None </span><ins>+ def dispose(self): + """disposes all Engines to which this DynamicMetaData has been connected.""" + for e in self.__engines.values(): + e.dispose() </ins><span class="cx"> engine=property(lambda s:s.context._engine) </span><span class="cx"> </span><span class="cx"> class SchemaVisitor(sql.ClauseVisitor): </span></span></pre> </div> </div> </body> </html> |
From: <co...@sq...> - 2006-04-19 20:41:41
|
<!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>[1300] sqlalchemy/branches/schema/doc/build/content: doc stuff</title> </head> <body> <div id="msg"> <dl> <dt>Revision</dt> <dd>1300</dd> <dt>Author</dt> <dd>zzzeek</dd> <dt>Date</dt> <dd>2006-04-19 15:41:27 -0500 (Wed, 19 Apr 2006)</dd> </dl> <h3>Log Message</h3> <pre>doc stuff</pre> <h3>Modified Paths</h3> <ul> <li><a href="#sqlalchemybranchesschemadocbuildcomponentspydocmyt">sqlalchemy/branches/schema/doc/build/components/pydoc.myt</a></li> <li><a href="#sqlalchemybranchesschemadocbuildcontentdocstringsmyt">sqlalchemy/branches/schema/doc/build/content/docstrings.myt</a></li> <li><a href="#sqlalchemybranchesschemalibsqlalchemyenginebasepy">sqlalchemy/branches/schema/lib/sqlalchemy/engine/base.py</a></li> <li><a href="#sqlalchemybranchesschemalibsqlalchemyenginestrategiespy">sqlalchemy/branches/schema/lib/sqlalchemy/engine/strategies.py</a></li> </ul> </div> <div id="patch"> <h3>Diff</h3> <a id="sqlalchemybranchesschemadocbuildcomponentspydocmyt"></a> <div class="modfile"><h4>Modified: sqlalchemy/branches/schema/doc/build/components/pydoc.myt (1299 => 1300)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/schema/doc/build/components/pydoc.myt 2006-04-19 20:02:27 UTC (rev 1299) +++ sqlalchemy/branches/schema/doc/build/components/pydoc.myt 2006-04-19 20:41:27 UTC (rev 1300) </span><span class="lines">@@ -1,5 +1,5 @@ </span><span class="cx"> <%global> </span><del>- import re, types </del><ins>+ import re, types, string </ins><span class="cx"> def format_paragraphs(text): </span><span class="cx"> return re.sub(r'([\w ])\n([\w ])', r'\1 \2', text or '', re.S) </span><span class="cx"> </%global> </span><span class="lines">@@ -60,7 +60,15 @@ </span><span class="cx"> if isclass: </span><span class="cx"> description = "Class " + name </span><span class="cx"> if hasattr(obj, '__mro__'): </span><del>- description += "(" + obj.__mro__[1].__name__ + ")" </del><ins>+ l = [] + mro = list(obj.__mro__[1:]) + mro.reverse() + for x in mro: + for y in x.__mro__[1:]: + if y in l: + del l[l.index(y)] + l.insert(0, x) + description += "(" + string.join([x.__name__ for x in l], ',') + ")" </ins><span class="cx"> else: </span><span class="cx"> description = "Module " + name </span><span class="cx"> </%init> </span></span></pre></div> <a id="sqlalchemybranchesschemadocbuildcontentdocstringsmyt"></a> <div class="modfile"><h4>Modified: sqlalchemy/branches/schema/doc/build/content/docstrings.myt (1299 => 1300)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/schema/doc/build/content/docstrings.myt 2006-04-19 20:02:27 UTC (rev 1299) +++ sqlalchemy/branches/schema/doc/build/content/docstrings.myt 2006-04-19 20:41:27 UTC (rev 1300) </span><span class="lines">@@ -4,22 +4,26 @@ </span><span class="cx"> <%init> </span><span class="cx"> import sqlalchemy.schema as schema </span><span class="cx"> import sqlalchemy.engine as engine </span><ins>+ import sqlalchemy.engine.strategies as strategies </ins><span class="cx"> import sqlalchemy.sql as sql </span><span class="cx"> import sqlalchemy.pool as pool </span><span class="cx"> import sqlalchemy.mapping as mapping </span><span class="cx"> import sqlalchemy.exceptions as exceptions </span><span class="cx"> import sqlalchemy.ext.proxy as proxy </span><ins>+ import sqlalchemy.mods.threadlocal as threadlocal </ins><span class="cx"> </%init> </span><span class="cx"> </span><span class="cx"> </span><ins>+<& pydoc.myt:obj_doc, obj=sql, classes=[sql.Engine, sql.AbstractDialect, sql.ClauseParameters, sql.Compiled, sql.ClauseElement, sql.TableClause, sql.ColumnClause] &> </ins><span class="cx"> <& pydoc.myt:obj_doc, obj=schema &> </span><del>-<& pydoc.myt:obj_doc, obj=engine, classes=[engine.SQLSession, engine.SQLEngine, engine.ResultProxy, engine.RowProxy] &> -<& pydoc.myt:obj_doc, obj=sql, classes=[sql.ClauseParameters, sql.Compiled, sql.ClauseElement, sql.TableClause, sql.ColumnClause] &> -<& pydoc.myt:obj_doc, obj=pool, classes=[pool.DBProxy, pool.Pool, pool.QueuePool, pool.SingletonThreadPool] &> </del><ins>+<& pydoc.myt:obj_doc, obj=engine, classes=[engine.ComposedSQLEngine, engine.Connection, engine.Transaction, engine.Dialect, engine.ConnectionProvider, engine.ExecutionContext, engine.ResultProxy, engine.RowProxy] &> +<& pydoc.myt:obj_doc, obj=strategies &> </ins><span class="cx"> <& pydoc.myt:obj_doc, obj=mapping, classes=[mapping.Mapper, mapping.MapperExtension] &> </span><span class="cx"> <& pydoc.myt:obj_doc, obj=mapping.query, classes=[mapping.query.Query] &> </span><del>-<& pydoc.myt:obj_doc, obj=mapping.objectstore, classes=[mapping.objectstore.Session, mapping.objectstore.Session.SessionTrans] &> </del><ins>+<& pydoc.myt:obj_doc, obj=mapping.objectstore, classes=[mapping.objectstore.Session, mapping.objectstore.SessionTransaction] &> +<& pydoc.myt:obj_doc, obj=threadlocal &> </ins><span class="cx"> <& pydoc.myt:obj_doc, obj=exceptions &> </span><ins>+<& pydoc.myt:obj_doc, obj=pool, classes=[pool.DBProxy, pool.Pool, pool.QueuePool, pool.SingletonThreadPool] &> </ins><span class="cx"> <& pydoc.myt:obj_doc, obj=proxy &> </span><span class="cx"> </span><span class="cx"> </&> </span></span></pre></div> <a id="sqlalchemybranchesschemalibsqlalchemyenginebasepy"></a> <div class="modfile"><h4>Modified: sqlalchemy/branches/schema/lib/sqlalchemy/engine/base.py (1299 => 1300)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/schema/lib/sqlalchemy/engine/base.py 2006-04-19 20:02:27 UTC (rev 1299) +++ sqlalchemy/branches/schema/lib/sqlalchemy/engine/base.py 2006-04-19 20:41:27 UTC (rev 1300) </span><span class="lines">@@ -21,10 +21,13 @@ </span><span class="cx"> and a general consistentization of the behavior of various DBAPIs. </span><span class="cx"> </span><span class="cx"> The Dialect should also implement the following two attributes: </span><del>- </del><ins>+ </ins><span class="cx"> positional - True if the paramstyle for this Dialect is positional </span><ins>+ </ins><span class="cx"> paramstyle - the paramstyle to be used (some DBAPIs support multiple paramstyles) </span><ins>+ </ins><span class="cx"> convert_unicode - True if unicode conversion should be applied to all str types </span><ins>+ </ins><span class="cx"> encoding - type of encoding to use for unicode, usually defaults to 'utf-8' </span><span class="cx"> """ </span><span class="cx"> def create_connect_args(self, opts): </span><span class="lines">@@ -143,7 +146,9 @@ </span><span class="cx"> raise NotImplementedError() </span><span class="cx"> </span><span class="cx"> class Connection(object): </span><del>- """wraps a DBAPI connection to provide execution support for ClauseElements""" </del><ins>+ """represents a single DBAPI connection returned from the underlying connection pool. Provides + execution support for string-based SQL statements as well as ClauseElement, Compiled and DefaultGenerator objects. + provides a begin method to return Transaction objects.""" </ins><span class="cx"> def __init__(self, engine, connection=None, close_with_result=False): </span><span class="cx"> self.engine = engine </span><span class="cx"> self.connection = connection or engine.raw_connection() </span></span></pre></div> <a id="sqlalchemybranchesschemalibsqlalchemyenginestrategiespy"></a> <div class="modfile"><h4>Modified: sqlalchemy/branches/schema/lib/sqlalchemy/engine/strategies.py (1299 => 1300)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/schema/lib/sqlalchemy/engine/strategies.py 2006-04-19 20:02:27 UTC (rev 1299) +++ sqlalchemy/branches/schema/lib/sqlalchemy/engine/strategies.py 2006-04-19 20:41:27 UTC (rev 1300) </span><span class="lines">@@ -5,7 +5,7 @@ </span><span class="cx"> this can be accomplished via a mod; see the sqlalchemy/mods package for details.""" </span><span class="cx"> </span><span class="cx"> import re </span><del>-from cgi import parse_qsl </del><ins>+import cgi </ins><span class="cx"> </span><span class="cx"> from sqlalchemy.engine import base, default, transactional </span><span class="cx"> </span><span class="lines">@@ -100,7 +100,7 @@ </span><span class="cx"> m = re.match( r'(\w+)://(.*)', name) </span><span class="cx"> if m is not None: </span><span class="cx"> (name, args) = m.group(1, 2) </span><del>- opts = dict( parse_qsl( args ) ) </del><ins>+ opts = dict( cgi.parse_qsl( args ) ) </ins><span class="cx"> return (name, opts) </span><span class="cx"> else: </span><span class="cx"> return None </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>[1299] sqlalchemy/branches/schema/lib/sqlalchemy: selectresults becomes an extension, the mod just installs it, so you can use selectresults without installing the mod</title> </head> <body> <div id="msg"> <dl> <dt>Revision</dt> <dd>1299</dd> <dt>Author</dt> <dd>zzzeek</dd> <dt>Date</dt> <dd>2006-04-19 15:02:27 -0500 (Wed, 19 Apr 2006)</dd> </dl> <h3>Log Message</h3> <pre>selectresults becomes an extension, the mod just installs it, so you can use selectresults without installing the mod</pre> <h3>Modified Paths</h3> <ul> <li><a href="#sqlalchemybranchesschemalibsqlalchemyenginebasepy">sqlalchemy/branches/schema/lib/sqlalchemy/engine/base.py</a></li> <li><a href="#sqlalchemybranchesschemalibsqlalchemymodsselectresultspy">sqlalchemy/branches/schema/lib/sqlalchemy/mods/selectresults.py</a></li> <li><a href="#sqlalchemybranchesschemalibsqlalchemyschemapy">sqlalchemy/branches/schema/lib/sqlalchemy/schema.py</a></li> </ul> <h3>Added Paths</h3> <ul> <li><a href="#sqlalchemybranchesschemalibsqlalchemyextselectresultspy">sqlalchemy/branches/schema/lib/sqlalchemy/ext/selectresults.py</a></li> </ul> </div> <div id="patch"> <h3>Diff</h3> <a id="sqlalchemybranchesschemalibsqlalchemyenginebasepy"></a> <div class="modfile"><h4>Modified: sqlalchemy/branches/schema/lib/sqlalchemy/engine/base.py (1298 => 1299)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/schema/lib/sqlalchemy/engine/base.py 2006-04-19 19:47:02 UTC (rev 1298) +++ sqlalchemy/branches/schema/lib/sqlalchemy/engine/base.py 2006-04-19 20:02:27 UTC (rev 1299) </span><span class="lines">@@ -350,7 +350,7 @@ </span><span class="cx"> else: </span><span class="cx"> conn = connection </span><span class="cx"> try: </span><del>- element.accept_visitor(visitorcallable(self, conn.proxy, **kwargs)) </del><ins>+ element.accept_schema_visitor(visitorcallable(self, conn.proxy, **kwargs)) </ins><span class="cx"> finally: </span><span class="cx"> if connection is None: </span><span class="cx"> conn.close() </span></span></pre></div> <a id="sqlalchemybranchesschemalibsqlalchemyextselectresultspy"></a> <div class="addfile"><h4>Added: sqlalchemy/branches/schema/lib/sqlalchemy/ext/selectresults.py (1298 => 1299)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/schema/lib/sqlalchemy/ext/selectresults.py 2006-04-19 19:47:02 UTC (rev 1298) +++ sqlalchemy/branches/schema/lib/sqlalchemy/ext/selectresults.py 2006-04-19 20:02:27 UTC (rev 1299) </span><span class="lines">@@ -0,0 +1,82 @@ </span><ins>+import sqlalchemy.sql as sql + +import sqlalchemy.mapping as mapping + + +class SelectResultsExt(mapping.MapperExtension): + def select_by(self, query, *args, **params): + return SelectResults(query, query._by_clause(*args, **params)) + def select(self, query, arg=None, **kwargs): + if arg is not None and isinstance(arg, sql.Selectable): + return mapping.EXT_PASS + else: + return SelectResults(query, arg, ops=kwargs) + +class SelectResults(object): + def __init__(self, query, clause=None, ops={}): + self._query = query + self._clause = clause + self._ops = {} + self._ops.update(ops) + + def count(self): + return self._query.count(self._clause) + + def min(self, col): + return sql.select([sql.func.min(col)], self._clause, **self._ops).scalar() + + def max(self, col): + return sql.select([sql.func.max(col)], self._clause, **self._ops).scalar() + + def sum(self, col): + return sql.select([sql.func.sum(col)], self._clause, **self._ops).scalar() + + def avg(self, col): + return sql.select([sql.func.avg(col)], self._clause, **self._ops).scalar() + + def clone(self): + return SelectResults(self._query, self._clause, self._ops.copy()) + + def filter(self, clause): + new = self.clone() + new._clause = sql.and_(self._clause, clause) + return new + + def order_by(self, order_by): + new = self.clone() + new._ops['order_by'] = order_by + return new + + def limit(self, limit): + return self[:limit] + + def offset(self, offset): + return self[offset:] + + def list(self): + return list(self) + + def __getitem__(self, item): + if isinstance(item, slice): + start = item.start + stop = item.stop + if (isinstance(start, int) and start < 0) or \ + (isinstance(stop, int) and stop < 0): + return list(self)[item] + else: + res = self.clone() + if start is not None and stop is not None: + res._ops.update(dict(offset=start, limit=stop-start)) + elif start is None and stop is not None: + res._ops.update(dict(limit=stop)) + elif start is not None and stop is None: + res._ops.update(dict(offset=start)) + if item.step is not None: + return list(res)[None:None:item.step] + else: + return res + else: + return list(self[item:item+1])[0] + + def __iter__(self): + return iter(self._query.select_whereclause(self._clause, **self._ops)) </ins></span></pre></div> <a id="sqlalchemybranchesschemalibsqlalchemymodsselectresultspy"></a> <div class="modfile"><h4>Modified: sqlalchemy/branches/schema/lib/sqlalchemy/mods/selectresults.py (1298 => 1299)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/schema/lib/sqlalchemy/mods/selectresults.py 2006-04-19 19:47:02 UTC (rev 1298) +++ sqlalchemy/branches/schema/lib/sqlalchemy/mods/selectresults.py 2006-04-19 20:02:27 UTC (rev 1299) </span><span class="lines">@@ -1,87 +1,6 @@ </span><del>-import sqlalchemy.sql as sql </del><ins>+from sqlalchemy.ext.selectresults import * </ins><span class="cx"> </span><del>-import sqlalchemy.mapping as mapping </del><span class="cx"> </span><del>-class SelectResultsExt(mapping.MapperExtension): - def select_by(self, query, *args, **params): - return SelectResults(query, query._by_clause(*args, **params)) - def select(self, query, arg=None, **kwargs): - if arg is not None and isinstance(arg, sql.Selectable): - return mapping.EXT_PASS - else: - return SelectResults(query, arg, ops=kwargs) - -MapperExtension = SelectResultsExt - -class SelectResults(object): - def __init__(self, query, clause=None, ops={}): - self._query = query - self._clause = clause - self._ops = {} - self._ops.update(ops) - - def count(self): - return self._query.count(self._clause) - - def min(self, col): - return sql.select([sql.func.min(col)], self._clause, **self._ops).scalar() - - def max(self, col): - return sql.select([sql.func.max(col)], self._clause, **self._ops).scalar() - - def sum(self, col): - return sql.select([sql.func.sum(col)], self._clause, **self._ops).scalar() - - def avg(self, col): - return sql.select([sql.func.avg(col)], self._clause, **self._ops).scalar() - - def clone(self): - return SelectResults(self._query, self._clause, self._ops.copy()) - - def filter(self, clause): - new = self.clone() - new._clause = sql.and_(self._clause, clause) - return new - - def order_by(self, order_by): - new = self.clone() - new._ops['order_by'] = order_by - return new - - def limit(self, limit): - return self[:limit] - - def offset(self, offset): - return self[offset:] - - def list(self): - return list(self) - - def __getitem__(self, item): - if isinstance(item, slice): - start = item.start - stop = item.stop - if (isinstance(start, int) and start < 0) or \ - (isinstance(stop, int) and stop < 0): - return list(self)[item] - else: - res = self.clone() - if start is not None and stop is not None: - res._ops.update(dict(offset=start, limit=stop-start)) - elif start is None and stop is not None: - res._ops.update(dict(limit=stop)) - elif start is not None and stop is None: - res._ops.update(dict(offset=start)) - if item.step is not None: - return list(res)[None:None:item.step] - else: - return res - else: - return list(self[item:item+1])[0] - - def __iter__(self): - return iter(self._query.select_whereclause(self._clause, **self._ops)) - </del><span class="cx"> def install_plugin(): </span><span class="cx"> mapping.global_extensions.append(SelectResultsExt) </span><span class="cx"> install_plugin() </span></span></pre></div> <a id="sqlalchemybranchesschemalibsqlalchemyschemapy"></a> <div class="modfile"><h4>Modified: sqlalchemy/branches/schema/lib/sqlalchemy/schema.py (1298 => 1299)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/schema/lib/sqlalchemy/schema.py 2006-04-19 19:47:02 UTC (rev 1298) +++ sqlalchemy/branches/schema/lib/sqlalchemy/schema.py 2006-04-19 20:02:27 UTC (rev 1299) </span><span class="lines">@@ -183,6 +183,7 @@ </span><span class="cx"> def accept_schema_visitor(self, visitor): </span><span class="cx"> """traverses the given visitor across the Column objects inside this Table, </span><span class="cx"> then calls the visit_table method on the visitor.""" </span><ins>+ print "TABLE ACCEPT VISITOR, C IS", [c for c in self.columns] </ins><span class="cx"> for c in self.columns: </span><span class="cx"> c.accept_schema_visitor(visitor) </span><span class="cx"> return visitor.visit_table(self) </span></span></pre> </div> </div> </body> </html> |
From: <co...@sq...> - 2006-04-19 19:47:13
|
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head><style type="text/css"><!-- #msg dl { border: 1px #006 solid; background: #369; padding: 6px; color: #fff; } #msg dt { float: left; width: 6em; font-weight: bold; } #msg dt:after { content:':';} #msg dl, #msg dt, #msg ul, #msg li { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; } #msg dl a { font-weight: bold} #msg dl a:link { color:#fc3; } #msg dl a:active { color:#ff0; } #msg dl a:visited { color:#cc6; } h3 { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; font-weight: bold; } #msg pre { overflow: auto; background: #ffc; border: 1px #fc0 solid; padding: 6px; } #msg ul, pre { overflow: auto; } #patch { width: 100%; } #patch h4 {font-family: verdana,arial,helvetica,sans-serif;font-size:10pt;padding:8px;background:#369;color:#fff;margin:0;} #patch .propset h4, #patch .binary h4 {margin:0;} #patch pre {padding:0;line-height:1.2em;margin:0;} #patch .diff {width:100%;background:#eee;padding: 0 0 10px 0;overflow:auto;} #patch .propset .diff, #patch .binary .diff {padding:10px 0;} #patch span {display:block;padding:0 10px;} #patch .modfile, #patch .addfile, #patch .delfile, #patch .propset, #patch .binary, #patch .copfile {border:1px solid #ccc;margin:10px 0;} #patch ins {background:#dfd;text-decoration:none;display:block;padding:0 10px;} #patch del {background:#fdd;text-decoration:none;display:block;padding:0 10px;} #patch .lines, .info {color:#888;background:#fff;} --></style> <title>[1298] sqlalchemy/trunk/test/selectresults.py: what a strange thing to not be in there....</title> </head> <body> <div id="msg"> <dl> <dt>Revision</dt> <dd>1298</dd> <dt>Author</dt> <dd>zzzeek</dd> <dt>Date</dt> <dd>2006-04-19 14:47:02 -0500 (Wed, 19 Apr 2006)</dd> </dl> <h3>Log Message</h3> <pre>what a strange thing to not be in there....</pre> <h3>Added Paths</h3> <ul> <li><a href="#sqlalchemytrunktestselectresultspy">sqlalchemy/trunk/test/selectresults.py</a></li> </ul> </div> <div id="patch"> <h3>Diff</h3> <a id="sqlalchemytrunktestselectresultspy"></a> <div class="addfile"><h4>Added: sqlalchemy/trunk/test/selectresults.py (1297 => 1298)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/trunk/test/selectresults.py 2006-04-19 19:43:31 UTC (rev 1297) +++ sqlalchemy/trunk/test/selectresults.py 2006-04-19 19:47:02 UTC (rev 1298) </span><span class="lines">@@ -0,0 +1,68 @@ </span><ins>+from testbase import PersistTest +import testbase + +from sqlalchemy import * + +from sqlalchemy.mods.selectresults import SelectResultsExt + +class Foo(object): + pass + +class SelectResultsTest(PersistTest): + def setUpAll(self): + global foo + foo = Table('foo', testbase.db, + Column('id', Integer, Sequence('foo_id_seq'), primary_key=True), + Column('bar', Integer)) + + assign_mapper(Foo, foo, extension=SelectResultsExt()) + foo.create() + for i in range(100): + Foo(bar=i) + objectstore.commit() + + def setUp(self): + self.orig = Foo.mapper.select_whereclause() + self.res = Foo.select() + + def tearDownAll(self): + global foo + foo.drop() + + def test_slice(self): + assert self.res[1] == self.orig[1] + assert list(self.res[10:20]) == self.orig[10:20] + assert list(self.res[10:]) == self.orig[10:] + assert list(self.res[:10]) == self.orig[:10] + assert list(self.res[:10]) == self.orig[:10] + assert list(self.res[10:40:3]) == self.orig[10:40:3] + assert list(self.res[-5:]) == self.orig[-5:] + + def test_aggregate(self): + assert self.res.count() == 100 + assert self.res.filter(foo.c.bar<30).min(foo.c.bar) == 0 + assert self.res.filter(foo.c.bar<30).max(foo.c.bar) == 29 + # this one fails in mysql as the result comes back as a string + assert self.res.filter(foo.c.bar<30).sum(foo.c.bar) == 435 + # this one fails with postgres, the floating point comparison fails + assert self.res.filter(foo.c.bar<30).avg(foo.c.bar) == 14.5 + + def test_filter(self): + assert self.res.count() == 100 + assert self.res.filter(Foo.c.bar < 30).count() == 30 + res2 = self.res.filter(Foo.c.bar < 30).filter(Foo.c.bar > 10) + assert res2.count() == 19 + + def test_order_by(self): + assert self.res.order_by([Foo.c.bar])[0].bar == 0 + assert self.res.order_by([desc(Foo.c.bar)])[0].bar == 99 + + def test_offset(self): + assert list(self.res.order_by([Foo.c.bar]).offset(10))[0].bar == 10 + + def test_offset(self): + assert len(list(self.res.limit(10))) == 10 + + +if __name__ == "__main__": + testbase.main() </ins></span></pre> </div> </div> </body> </html> |
From: <co...@sq...> - 2006-04-19 19:43:45
|
<!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>[1297] sqlalchemy/branches/schema/test: merge 1282:1296 from trunk</title> </head> <body> <div id="msg"> <dl> <dt>Revision</dt> <dd>1297</dd> <dt>Author</dt> <dd>zzzeek</dd> <dt>Date</dt> <dd>2006-04-19 14:43:31 -0500 (Wed, 19 Apr 2006)</dd> </dl> <h3>Log Message</h3> <pre>merge 1282:1296 from trunk</pre> <h3>Modified Paths</h3> <ul> <li><a href="#sqlalchemybranchesschemalibsqlalchemyattributespy">sqlalchemy/branches/schema/lib/sqlalchemy/attributes.py</a></li> <li><a href="#sqlalchemybranchesschemalibsqlalchemymappingobjectstorepy">sqlalchemy/branches/schema/lib/sqlalchemy/mapping/objectstore.py</a></li> <li><a href="#sqlalchemybranchesschemalibsqlalchemymappingpropertiespy">sqlalchemy/branches/schema/lib/sqlalchemy/mapping/properties.py</a></li> <li><a href="#sqlalchemybranchesschemalibsqlalchemymappingquerypy">sqlalchemy/branches/schema/lib/sqlalchemy/mapping/query.py</a></li> <li><a href="#sqlalchemybranchesschemalibsqlalchemymodsselectresultspy">sqlalchemy/branches/schema/lib/sqlalchemy/mods/selectresults.py</a></li> <li><a href="#sqlalchemybranchesschemalibsqlalchemysqlpy">sqlalchemy/branches/schema/lib/sqlalchemy/sql.py</a></li> <li><a href="#sqlalchemybranchesschematestalltestspy">sqlalchemy/branches/schema/test/alltests.py</a></li> <li><a href="#sqlalchemybranchesschematestattributespy">sqlalchemy/branches/schema/test/attributes.py</a></li> <li><a href="#sqlalchemybranchesschematestmanytomanypy">sqlalchemy/branches/schema/test/manytomany.py</a></li> <li><a href="#sqlalchemybranchesschematestmasscreatepy">sqlalchemy/branches/schema/test/masscreate.py</a></li> <li><a href="#sqlalchemybranchesschematestrelationshipspy">sqlalchemy/branches/schema/test/relationships.py</a></li> </ul> <h3>Added Paths</h3> <ul> <li><a href="#sqlalchemybranchesschematestmassloadpy">sqlalchemy/branches/schema/test/massload.py</a></li> </ul> </div> <div id="patch"> <h3>Diff</h3> <a id="sqlalchemybranchesschemalibsqlalchemyattributespy"></a> <div class="modfile"><h4>Modified: sqlalchemy/branches/schema/lib/sqlalchemy/attributes.py (1296 => 1297)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/schema/lib/sqlalchemy/attributes.py 2006-04-19 19:33:51 UTC (rev 1296) +++ sqlalchemy/branches/schema/lib/sqlalchemy/attributes.py 2006-04-19 19:43:31 UTC (rev 1297) </span><span class="lines">@@ -78,10 +78,10 @@ </span><span class="cx"> which occurs through the SmartProperty property object ultimately calls upon </span><span class="cx"> ManagedAttribute objects associated with the instance via this dictionary.""" </span><span class="cx"> def __init__(self, obj, key): </span><del>- #self.__obj = weakref.ref(obj) - self.obj = obj </del><ins>+ self.__obj = weakref.ref(obj) + #self.obj = obj </ins><span class="cx"> self.key = key </span><del>- #obj = property(lambda s:s.__obj()) </del><ins>+ obj = property(lambda s:s.__obj()) </ins><span class="cx"> def history(self, **kwargs): </span><span class="cx"> return self </span><span class="cx"> def plain_init(self, *args, **kwargs): </span></span></pre></div> <a id="sqlalchemybranchesschemalibsqlalchemymappingobjectstorepy"></a> <div class="modfile"><h4>Modified: sqlalchemy/branches/schema/lib/sqlalchemy/mapping/objectstore.py (1296 => 1297)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/schema/lib/sqlalchemy/mapping/objectstore.py 2006-04-19 19:33:51 UTC (rev 1296) +++ sqlalchemy/branches/schema/lib/sqlalchemy/mapping/objectstore.py 2006-04-19 19:43:31 UTC (rev 1297) </span><span class="lines">@@ -179,7 +179,7 @@ </span><span class="cx"> </span><span class="cx"> def expunge(self, *obj): </span><span class="cx"> for o in obj: </span><del>- self.uow.expunge(obj) </del><ins>+ self.uow.expunge(o) </ins><span class="cx"> </span><span class="cx"> def register_clean(self, *obj): </span><span class="cx"> for o in obj: </span></span></pre></div> <a id="sqlalchemybranchesschemalibsqlalchemymappingpropertiespy"></a> <div class="modfile"><h4>Modified: sqlalchemy/branches/schema/lib/sqlalchemy/mapping/properties.py (1296 => 1297)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/schema/lib/sqlalchemy/mapping/properties.py 2006-04-19 19:33:51 UTC (rev 1296) +++ sqlalchemy/branches/schema/lib/sqlalchemy/mapping/properties.py 2006-04-19 19:43:31 UTC (rev 1297) </span><span class="lines">@@ -232,13 +232,13 @@ </span><span class="cx"> """determines our 'direction', i.e. do we represent one to many, many to many, etc.""" </span><span class="cx"> #print self.key, repr(self.parent.table.name), repr(self.parent.primarytable.name), repr(self.foreignkey.table.name), repr(self.target), repr(self.foreigntable.name) </span><span class="cx"> </span><del>- if self.parent.table is self.target: </del><ins>+ if self.secondaryjoin is not None: + return PropertyLoader.MANYTOMANY + elif self.parent.table is self.target: </ins><span class="cx"> if self.foreignkey.primary_key: </span><span class="cx"> return PropertyLoader.MANYTOONE </span><span class="cx"> else: </span><span class="cx"> return PropertyLoader.ONETOMANY </span><del>- elif self.secondaryjoin is not None: - return PropertyLoader.MANYTOMANY </del><span class="cx"> elif self.foreigntable == self.mapper.noninherited_table: </span><span class="cx"> return PropertyLoader.ONETOMANY </span><span class="cx"> elif self.foreigntable == self.parent.noninherited_table: </span><span class="lines">@@ -666,13 +666,11 @@ </span><span class="cx"> binary.right = binds.setdefault(binary.right, </span><span class="cx"> sql.BindParamClause(binary.left._label, None, shortname = binary.right.name)) </span><span class="cx"> </span><del>- if secondaryjoin is not None: - lazywhere = sql.and_(primaryjoin, secondaryjoin) - else: - lazywhere = primaryjoin - lazywhere = lazywhere.copy_container() </del><ins>+ lazywhere = primaryjoin.copy_container() </ins><span class="cx"> li = BinaryVisitor(visit_binary) </span><span class="cx"> lazywhere.accept_visitor(li) </span><ins>+ if secondaryjoin is not None: + lazywhere = sql.and_(lazywhere, secondaryjoin) </ins><span class="cx"> return (lazywhere, binds) </span><span class="cx"> </span><span class="cx"> </span></span></pre></div> <a id="sqlalchemybranchesschemalibsqlalchemymappingquerypy"></a> <div class="modfile"><h4>Modified: sqlalchemy/branches/schema/lib/sqlalchemy/mapping/query.py (1296 => 1297)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/schema/lib/sqlalchemy/mapping/query.py 2006-04-19 19:33:51 UTC (rev 1296) +++ sqlalchemy/branches/schema/lib/sqlalchemy/mapping/query.py 2006-04-19 19:43:31 UTC (rev 1297) </span><span class="lines">@@ -10,6 +10,7 @@ </span><span class="cx"> import sqlalchemy.util as util </span><span class="cx"> from sqlalchemy.exceptions import * </span><span class="cx"> import mapper </span><ins>+from sqlalchemy.exceptions import * </ins><span class="cx"> </span><span class="cx"> class Query(object): </span><span class="cx"> """encapsulates the object-fetching operations provided by Mappers.""" </span></span></pre></div> <a id="sqlalchemybranchesschemalibsqlalchemymodsselectresultspy"></a> <div class="modfile"><h4>Modified: sqlalchemy/branches/schema/lib/sqlalchemy/mods/selectresults.py (1296 => 1297)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/schema/lib/sqlalchemy/mods/selectresults.py 2006-04-19 19:33:51 UTC (rev 1296) +++ sqlalchemy/branches/schema/lib/sqlalchemy/mods/selectresults.py 2006-04-19 19:43:31 UTC (rev 1297) </span><span class="lines">@@ -2,7 +2,6 @@ </span><span class="cx"> </span><span class="cx"> import sqlalchemy.mapping as mapping </span><span class="cx"> </span><del>- </del><span class="cx"> class SelectResultsExt(mapping.MapperExtension): </span><span class="cx"> def select_by(self, query, *args, **params): </span><span class="cx"> return SelectResults(query, query._by_clause(*args, **params)) </span></span></pre></div> <a id="sqlalchemybranchesschemalibsqlalchemysqlpy"></a> <div class="modfile"><h4>Modified: sqlalchemy/branches/schema/lib/sqlalchemy/sql.py (1296 => 1297)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/schema/lib/sqlalchemy/sql.py 2006-04-19 19:33:51 UTC (rev 1296) +++ sqlalchemy/branches/schema/lib/sqlalchemy/sql.py 2006-04-19 19:43:31 UTC (rev 1297) </span><span class="lines">@@ -130,7 +130,8 @@ </span><span class="cx"> def between_(ctest, cleft, cright): </span><span class="cx"> """ returns BETWEEN predicate clause (clausetest BETWEEN clauseleft AND clauseright) """ </span><span class="cx"> return BooleanExpression(ctest, and_(cleft, cright), 'BETWEEN') </span><del>- </del><ins>+between = between_ + </ins><span class="cx"> def cast(clause, totype, **kwargs): </span><span class="cx"> """ returns CAST function CAST(clause AS totype) </span><span class="cx"> Use with a sqlalchemy.types.TypeEngine object, i.e </span><span class="lines">@@ -541,6 +542,8 @@ </span><span class="cx"> return Label(name, self, self.type) </span><span class="cx"> def distinct(self): </span><span class="cx"> return CompoundClause(None,"DISTINCT", self) </span><ins>+ def between(self, cleft, cright): + return between_(self, cleft, cright) </ins><span class="cx"> def op(self, operator): </span><span class="cx"> return lambda other: self._compare(operator, other) </span><span class="cx"> # and here come the math operators: </span></span></pre></div> <a id="sqlalchemybranchesschematestalltestspy"></a> <div class="modfile"><h4>Modified: sqlalchemy/branches/schema/test/alltests.py (1296 => 1297)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/schema/test/alltests.py 2006-04-19 19:33:51 UTC (rev 1296) +++ sqlalchemy/branches/schema/test/alltests.py 2006-04-19 19:43:31 UTC (rev 1297) </span><span class="lines">@@ -39,6 +39,7 @@ </span><span class="cx"> </span><span class="cx"> # ORM persistence </span><span class="cx"> 'objectstore', </span><ins>+ 'relationships', </ins><span class="cx"> </span><span class="cx"> # cyclical ORM persistence </span><span class="cx"> 'cycles', </span></span></pre></div> <a id="sqlalchemybranchesschematestattributespy"></a> <div class="modfile"><h4>Modified: sqlalchemy/branches/schema/test/attributes.py (1296 => 1297)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/schema/test/attributes.py 2006-04-19 19:33:51 UTC (rev 1296) +++ sqlalchemy/branches/schema/test/attributes.py 2006-04-19 19:43:31 UTC (rev 1297) </span><span class="lines">@@ -43,9 +43,9 @@ </span><span class="cx"> manager.register_attribute(MyTest, 'user_id', uselist = False) </span><span class="cx"> manager.register_attribute(MyTest, 'user_name', uselist = False) </span><span class="cx"> manager.register_attribute(MyTest, 'email_address', uselist = False) </span><del>- x = MyTest() - x.user_id=7 - pickle.dumps(x) </del><ins>+ x = MyTest() + x.user_id=7 + pickle.dumps(x) </ins><span class="cx"> </span><span class="cx"> def testlist(self): </span><span class="cx"> class User(object):pass </span></span></pre></div> <a id="sqlalchemybranchesschematestmanytomanypy"></a> <div class="modfile"><h4>Modified: sqlalchemy/branches/schema/test/manytomany.py (1296 => 1297)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/schema/test/manytomany.py 2006-04-19 19:33:51 UTC (rev 1296) +++ sqlalchemy/branches/schema/test/manytomany.py 2006-04-19 19:43:31 UTC (rev 1297) </span><span class="lines">@@ -7,6 +7,10 @@ </span><span class="cx"> '''represents a place''' </span><span class="cx"> def __init__(self, name=None): </span><span class="cx"> self.name = name </span><ins>+ def __str__(self): + return "(Place '%s')" % self.name + def __repr__(self): + return str(self) </ins><span class="cx"> </span><span class="cx"> class PlaceThingy(object): </span><span class="cx"> '''represents a thingy attached to a Place''' </span><span class="lines">@@ -58,29 +62,87 @@ </span><span class="cx"> Column('transition_id', Integer, ForeignKey('transition.transition_id')), </span><span class="cx"> ) </span><span class="cx"> </span><ins>+ global place_place + place_place = Table('place_place', db, + Column('pl1_id', Integer, ForeignKey('place.place_id')), + Column('pl2_id', Integer, ForeignKey('place.place_id')), + ) + </ins><span class="cx"> place.create() </span><span class="cx"> transition.create() </span><span class="cx"> place_input.create() </span><span class="cx"> place_output.create() </span><span class="cx"> place_thingy.create() </span><ins>+ place_place.create() </ins><span class="cx"> </span><span class="cx"> def tearDownAll(self): </span><ins>+ place_place.drop() </ins><span class="cx"> place_input.drop() </span><span class="cx"> place_output.drop() </span><span class="cx"> place_thingy.drop() </span><span class="cx"> place.drop() </span><span class="cx"> transition.drop() </span><ins>+ #testbase.db.tables.clear() </ins><span class="cx"> </span><span class="cx"> def setUp(self): </span><span class="cx"> objectstore.clear() </span><span class="cx"> clear_mappers() </span><span class="cx"> </span><span class="cx"> def tearDown(self): </span><ins>+ place_place.delete().execute() </ins><span class="cx"> place_input.delete().execute() </span><span class="cx"> place_output.delete().execute() </span><span class="cx"> transition.delete().execute() </span><span class="cx"> place.delete().execute() </span><span class="cx"> </span><ins>+ def testcircular(self): + """tests a many-to-many relationship from a table to itself.""" + + Place.mapper = mapper(Place, place) + + Place.mapper.add_property('places', relation( + Place.mapper, secondary=place_place, primaryjoin=place.c.place_id==place_place.c.pl1_id, + secondaryjoin=place.c.place_id==place_place.c.pl2_id, + order_by=place_place.c.pl2_id, + lazy=True, + )) + + p1 = Place('place1') + p2 = Place('place2') + p3 = Place('place3') + p4 = Place('place4') + p5 = Place('place5') + p6 = Place('place6') + p7 = Place('place7') + + p1.places.append(p2) + p1.places.append(p3) + p5.places.append(p6) + p6.places.append(p1) + p7.places.append(p1) + p1.places.append(p5) + p4.places.append(p3) + p3.places.append(p4) + objectstore.flush() + + objectstore.clear() + l = Place.mapper.select(order_by=place.c.place_id) + (p1, p2, p3, p4, p5, p6, p7) = l + assert p1.places == [p2,p3,p5] + assert p5.places == [p6] + assert p7.places == [p1] + assert p6.places == [p1] + assert p4.places == [p3] + assert p3.places == [p4] + assert p2.places == [] + + for p in l: + pp = p.places + self.echo("Place " + str(p) +" places " + repr(pp)) + + objectstore.delete(p1,p2,p3,p4,p5,p6,p7) + objectstore.flush() + </ins><span class="cx"> def testdouble(self): </span><span class="cx"> """tests that a mapper can have two eager relations to the same table, via </span><span class="cx"> two different association tables. aliases are required.""" </span><span class="lines">@@ -110,17 +172,14 @@ </span><span class="cx"> } </span><span class="cx"> ) </span><span class="cx"> </span><del>- def testcircular(self): - """tests a circular many-to-many relationship. this requires that the mapper - "break off" a new "mapper stub" to indicate a third depedendent processor.""" </del><ins>+ def testbidirectional(self): + """tests a bi-directional many-to-many relationship.""" </ins><span class="cx"> Place.mapper = mapper(Place, place) </span><span class="cx"> Transition.mapper = mapper(Transition, transition, properties = dict( </span><span class="cx"> inputs = relation(Place.mapper, place_output, lazy=True, backref='inputs'), </span><span class="cx"> outputs = relation(Place.mapper, place_input, lazy=True, backref='outputs'), </span><span class="cx"> ) </span><span class="cx"> ) </span><del>- #Place.mapper.add_property('inputs', relation(Transition.mapper, place_output, lazy=True, attributeext=attr.ListBackrefExtension('inputs'))) - #Place.mapper.add_property('outputs', relation(Transition.mapper, place_input, lazy=True, attributeext=attr.ListBackrefExtension('outputs'))) </del><span class="cx"> </span><span class="cx"> Place.eagermapper = Place.mapper.options( </span><span class="cx"> eagerload('inputs', selectalias='ip_alias'), </span><span class="lines">@@ -167,6 +226,7 @@ </span><span class="cx"> enrolTbl.drop() </span><span class="cx"> studentTbl.drop() </span><span class="cx"> courseTbl.drop() </span><ins>+ #testbase.db.tables.clear() </ins><span class="cx"> </span><span class="cx"> def setUp(self): </span><span class="cx"> objectstore.clear() </span><span class="lines">@@ -242,6 +302,7 @@ </span><span class="cx"> c2a1.drop() </span><span class="cx"> a.drop() </span><span class="cx"> c.drop() </span><ins>+ #testbase.db.tables.clear() </ins><span class="cx"> </span><span class="cx"> def testbasic(self): </span><span class="cx"> class C(object):pass </span></span></pre></div> <a id="sqlalchemybranchesschematestmasscreatepy"></a> <div class="modfile"><h4>Modified: sqlalchemy/branches/schema/test/masscreate.py (1296 => 1297)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/schema/test/masscreate.py 2006-04-19 19:33:51 UTC (rev 1296) +++ sqlalchemy/branches/schema/test/masscreate.py 2006-04-19 19:43:31 UTC (rev 1297) </span><span class="lines">@@ -2,6 +2,7 @@ </span><span class="cx"> </span><span class="cx"> from sqlalchemy.attributes import * </span><span class="cx"> import time </span><ins>+import gc </ins><span class="cx"> </span><span class="cx"> manage_attributes = True </span><span class="cx"> init_attributes = manage_attributes and True </span><span class="lines">@@ -32,7 +33,9 @@ </span><span class="cx"> if init_attributes: </span><span class="cx"> attr_manager.init_attr(a) </span><span class="cx"> a.email = 'fo...@ba...' </span><del>- u.addresses.append(u) - </del><ins>+ u.addresses.append(a) +# gc.collect() + print len(managed_attributes) +# managed_attributes.clear() </ins><span class="cx"> total = time.time() - now </span><span class="cx"> print "Total time", total </span></span></pre></div> <a id="sqlalchemybranchesschematestmassloadpyfromrev1296sqlalchemytrunktestmassloadpy"></a> <div class="copfile"><h4>Copied: sqlalchemy/branches/schema/test/massload.py (from rev 1296, sqlalchemy/trunk/test/massload.py) ( => )</h4> <pre class="diff"><span> <span class="info">Modified: sqlalchemy/branches/schema/test/relationships.py =================================================================== </span><del>--- sqlalchemy/branches/schema/test/relationships.py 2006-04-19 19:33:51 UTC (rev 1296) </del><ins>+++ sqlalchemy/branches/schema/test/relationships.py 2006-04-19 19:43:31 UTC (rev 1297) </ins><span class="lines">@@ -10,9 +10,13 @@ </span><span class="cx"> </span><span class="cx"> </span><span class="cx"> class RelationTest(testbase.PersistTest): </span><del>- """this is essentially an extension of the "dependency.py" topological sort test. this exposes - a particular issue that doesnt always occur with the straight dependency tests, due to the nature - of the sort being different based on random conditions""" </del><ins>+ """this is essentially an extension of the "dependency.py" topological sort test. + in this test, a table is dependent on two other tables that are otherwise unrelated to each other. + the dependency sort must insure that this childmost table is below both parent tables in the outcome + (a bug existed where this was not always the case). + while the straight topological sort tests should expose this, since the sorting can be different due + to subtle differences in program execution, this test case was exposing the bug whereas the simpler tests + were not.""" </ins><span class="cx"> def setUpAll(self): </span><span class="cx"> global tbl_a </span><span class="cx"> global tbl_b </span><span class="lines">@@ -83,6 +87,9 @@ </span><span class="cx"> conn.drop(tbl_c) </span><span class="cx"> conn.drop(tbl_b) </span><span class="cx"> conn.drop(tbl_a) </span><ins>+ + def tearDownAll(self): + testbase.metadata.tables.clear() </ins><span class="cx"> </span><span class="cx"> def testDeleteRootTable(self): </span><span class="cx"> session.flush() </span></span></pre> </div> </div> </body> </html> |
From: <co...@sq...> - 2006-04-19 19:34: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>[1296] sqlalchemy/trunk/test: fixed up expunge() and the continuing circular refs in attributes, added a unit test for the whole thing</title> </head> <body> <div id="msg"> <dl> <dt>Revision</dt> <dd>1296</dd> <dt>Author</dt> <dd>zzzeek</dd> <dt>Date</dt> <dd>2006-04-19 14:33:51 -0500 (Wed, 19 Apr 2006)</dd> </dl> <h3>Log Message</h3> <pre>fixed up expunge() and the continuing circular refs in attributes, added a unit test for the whole thing</pre> <h3>Modified Paths</h3> <ul> <li><a href="#sqlalchemytrunklibsqlalchemyattributespy">sqlalchemy/trunk/lib/sqlalchemy/attributes.py</a></li> <li><a href="#sqlalchemytrunklibsqlalchemymappingobjectstorepy">sqlalchemy/trunk/lib/sqlalchemy/mapping/objectstore.py</a></li> <li><a href="#sqlalchemytrunklibsqlalchemysqlpy">sqlalchemy/trunk/lib/sqlalchemy/sql.py</a></li> <li><a href="#sqlalchemytrunktestattributespy">sqlalchemy/trunk/test/attributes.py</a></li> <li><a href="#sqlalchemytrunktestmasscreatepy">sqlalchemy/trunk/test/masscreate.py</a></li> </ul> <h3>Added Paths</h3> <ul> <li><a href="#sqlalchemytrunktestmassloadpy">sqlalchemy/trunk/test/massload.py</a></li> </ul> </div> <div id="patch"> <h3>Diff</h3> <a id="sqlalchemytrunklibsqlalchemyattributespy"></a> <div class="modfile"><h4>Modified: sqlalchemy/trunk/lib/sqlalchemy/attributes.py (1295 => 1296)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/trunk/lib/sqlalchemy/attributes.py 2006-04-19 18:24:45 UTC (rev 1295) +++ sqlalchemy/trunk/lib/sqlalchemy/attributes.py 2006-04-19 19:33:51 UTC (rev 1296) </span><span class="lines">@@ -78,10 +78,10 @@ </span><span class="cx"> which occurs through the SmartProperty property object ultimately calls upon </span><span class="cx"> ManagedAttribute objects associated with the instance via this dictionary.""" </span><span class="cx"> def __init__(self, obj, key): </span><del>- #self.__obj = weakref.ref(obj) - self.obj = obj </del><ins>+ self.__obj = weakref.ref(obj) + #self.obj = obj </ins><span class="cx"> self.key = key </span><del>- #obj = property(lambda s:s.__obj()) </del><ins>+ obj = property(lambda s:s.__obj()) </ins><span class="cx"> def history(self, **kwargs): </span><span class="cx"> return self </span><span class="cx"> def plain_init(self, *args, **kwargs): </span></span></pre></div> <a id="sqlalchemytrunklibsqlalchemymappingobjectstorepy"></a> <div class="modfile"><h4>Modified: sqlalchemy/trunk/lib/sqlalchemy/mapping/objectstore.py (1295 => 1296)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/trunk/lib/sqlalchemy/mapping/objectstore.py 2006-04-19 18:24:45 UTC (rev 1295) +++ sqlalchemy/trunk/lib/sqlalchemy/mapping/objectstore.py 2006-04-19 19:33:51 UTC (rev 1296) </span><span class="lines">@@ -94,7 +94,7 @@ </span><span class="cx"> </span><span class="cx"> def expunge(self, *obj): </span><span class="cx"> for o in obj: </span><del>- self.uow.expunge(obj) </del><ins>+ self.uow.expunge(o) </ins><span class="cx"> </span><span class="cx"> def register_clean(self, obj): </span><span class="cx"> self._bind_to(obj) </span></span></pre></div> <a id="sqlalchemytrunklibsqlalchemysqlpy"></a> <div class="modfile"><h4>Modified: sqlalchemy/trunk/lib/sqlalchemy/sql.py (1295 => 1296)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/trunk/lib/sqlalchemy/sql.py 2006-04-19 18:24:45 UTC (rev 1295) +++ sqlalchemy/trunk/lib/sqlalchemy/sql.py 2006-04-19 19:33:51 UTC (rev 1296) </span><span class="lines">@@ -131,7 +131,8 @@ </span><span class="cx"> def between_(ctest, cleft, cright): </span><span class="cx"> """ returns BETWEEN predicate clause (clausetest BETWEEN clauseleft AND clauseright) """ </span><span class="cx"> return BooleanExpression(ctest, and_(cleft, cright), 'BETWEEN') </span><del>- </del><ins>+between = between_ + </ins><span class="cx"> def cast(clause, totype, **kwargs): </span><span class="cx"> """ returns CAST function CAST(clause AS totype) </span><span class="cx"> Use with a sqlalchemy.types.TypeEngine object, i.e </span><span class="lines">@@ -517,6 +518,8 @@ </span><span class="cx"> return Label(name, self, self.type) </span><span class="cx"> def distinct(self): </span><span class="cx"> return CompoundClause(None,"DISTINCT", self) </span><ins>+ def between(self, cleft, cright): + return between_(self, cleft, cright) </ins><span class="cx"> def op(self, operator): </span><span class="cx"> return lambda other: self._compare(operator, other) </span><span class="cx"> # and here come the math operators: </span></span></pre></div> <a id="sqlalchemytrunktestattributespy"></a> <div class="modfile"><h4>Modified: sqlalchemy/trunk/test/attributes.py (1295 => 1296)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/trunk/test/attributes.py 2006-04-19 18:24:45 UTC (rev 1295) +++ sqlalchemy/trunk/test/attributes.py 2006-04-19 19:33:51 UTC (rev 1296) </span><span class="lines">@@ -43,9 +43,9 @@ </span><span class="cx"> manager.register_attribute(MyTest, 'user_id', uselist = False) </span><span class="cx"> manager.register_attribute(MyTest, 'user_name', uselist = False) </span><span class="cx"> manager.register_attribute(MyTest, 'email_address', uselist = False) </span><del>- x = MyTest() - x.user_id=7 - pickle.dumps(x) </del><ins>+ x = MyTest() + x.user_id=7 + pickle.dumps(x) </ins><span class="cx"> </span><span class="cx"> def testlist(self): </span><span class="cx"> class User(object):pass </span></span></pre></div> <a id="sqlalchemytrunktestmasscreatepy"></a> <div class="modfile"><h4>Modified: sqlalchemy/trunk/test/masscreate.py (1295 => 1296)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/trunk/test/masscreate.py 2006-04-19 18:24:45 UTC (rev 1295) +++ sqlalchemy/trunk/test/masscreate.py 2006-04-19 19:33:51 UTC (rev 1296) </span><span class="lines">@@ -2,6 +2,7 @@ </span><span class="cx"> </span><span class="cx"> from sqlalchemy.attributes import * </span><span class="cx"> import time </span><ins>+import gc </ins><span class="cx"> </span><span class="cx"> manage_attributes = True </span><span class="cx"> init_attributes = manage_attributes and True </span><span class="lines">@@ -32,7 +33,9 @@ </span><span class="cx"> if init_attributes: </span><span class="cx"> attr_manager.init_attr(a) </span><span class="cx"> a.email = 'fo...@ba...' </span><del>- u.addresses.append(u) - </del><ins>+ u.addresses.append(a) +# gc.collect() + print len(managed_attributes) +# managed_attributes.clear() </ins><span class="cx"> total = time.time() - now </span><span class="cx"> print "Total time", total </span></span></pre></div> <a id="sqlalchemytrunktestmassloadpy"></a> <div class="addfile"><h4>Added: sqlalchemy/trunk/test/massload.py (1295 => 1296)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/trunk/test/massload.py 2006-04-19 18:24:45 UTC (rev 1295) +++ sqlalchemy/trunk/test/massload.py 2006-04-19 19:33:51 UTC (rev 1296) </span><span class="lines">@@ -0,0 +1,65 @@ </span><ins>+from testbase import PersistTest, AssertMixin +import unittest, sys, os +from sqlalchemy import * +import sqlalchemy.attributes as attributes +import StringIO +import testbase +import gc + +db = testbase.db + +NUM = 25000 + +""" +we are testing session.expunge() here, also that the attributes and unitofwork packages dont keep dereferenced +stuff hanging around. + +for best results, dont run with sqlite :memory: database, and keep an eye on top while it runs""" + +class LoadTest(AssertMixin): + def setUpAll(self): + db.echo = False + global items + items = Table('items', db, + Column('item_id', Integer, primary_key=True), + Column('value', String(100))) + items.create() + db.echo = testbase.echo + def tearDownAll(self): + db.echo = False + items.drop() + items.deregister() + db.echo = testbase.echo + def setUp(self): + objectstore.clear() + clear_mappers() + for x in range(1,NUM/500+1): + l = [] + for y in range(x*500-500, x*500): + l.append({'item_id':y, 'value':'this is item #%d' % y}) + items.insert().execute(*l) + + def testload(self): + class Item(object):pass + + m = mapper(Item, items) + + for x in range (1,NUM/100): + # this is not needed with cpython which clears non-circular refs immediately + #gc.collect() + l = m.select(items.c.item_id.between(x*100 - 100, x*100 - 1)) + assert len(l) == 100 + print "loaded ", len(l), " items " + # modifying each object will insure that the objects get placed in the "dirty" list + # and will hang around until expunged + for a in l: + a.value = 'changed...' + assert len(objectstore.get_session().dirty) == len(l) + assert len(objectstore.get_session().identity_map) == len(l) + assert len(attributes.managed_attributes) == len(l) + print len(objectstore.get_session().dirty) + print len(objectstore.get_session().identity_map) + #objectstore.expunge(*l) + +if __name__ == "__main__": + testbase.main() </ins></span></pre> </div> </div> </body> </html> |
From: <co...@sq...> - 2006-04-19 18:25:00
|
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head><style type="text/css"><!-- #msg dl { border: 1px #006 solid; background: #369; padding: 6px; color: #fff; } #msg dt { float: left; width: 6em; font-weight: bold; } #msg dt:after { content:':';} #msg dl, #msg dt, #msg ul, #msg li { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; } #msg dl a { font-weight: bold} #msg dl a:link { color:#fc3; } #msg dl a:active { color:#ff0; } #msg dl a:visited { color:#cc6; } h3 { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; font-weight: bold; } #msg pre { overflow: auto; background: #ffc; border: 1px #fc0 solid; padding: 6px; } #msg ul, pre { overflow: auto; } #patch { width: 100%; } #patch h4 {font-family: verdana,arial,helvetica,sans-serif;font-size:10pt;padding:8px;background:#369;color:#fff;margin:0;} #patch .propset h4, #patch .binary h4 {margin:0;} #patch pre {padding:0;line-height:1.2em;margin:0;} #patch .diff {width:100%;background:#eee;padding: 0 0 10px 0;overflow:auto;} #patch .propset .diff, #patch .binary .diff {padding:10px 0;} #patch span {display:block;padding:0 10px;} #patch .modfile, #patch .addfile, #patch .delfile, #patch .propset, #patch .binary, #patch .copfile {border:1px solid #ccc;margin:10px 0;} #patch ins {background:#dfd;text-decoration:none;display:block;padding:0 10px;} #patch del {background:#fdd;text-decoration:none;display:block;padding:0 10px;} #patch .lines, .info {color:#888;background:#fff;} --></style> <title>[1295] sqlalchemy/trunk/test: got circular many-to-many relationships to work</title> </head> <body> <div id="msg"> <dl> <dt>Revision</dt> <dd>1295</dd> <dt>Author</dt> <dd>zzzeek</dd> <dt>Date</dt> <dd>2006-04-19 13:24:45 -0500 (Wed, 19 Apr 2006)</dd> </dl> <h3>Log Message</h3> <pre>got circular many-to-many relationships to work</pre> <h3>Modified Paths</h3> <ul> <li><a href="#sqlalchemytrunklibsqlalchemymappingpropertiespy">sqlalchemy/trunk/lib/sqlalchemy/mapping/properties.py</a></li> <li><a href="#sqlalchemytrunktestalltestspy">sqlalchemy/trunk/test/alltests.py</a></li> <li><a href="#sqlalchemytrunktestmanytomanypy">sqlalchemy/trunk/test/manytomany.py</a></li> <li><a href="#sqlalchemytrunktestrelationshipspy">sqlalchemy/trunk/test/relationships.py</a></li> </ul> </div> <div id="patch"> <h3>Diff</h3> <a id="sqlalchemytrunklibsqlalchemymappingpropertiespy"></a> <div class="modfile"><h4>Modified: sqlalchemy/trunk/lib/sqlalchemy/mapping/properties.py (1294 => 1295)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/trunk/lib/sqlalchemy/mapping/properties.py 2006-04-19 03:58:05 UTC (rev 1294) +++ sqlalchemy/trunk/lib/sqlalchemy/mapping/properties.py 2006-04-19 18:24:45 UTC (rev 1295) </span><span class="lines">@@ -225,13 +225,13 @@ </span><span class="cx"> """determines our 'direction', i.e. do we represent one to many, many to many, etc.""" </span><span class="cx"> #print self.key, repr(self.parent.table.name), repr(self.parent.primarytable.name), repr(self.foreignkey.table.name), repr(self.target), repr(self.foreigntable.name) </span><span class="cx"> </span><del>- if self.parent.table is self.target: </del><ins>+ if self.secondaryjoin is not None: + return PropertyLoader.MANYTOMANY + elif self.parent.table is self.target: </ins><span class="cx"> if self.foreignkey.primary_key: </span><span class="cx"> return PropertyLoader.MANYTOONE </span><span class="cx"> else: </span><span class="cx"> return PropertyLoader.ONETOMANY </span><del>- elif self.secondaryjoin is not None: - return PropertyLoader.MANYTOMANY </del><span class="cx"> elif self.foreigntable == self.mapper.noninherited_table: </span><span class="cx"> return PropertyLoader.ONETOMANY </span><span class="cx"> elif self.foreigntable == self.parent.noninherited_table: </span><span class="lines">@@ -657,13 +657,11 @@ </span><span class="cx"> binary.right = binds.setdefault(binary.right, </span><span class="cx"> sql.BindParamClause(binary.left._label, None, shortname = binary.right.name)) </span><span class="cx"> </span><del>- if secondaryjoin is not None: - lazywhere = sql.and_(primaryjoin, secondaryjoin) - else: - lazywhere = primaryjoin - lazywhere = lazywhere.copy_container() </del><ins>+ lazywhere = primaryjoin.copy_container() </ins><span class="cx"> li = BinaryVisitor(visit_binary) </span><span class="cx"> lazywhere.accept_visitor(li) </span><ins>+ if secondaryjoin is not None: + lazywhere = sql.and_(lazywhere, secondaryjoin) </ins><span class="cx"> return (lazywhere, binds) </span><span class="cx"> </span><span class="cx"> </span></span></pre></div> <a id="sqlalchemytrunktestalltestspy"></a> <div class="modfile"><h4>Modified: sqlalchemy/trunk/test/alltests.py (1294 => 1295)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/trunk/test/alltests.py 2006-04-19 03:58:05 UTC (rev 1294) +++ sqlalchemy/trunk/test/alltests.py 2006-04-19 18:24:45 UTC (rev 1295) </span><span class="lines">@@ -39,6 +39,7 @@ </span><span class="cx"> </span><span class="cx"> # ORM persistence </span><span class="cx"> 'objectstore', </span><ins>+ 'relationships', </ins><span class="cx"> </span><span class="cx"> # cyclical ORM persistence </span><span class="cx"> 'cycles', </span></span></pre></div> <a id="sqlalchemytrunktestmanytomanypy"></a> <div class="modfile"><h4>Modified: sqlalchemy/trunk/test/manytomany.py (1294 => 1295)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/trunk/test/manytomany.py 2006-04-19 03:58:05 UTC (rev 1294) +++ sqlalchemy/trunk/test/manytomany.py 2006-04-19 18:24:45 UTC (rev 1295) </span><span class="lines">@@ -7,6 +7,10 @@ </span><span class="cx"> '''represents a place''' </span><span class="cx"> def __init__(self, name=None): </span><span class="cx"> self.name = name </span><ins>+ def __str__(self): + return "(Place '%s')" % self.name + def __repr__(self): + return str(self) </ins><span class="cx"> </span><span class="cx"> class PlaceThingy(object): </span><span class="cx"> '''represents a thingy attached to a Place''' </span><span class="lines">@@ -58,29 +62,87 @@ </span><span class="cx"> Column('transition_id', Integer, ForeignKey('transition.transition_id')), </span><span class="cx"> ) </span><span class="cx"> </span><ins>+ global place_place + place_place = Table('place_place', db, + Column('pl1_id', Integer, ForeignKey('place.place_id')), + Column('pl2_id', Integer, ForeignKey('place.place_id')), + ) + </ins><span class="cx"> place.create() </span><span class="cx"> transition.create() </span><span class="cx"> place_input.create() </span><span class="cx"> place_output.create() </span><span class="cx"> place_thingy.create() </span><ins>+ place_place.create() </ins><span class="cx"> </span><span class="cx"> def tearDownAll(self): </span><ins>+ place_place.drop() </ins><span class="cx"> place_input.drop() </span><span class="cx"> place_output.drop() </span><span class="cx"> place_thingy.drop() </span><span class="cx"> place.drop() </span><span class="cx"> transition.drop() </span><ins>+ #testbase.db.tables.clear() </ins><span class="cx"> </span><span class="cx"> def setUp(self): </span><span class="cx"> objectstore.clear() </span><span class="cx"> clear_mappers() </span><span class="cx"> </span><span class="cx"> def tearDown(self): </span><ins>+ place_place.delete().execute() </ins><span class="cx"> place_input.delete().execute() </span><span class="cx"> place_output.delete().execute() </span><span class="cx"> transition.delete().execute() </span><span class="cx"> place.delete().execute() </span><span class="cx"> </span><ins>+ def testcircular(self): + """tests a many-to-many relationship from a table to itself.""" + + Place.mapper = mapper(Place, place) + + Place.mapper.add_property('places', relation( + Place.mapper, secondary=place_place, primaryjoin=place.c.place_id==place_place.c.pl1_id, + secondaryjoin=place.c.place_id==place_place.c.pl2_id, + order_by=place_place.c.pl2_id, + lazy=True, + )) + + p1 = Place('place1') + p2 = Place('place2') + p3 = Place('place3') + p4 = Place('place4') + p5 = Place('place5') + p6 = Place('place6') + p7 = Place('place7') + + p1.places.append(p2) + p1.places.append(p3) + p5.places.append(p6) + p6.places.append(p1) + p7.places.append(p1) + p1.places.append(p5) + p4.places.append(p3) + p3.places.append(p4) + objectstore.flush() + + objectstore.clear() + l = Place.mapper.select(order_by=place.c.place_id) + (p1, p2, p3, p4, p5, p6, p7) = l + assert p1.places == [p2,p3,p5] + assert p5.places == [p6] + assert p7.places == [p1] + assert p6.places == [p1] + assert p4.places == [p3] + assert p3.places == [p4] + assert p2.places == [] + + for p in l: + pp = p.places + self.echo("Place " + str(p) +" places " + repr(pp)) + + objectstore.delete(p1,p2,p3,p4,p5,p6,p7) + objectstore.flush() + </ins><span class="cx"> def testdouble(self): </span><span class="cx"> """tests that a mapper can have two eager relations to the same table, via </span><span class="cx"> two different association tables. aliases are required.""" </span><span class="lines">@@ -110,17 +172,14 @@ </span><span class="cx"> } </span><span class="cx"> ) </span><span class="cx"> </span><del>- def testcircular(self): - """tests a circular many-to-many relationship. this requires that the mapper - "break off" a new "mapper stub" to indicate a third depedendent processor.""" </del><ins>+ def testbidirectional(self): + """tests a bi-directional many-to-many relationship.""" </ins><span class="cx"> Place.mapper = mapper(Place, place) </span><span class="cx"> Transition.mapper = mapper(Transition, transition, properties = dict( </span><span class="cx"> inputs = relation(Place.mapper, place_output, lazy=True, backref='inputs'), </span><span class="cx"> outputs = relation(Place.mapper, place_input, lazy=True, backref='outputs'), </span><span class="cx"> ) </span><span class="cx"> ) </span><del>- #Place.mapper.add_property('inputs', relation(Transition.mapper, place_output, lazy=True, attributeext=attr.ListBackrefExtension('inputs'))) - #Place.mapper.add_property('outputs', relation(Transition.mapper, place_input, lazy=True, attributeext=attr.ListBackrefExtension('outputs'))) </del><span class="cx"> </span><span class="cx"> Place.eagermapper = Place.mapper.options( </span><span class="cx"> eagerload('inputs', selectalias='ip_alias'), </span><span class="lines">@@ -167,6 +226,7 @@ </span><span class="cx"> enrolTbl.drop() </span><span class="cx"> studentTbl.drop() </span><span class="cx"> courseTbl.drop() </span><ins>+ #testbase.db.tables.clear() </ins><span class="cx"> </span><span class="cx"> def setUp(self): </span><span class="cx"> objectstore.clear() </span><span class="lines">@@ -242,6 +302,7 @@ </span><span class="cx"> c2a1.drop() </span><span class="cx"> a.drop() </span><span class="cx"> c.drop() </span><ins>+ #testbase.db.tables.clear() </ins><span class="cx"> </span><span class="cx"> def testbasic(self): </span><span class="cx"> class C(object):pass </span></span></pre></div> <a id="sqlalchemytrunktestrelationshipspy"></a> <div class="modfile"><h4>Modified: sqlalchemy/trunk/test/relationships.py (1294 => 1295)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/trunk/test/relationships.py 2006-04-19 03:58:05 UTC (rev 1294) +++ sqlalchemy/trunk/test/relationships.py 2006-04-19 18:24:45 UTC (rev 1295) </span><span class="lines">@@ -10,11 +10,14 @@ </span><span class="cx"> </span><span class="cx"> </span><span class="cx"> class RelationTest(testbase.PersistTest): </span><del>- """this is essentially an extension of the "dependency.py" topological sort test. this exposes - a particular issue that doesnt always occur with the straight dependency tests, due to the nature - of the sort being different based on random conditions""" </del><ins>+ """this is essentially an extension of the "dependency.py" topological sort test. + in this test, a table is dependent on two other tables that are otherwise unrelated to each other. + the dependency sort must insure that this childmost table is below both parent tables in the outcome + (a bug existed where this was not always the case). + while the straight topological sort tests should expose this, since the sorting can be different due + to subtle differences in program execution, this test case was exposing the bug whereas the simpler tests + were not.""" </ins><span class="cx"> def setUpAll(self): </span><del>- testbase.db.tables.clear() </del><span class="cx"> global tbl_a </span><span class="cx"> global tbl_b </span><span class="cx"> global tbl_c </span><span class="lines">@@ -81,6 +84,9 @@ </span><span class="cx"> tbl_c.drop() </span><span class="cx"> tbl_b.drop() </span><span class="cx"> tbl_a.drop() </span><ins>+ + def tearDownAll(self): + testbase.db.tables.clear() </ins><span class="cx"> </span><span class="cx"> def testDeleteRootTable(self): </span><span class="cx"> session = objectstore.get_session() </span></span></pre> </div> </div> </body> </html> |
From: <co...@sq...> - 2006-04-19 03:58: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>[1294] sqlalchemy/branches/schema/doc/build: dev</title> </head> <body> <div id="msg"> <dl> <dt>Revision</dt> <dd>1294</dd> <dt>Author</dt> <dd>zzzeek</dd> <dt>Date</dt> <dd>2006-04-18 22:58:05 -0500 (Tue, 18 Apr 2006)</dd> </dl> <h3>Log Message</h3> <pre>dev</pre> <h3>Modified Paths</h3> <ul> <li><a href="#sqlalchemybranchesschemadocbuildcontentdocument_basemyt">sqlalchemy/branches/schema/doc/build/content/document_base.myt</a></li> <li><a href="#sqlalchemybranchesschemadocbuildcontenttutorialtxt">sqlalchemy/branches/schema/doc/build/content/tutorial.txt</a></li> <li><a href="#sqlalchemybranchesschemadocbuildtestdocspy">sqlalchemy/branches/schema/doc/build/testdocs.py</a></li> <li><a href="#sqlalchemybranchesschemadocbuildtxt2mytpy">sqlalchemy/branches/schema/doc/build/txt2myt.py</a></li> </ul> </div> <div id="patch"> <h3>Diff</h3> <a id="sqlalchemybranchesschemadocbuildcontentdocument_basemyt"></a> <div class="modfile"><h4>Modified: sqlalchemy/branches/schema/doc/build/content/document_base.myt (1293 => 1294)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/schema/doc/build/content/document_base.myt 2006-04-19 02:26:23 UTC (rev 1293) +++ sqlalchemy/branches/schema/doc/build/content/document_base.myt 2006-04-19 03:58:05 UTC (rev 1294) </span><span class="lines">@@ -3,7 +3,7 @@ </span><span class="cx"> <%python scope="global"> </span><span class="cx"> </span><span class="cx"> files = [ </span><del>- #'tutorial', </del><ins>+ 'tutorial', </ins><span class="cx"> 'trailmap', </span><span class="cx"> 'pooling', </span><span class="cx"> 'dbengine', </span><span class="lines">@@ -24,7 +24,7 @@ </span><span class="cx"> onepage='documentation' </span><span class="cx"> index='index' </span><span class="cx"> title='SQLAlchemy Documentation' </span><del>- version = '0.1.5' </del><ins>+ version = '0.2.0' </ins><span class="cx"> </%attr> </span><span class="cx"> </span><span class="cx"> <%method title> </span></span></pre></div> <a id="sqlalchemybranchesschemadocbuildcontenttutorialtxt"></a> <div class="modfile"><h4>Modified: sqlalchemy/branches/schema/doc/build/content/tutorial.txt (1293 => 1294)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/schema/doc/build/content/tutorial.txt 2006-04-19 02:26:23 UTC (rev 1293) +++ sqlalchemy/branches/schema/doc/build/content/tutorial.txt 2006-04-19 03:58:05 UTC (rev 1294) </span><span class="lines">@@ -90,7 +90,7 @@ </span><span class="cx"> </span><span class="cx"> 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: </span><span class="cx"> </span><del>- >>> users = Table('users', db, autoload = True) </del><ins>+ >>> users = Table('users', metadata, autoload=True) </ins><span class="cx"> >>> list(users.columns)[0].name </span><span class="cx"> 'user_id' </span><span class="cx"> </span><span class="lines">@@ -110,10 +110,10 @@ </span><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><del>- <sqlalchemy.engine.ResultProxy instance at 0x...> </del><ins>+ <sqlalchemy.engine.base.ResultProxy instance at 0x...> </ins><span class="cx"> ... </span><span class="cx"> >>> i.execute(user_name = 'Mary', password = 'secure') # doctest:+ELLIPSIS </span><del>- <sqlalchemy.engine.ResultProxy instance at 0x...> </del><ins>+ <sqlalchemy.engine.base.ResultProxy instance at 0x...> </ins><span class="cx"> </span><span class="cx"> 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. </span><span class="cx"> </span><span class="lines">@@ -134,7 +134,7 @@ </span><span class="cx"> This time, we won't ignore the return value of `execute()`: </span><span class="cx"> </span><span class="cx"> >>> r # doctest:+ELLIPSIS </span><del>- <sqlalchemy.engine.ResultProxy instance at 0x...> </del><ins>+ <sqlalchemy.engine.base.ResultProxy instance at 0x...> </ins><span class="cx"> >>> r.keys </span><span class="cx"> ['user_id', 'user_name', 'password'] </span><span class="cx"> >>> row = r.fetchone() </span></span></pre></div> <a id="sqlalchemybranchesschemadocbuildtestdocspy"></a> <div class="modfile"><h4>Modified: sqlalchemy/branches/schema/doc/build/testdocs.py (1293 => 1294)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/schema/doc/build/testdocs.py 2006-04-19 02:26:23 UTC (rev 1293) +++ sqlalchemy/branches/schema/doc/build/testdocs.py 2006-04-19 03:58:05 UTC (rev 1294) </span><span class="lines">@@ -1,7 +1,19 @@ </span><ins>+import sys +sys.path = ['../../lib', './lib/'] + sys.path + </ins><span class="cx"> import os </span><span class="cx"> import re </span><span class="cx"> import doctest </span><ins>+import sqlalchemy.util as util </ins><span class="cx"> </span><ins>+# monkeypatch a plain logger +class Logger(object): + def __init__(self, *args, **kwargs): + pass + def write(self, msg): + print msg +util.Logger = Logger + </ins><span class="cx"> def teststring(s, name, globs=None, verbose=None, report=True, </span><span class="cx"> optionflags=0, extraglobs=None, raise_on_error=False, </span><span class="cx"> parser=doctest.DocTestParser()): </span><span class="lines">@@ -37,7 +49,7 @@ </span><span class="cx"> def replace_file(s, oldfile, newfile): </span><span class="cx"> engine = r"sqlite:///" + oldfile </span><span class="cx"> engine = re.compile(engine, re.MULTILINE) </span><del>- s, n = re.subn(engine, "sqlite:///" + newfile, s, 1) </del><ins>+ s, n = re.subn(engine, "sqlite:///" + newfile, s) </ins><span class="cx"> if not n: </span><span class="cx"> raise ValueError("Couldn't find suitable create_engine call to replace '%s' in it" % oldfile) </span><span class="cx"> return s </span></span></pre></div> <a id="sqlalchemybranchesschemadocbuildtxt2mytpy"></a> <div class="modfile"><h4>Modified: sqlalchemy/branches/schema/doc/build/txt2myt.py (1293 => 1294)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/schema/doc/build/txt2myt.py 2006-04-19 02:26:23 UTC (rev 1293) +++ sqlalchemy/branches/schema/doc/build/txt2myt.py 2006-04-19 03:58:05 UTC (rev 1294) </span><span class="lines">@@ -274,6 +274,6 @@ </span><span class="cx"> print inname, '->', outname </span><span class="cx"> input = file(inname).read() </span><span class="cx"> html = markdown.markdown(input) </span><del>- file(inname[:-3] + "html", 'w').write(html) </del><ins>+ #file(inname[:-3] + "html", 'w').write(html) </ins><span class="cx"> myt = html2myghtydoc(html) </span><span class="cx"> file(outname, 'w').write(myt) </span></span></pre> </div> </div> </body> </html> |
From: <co...@sq...> - 2006-04-19 02:26:33
|
<!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>[1293] sqlalchemy/branches/schema/doc/build: dev</title> </head> <body> <div id="msg"> <dl> <dt>Revision</dt> <dd>1293</dd> <dt>Author</dt> <dd>zzzeek</dd> <dt>Date</dt> <dd>2006-04-18 21:26:23 -0500 (Tue, 18 Apr 2006)</dd> </dl> <h3>Log Message</h3> <pre>dev</pre> <h3>Modified Paths</h3> <ul> <li><a href="#sqlalchemybranchesschemadocbuildcontenttutorialtxt">sqlalchemy/branches/schema/doc/build/content/tutorial.txt</a></li> <li><a href="#sqlalchemybranchesschemadocbuildtestdocspy">sqlalchemy/branches/schema/doc/build/testdocs.py</a></li> </ul> </div> <div id="patch"> <h3>Diff</h3> <a id="sqlalchemybranchesschemadocbuildcontenttutorialtxt"></a> <div class="modfile"><h4>Modified: sqlalchemy/branches/schema/doc/build/content/tutorial.txt (1292 => 1293)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/schema/doc/build/content/tutorial.txt 2006-04-19 02:00:16 UTC (rev 1292) +++ sqlalchemy/branches/schema/doc/build/content/tutorial.txt 2006-04-19 02:26:23 UTC (rev 1293) </span><span class="lines">@@ -1,148 +1,165 @@ </span><del>-Tutorial -======== -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. - -[manual]: rel:howtoread - -Installation ------------- - -### Installing SQLAlchemy {@name=sqlalchemy} - -Installing SQLAlchemy from scratch is most easily achieved with [setuptools][]. ([setuptools installation][install setuptools]). 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 a Database API {@name=dbms} - -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. - -[DBAPI]: http://www.python.org/doc/peps/pep-0249/ - -To work with SQLite, you'll need: - - * [pysqlite][] - Python interface for SQLite - * SQLite library - -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]. - -[supported dbms]: rel:dbengine_establishing -[sqlite]: http://sqlite.org/ -[pysqlite]: http://pysqlite.org/ -[pysqlite packages]: http://initd.org/tracker/pysqlite/wiki/PysqlitePackages - -Getting Started {@name=gettingstarted} --------------------------- - -### Connecting to the Database - -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". - - >>> from sqlalchemy import * - >>> db = create_engine('sqlite://filename=tutorial.db') - -For full information on creating database engines, including those for SQLite and others, see [dbengine](rel:dbengine). - -### Creating a Table {@name=table} - -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: - - >>> users = Table('users', db, - ... Column('user_id', Integer, primary_key = True), - ... Column('user_name', String(40)), - ... Column('password', String(80)) - ... ) - -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: - - >>> 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, 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: - - >>> users = Table('users', db, autoload = True) - >>> list(users.columns)[0].name - 'user_id' - -Documentation on table metadata is available in [metadata](rel:metadata). - -### Inserting Rows - -Inserting is achieved via the `insert()` method, which defines a *clause object* representing an INSERT statement: - - >>> i = users.insert() - >>> i # doctest:+ELLIPSIS - <sqlalchemy.sql.Insert object at 0x...> - >>> print i - INSERT INTO users (user_id, user_name, password) VALUES (?, ?, ?) - -The `execute()` method of the clause object executes the statement at the database level: - - >>> 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...> - -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. - -[SQL injection]: http://en.wikipedia.org/wiki/SQL_injection - -Documentation on inserting: [sql_insert](rel:sql_insert). - -### Constructing Queries - -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: - - >>> s = users.select() - >>> print s - SELECT users.user_id, users.user_name, users.password - FROM users - >>> r = s.execute() - -This time, we won't ignore the 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')] - -Documentation on selecting: [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 ----------- </del><ins>+Tutorial +======== +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. + +[manual]: rel:howtoread + +Installation +------------ + +### Installing SQLAlchemy {@name=sqlalchemy} + +Installing SQLAlchemy from scratch is most easily achieved with [setuptools][]. ([setuptools installation][install setuptools]). 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 a Database API {@name=dbms} + +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. + +[DBAPI]: http://www.python.org/doc/peps/pep-0249/ + +To work with SQLite, you'll need: + + * [pysqlite][] - Python interface for SQLite + * SQLite library + +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]. + +[supported dbms]: rel:dbengine_establishing +[sqlite]: http://sqlite.org/ +[pysqlite]: http://pysqlite.org/ +[pysqlite packages]: http://initd.org/tracker/pysqlite/wiki/PysqlitePackages + +Getting Started {@name=gettingstarted} +-------------------------- + +### Imports + +SQLAlchemy provides the entire namespace of everything you'll need under the module name `sqlalchemy`. For the purposes of this tutorial, we will import its full list of symbols into our own local namespace. We also will be using a *mod* that provides access to thread-managed connection and session objects, which will greatly simplifies our code. A *mod* is a module that augments the core functionality of SQLAlchemy with additional functionality, and only needs to be imported once within an application. + + >>> from sqlalchemy import * + >>> import sqlalchemy.mods.threadlocal + +### Connecting to the Database + +After our imports, the next thing we need is a handle to the desired database, represented by an `Engine` 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". + + >>> db = create_engine('sqlite:///tutorial.db') + + +For full information on creating database engines, including those for SQLite and others, see [dbengine](rel:dbengine). + +### Creating a Table {@name=table} + +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. + +Firstly, your Tables have to belong to a collection called `MetaData`. We will create a handy form of `MetaData` that automatically connects to our `Engine` (connecting a schema object to an Engine is called *binding*): + + >>> metadata = BoundMetaData(db) + +An equivalent operation is to create the `BoundMetaData` object directly with an Engine URL, which calls the `create_engine` call for us: + + >>> metadata = BoundMetaData('sqlite:///tutorial.db') + +With `metadata` as our established home for tables, lets make a Table for it: + + >>> users = Table('users', metadata, + ... Column('user_id', Integer, primary_key=True), + ... Column('user_name', String(40)), + ... Column('password', String(80)) + ... ) + +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, by setting the `echo` flag on the `Engine` associated with our `BoundMetaData`: + + >>> metadata.engine.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) + ) + ... + >>> metadata.engine.echo = False # you can skip this if you want to keep logging SQL statements + +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: + + >>> users = Table('users', db, autoload = True) + >>> list(users.columns)[0].name + 'user_id' + +Documentation on table metadata is available in [metadata](rel:metadata). + +### Inserting Rows + +Inserting is achieved via the `insert()` method, which defines a *clause object* representing an INSERT statement: + + >>> i = users.insert() + >>> i # doctest:+ELLIPSIS + <sqlalchemy.sql.Insert object at 0x...> + >>> print i + INSERT INTO users (user_id, user_name, password) VALUES (?, ?, ?) + +The `execute()` method of the clause object executes the statement at the database level: + + >>> 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...> + +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. + +[SQL injection]: http://en.wikipedia.org/wiki/SQL_injection + +Documentation on inserting: [sql_insert](rel:sql_insert). + +### Constructing Queries + +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: + + >>> s = users.select() + >>> print s + SELECT users.user_id, users.user_name, users.password + FROM users + >>> r = s.execute() + +This time, we won't ignore the 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')] + +Documentation on selecting: [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="sqlalchemybranchesschemadocbuildtestdocspy"></a> <div class="modfile"><h4>Modified: sqlalchemy/branches/schema/doc/build/testdocs.py (1292 => 1293)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/schema/doc/build/testdocs.py 2006-04-19 02:00:16 UTC (rev 1292) +++ sqlalchemy/branches/schema/doc/build/testdocs.py 2006-04-19 02:26:23 UTC (rev 1293) </span><span class="lines">@@ -35,9 +35,9 @@ </span><span class="cx"> return runner.failures, runner.tries </span><span class="cx"> </span><span class="cx"> def replace_file(s, oldfile, newfile): </span><del>- engine = r"(^\s*>>>\s*[a-zA-Z_]\w*\s*=\s*create_engine\('sqlite',\s*\{'filename':\s*')" + oldfile+ "('\}\)$)" </del><ins>+ engine = r"sqlite:///" + oldfile </ins><span class="cx"> engine = re.compile(engine, re.MULTILINE) </span><del>- s, n = re.subn(engine, r'\1' + newfile + r'\2', s, 1) </del><ins>+ s, n = re.subn(engine, "sqlite:///" + newfile, s, 1) </ins><span class="cx"> if not n: </span><span class="cx"> raise ValueError("Couldn't find suitable create_engine call to replace '%s' in it" % oldfile) </span><span class="cx"> return s </span></span></pre> </div> </div> </body> </html> |
From: <co...@sq...> - 2006-04-19 02:00: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>[1292] sqlalchemy/branches/schema/test: dev, mods work through straight import now</title> </head> <body> <div id="msg"> <dl> <dt>Revision</dt> <dd>1292</dd> <dt>Author</dt> <dd>zzzeek</dd> <dt>Date</dt> <dd>2006-04-18 21:00:16 -0500 (Tue, 18 Apr 2006)</dd> </dl> <h3>Log Message</h3> <pre>dev, mods work through straight import now</pre> <h3>Modified Paths</h3> <ul> <li><a href="#sqlalchemybranchesschemalibsqlalchemy__init__py">sqlalchemy/branches/schema/lib/sqlalchemy/__init__.py</a></li> <li><a href="#sqlalchemybranchesschemalibsqlalchemymods__init__py">sqlalchemy/branches/schema/lib/sqlalchemy/mods/__init__.py</a></li> <li><a href="#sqlalchemybranchesschemalibsqlalchemymodslegacy_sessionpy">sqlalchemy/branches/schema/lib/sqlalchemy/mods/legacy_session.py</a></li> <li><a href="#sqlalchemybranchesschemalibsqlalchemymodsselectresultspy">sqlalchemy/branches/schema/lib/sqlalchemy/mods/selectresults.py</a></li> <li><a href="#sqlalchemybranchesschemalibsqlalchemymodsthreadlocalpy">sqlalchemy/branches/schema/lib/sqlalchemy/mods/threadlocal.py</a></li> <li><a href="#sqlalchemybranchesschematestcyclespy">sqlalchemy/branches/schema/test/cycles.py</a></li> <li><a href="#sqlalchemybranchesschematestobjectstorepy">sqlalchemy/branches/schema/test/objectstore.py</a></li> <li><a href="#sqlalchemybranchesschematesttestbasepy">sqlalchemy/branches/schema/test/testbase.py</a></li> </ul> </div> <div id="patch"> <h3>Diff</h3> <a id="sqlalchemybranchesschemalibsqlalchemy__init__py"></a> <div class="modfile"><h4>Modified: sqlalchemy/branches/schema/lib/sqlalchemy/__init__.py (1291 => 1292)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/schema/lib/sqlalchemy/__init__.py 2006-04-19 01:32:30 UTC (rev 1291) +++ sqlalchemy/branches/schema/lib/sqlalchemy/__init__.py 2006-04-19 02:00:16 UTC (rev 1292) </span><span class="lines">@@ -18,9 +18,7 @@ </span><span class="cx"> </span><span class="cx"> create_engine = sqlalchemy.engine.create_engine </span><span class="cx"> </span><del>-from sqlalchemy.mods import install_mods </del><span class="cx"> </span><del>- </del><span class="cx"> def global_connect(*args, **kwargs): </span><span class="cx"> sqlalchemy.schema.default_metadata.connect(*args, **kwargs) </span><span class="cx"> </span><span class="cx">\ No newline at end of file </span></span></pre></div> <a id="sqlalchemybranchesschemalibsqlalchemymods__init__py"></a> <div class="modfile"><h4>Modified: sqlalchemy/branches/schema/lib/sqlalchemy/mods/__init__.py (1291 => 1292)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/schema/lib/sqlalchemy/mods/__init__.py 2006-04-19 01:32:30 UTC (rev 1291) +++ sqlalchemy/branches/schema/lib/sqlalchemy/mods/__init__.py 2006-04-19 02:00:16 UTC (rev 1292) </span><span class="lines">@@ -1,15 +0,0 @@ </span><del>-import sets - -def install_mods(*mods): - for mod in mods: - if isinstance(mod, str): - _mod = getattr(__import__('sqlalchemy.mods.%s' % mod).mods, mod) - if _mod not in installed_mods: - _mod.install_plugin() - installed_mods.add(_mod) - else: - if mod not in installed_mods: - mod.install_plugin() - installed_mods.add(mod) - -installed_mods = sets.Set() </del><span class="cx">\ No newline at end of file </span></span></pre></div> <a id="sqlalchemybranchesschemalibsqlalchemymodslegacy_sessionpy"></a> <div class="modfile"><h4>Modified: sqlalchemy/branches/schema/lib/sqlalchemy/mods/legacy_session.py (1291 => 1292)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/schema/lib/sqlalchemy/mods/legacy_session.py 2006-04-19 01:32:30 UTC (rev 1291) +++ sqlalchemy/branches/schema/lib/sqlalchemy/mods/legacy_session.py 2006-04-19 02:00:16 UTC (rev 1292) </span><span class="lines">@@ -4,7 +4,7 @@ </span><span class="cx"> import sqlalchemy.util as util </span><span class="cx"> import sqlalchemy </span><span class="cx"> </span><del>-sqlalchemy.install_mods('threadlocal') </del><ins>+import sqlalchemy.mods.threadlocal </ins><span class="cx"> </span><span class="cx"> class LegacySession(objectstore.Session): </span><span class="cx"> def __init__(self, nest_on=None, hash_key=None, **kwargs): </span><span class="lines">@@ -135,4 +135,5 @@ </span><span class="cx"> objectstore.uow = uow </span><span class="cx"> objectstore.push_session = push_session </span><span class="cx"> objectstore.pop_session = pop_session </span><del>- objectstore.using_session = using_session </del><span class="cx">\ No newline at end of file </span><ins>+ objectstore.using_session = using_session +install_plugin() </ins></span></pre></div> <a id="sqlalchemybranchesschemalibsqlalchemymodsselectresultspy"></a> <div class="modfile"><h4>Modified: sqlalchemy/branches/schema/lib/sqlalchemy/mods/selectresults.py (1291 => 1292)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/schema/lib/sqlalchemy/mods/selectresults.py 2006-04-19 01:32:30 UTC (rev 1291) +++ sqlalchemy/branches/schema/lib/sqlalchemy/mods/selectresults.py 2006-04-19 02:00:16 UTC (rev 1292) </span><span class="lines">@@ -2,8 +2,6 @@ </span><span class="cx"> </span><span class="cx"> import sqlalchemy.mapping as mapping </span><span class="cx"> </span><del>-def install_plugin(): - mapping.global_extensions.append(SelectResultsExt) </del><span class="cx"> </span><span class="cx"> class SelectResultsExt(mapping.MapperExtension): </span><span class="cx"> def select_by(self, query, *args, **params): </span><span class="lines">@@ -84,3 +82,7 @@ </span><span class="cx"> </span><span class="cx"> def __iter__(self): </span><span class="cx"> return iter(self._query.select_whereclause(self._clause, **self._ops)) </span><ins>+ +def install_plugin(): + mapping.global_extensions.append(SelectResultsExt) +install_plugin() </ins></span></pre></div> <a id="sqlalchemybranchesschemalibsqlalchemymodsthreadlocalpy"></a> <div class="modfile"><h4>Modified: sqlalchemy/branches/schema/lib/sqlalchemy/mods/threadlocal.py (1291 => 1292)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/schema/lib/sqlalchemy/mods/threadlocal.py 2006-04-19 01:32:30 UTC (rev 1291) +++ sqlalchemy/branches/schema/lib/sqlalchemy/mods/threadlocal.py 2006-04-19 02:00:16 UTC (rev 1292) </span><span class="lines">@@ -101,4 +101,5 @@ </span><span class="cx"> setattr(objectstore, name, getattr(mod, name)) </span><span class="cx"> </span><span class="cx"> objectstore.session_registry = util.ScopedRegistry(objectstore.Session) # Default session registry </span><del>- engine.default_strategy = 'threadlocal' </del><span class="cx">\ No newline at end of file </span><ins>+ engine.default_strategy = 'threadlocal' +install_plugin() </ins></span></pre></div> <a id="sqlalchemybranchesschematestcyclespy"></a> <div class="modfile"><h4>Modified: sqlalchemy/branches/schema/test/cycles.py (1291 => 1292)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/schema/test/cycles.py 2006-04-19 01:32:30 UTC (rev 1291) +++ sqlalchemy/branches/schema/test/cycles.py 2006-04-19 02:00:16 UTC (rev 1292) </span><span class="lines">@@ -283,50 +283,50 @@ </span><span class="cx"> ), </span><span class="cx"> ( </span><span class="cx"> "INSERT INTO ball (person_id) VALUES (:person_id)", </span><del>- lambda:{'person_id':p.id} </del><ins>+ lambda ctx:{'person_id':p.id} </ins><span class="cx"> ), </span><span class="cx"> ( </span><span class="cx"> "INSERT INTO ball (person_id) VALUES (:person_id)", </span><del>- lambda:{'person_id':p.id} </del><ins>+ lambda ctx:{'person_id':p.id} </ins><span class="cx"> ), </span><span class="cx"> ( </span><span class="cx"> "INSERT INTO ball (person_id) VALUES (:person_id)", </span><del>- lambda:{'person_id':p.id} </del><ins>+ lambda ctx:{'person_id':p.id} </ins><span class="cx"> ), </span><span class="cx"> ( </span><span class="cx"> "INSERT INTO ball (person_id) VALUES (:person_id)", </span><del>- lambda:{'person_id':p.id} </del><ins>+ lambda ctx:{'person_id':p.id} </ins><span class="cx"> ), </span><span class="cx"> ( </span><span class="cx"> "UPDATE person SET favoriteBall_id=:favoriteBall_id WHERE person.id = :person_id", </span><del>- lambda:{'favoriteBall_id':p.favorateBall.id,'person_id':p.id} </del><ins>+ lambda ctx:{'favoriteBall_id':p.favorateBall.id,'person_id':p.id} </ins><span class="cx"> ) </span><span class="cx"> ], </span><span class="cx"> with_sequences= [ </span><span class="cx"> ( </span><span class="cx"> "INSERT INTO person (id, favoriteBall_id) VALUES (:id, :favoriteBall_id)", </span><del>- lambda:{'id':db.last_inserted_ids()[0], 'favoriteBall_id': None} </del><ins>+ lambda ctx:{'id':ctx.last_inserted_ids()[0], 'favoriteBall_id': None} </ins><span class="cx"> ), </span><span class="cx"> ( </span><span class="cx"> "INSERT INTO ball (id, person_id) VALUES (:id, :person_id)", </span><del>- lambda:{'id':db.last_inserted_ids()[0],'person_id':p.id} </del><ins>+ lambda ctx:{'id':ctx.last_inserted_ids()[0],'person_id':p.id} </ins><span class="cx"> ), </span><span class="cx"> ( </span><span class="cx"> "INSERT INTO ball (id, person_id) VALUES (:id, :person_id)", </span><del>- lambda:{'id':db.last_inserted_ids()[0],'person_id':p.id} </del><ins>+ lambda ctx:{'id':ctx.last_inserted_ids()[0],'person_id':p.id} </ins><span class="cx"> ), </span><span class="cx"> ( </span><span class="cx"> "INSERT INTO ball (id, person_id) VALUES (:id, :person_id)", </span><del>- lambda:{'id':db.last_inserted_ids()[0],'person_id':p.id} </del><ins>+ lambda ctx:{'id':ctx.last_inserted_ids()[0],'person_id':p.id} </ins><span class="cx"> ), </span><span class="cx"> ( </span><span class="cx"> "INSERT INTO ball (id, person_id) VALUES (:id, :person_id)", </span><del>- lambda:{'id':db.last_inserted_ids()[0],'person_id':p.id} </del><ins>+ lambda ctx:{'id':ctx.last_inserted_ids()[0],'person_id':p.id} </ins><span class="cx"> ), </span><span class="cx"> # heres the post update </span><span class="cx"> ( </span><span class="cx"> "UPDATE person SET favoriteBall_id=:favoriteBall_id WHERE person.id = :person_id", </span><del>- lambda:{'favoriteBall_id':p.favorateBall.id,'person_id':p.id} </del><ins>+ lambda ctx:{'favoriteBall_id':p.favorateBall.id,'person_id':p.id} </ins><span class="cx"> ) </span><span class="cx"> ]) </span><span class="cx"> objectstore.delete(p) </span><span class="lines">@@ -334,17 +334,17 @@ </span><span class="cx"> # heres the post update (which is a pre-update with deletes) </span><span class="cx"> ( </span><span class="cx"> "UPDATE person SET favoriteBall_id=:favoriteBall_id WHERE person.id = :person_id", </span><del>- lambda:{'person_id': p.id, 'favoriteBall_id': None} </del><ins>+ lambda ctx:{'person_id': p.id, 'favoriteBall_id': None} </ins><span class="cx"> ), </span><span class="cx"> ( </span><span class="cx"> "DELETE FROM ball WHERE ball.id = :id", </span><span class="cx"> None </span><span class="cx"> # order cant be predicted, but something like: </span><del>- #lambda:[{'id': 1L}, {'id': 4L}, {'id': 3L}, {'id': 2L}] </del><ins>+ #lambda ctx:[{'id': 1L}, {'id': 4L}, {'id': 3L}, {'id': 2L}] </ins><span class="cx"> ), </span><span class="cx"> ( </span><span class="cx"> "DELETE FROM person WHERE person.id = :id", </span><del>- lambda:[{'id': p.id}] </del><ins>+ lambda ctx:[{'id': p.id}] </ins><span class="cx"> ) </span><span class="cx"> </span><span class="cx"> </span><span class="lines">@@ -396,62 +396,62 @@ </span><span class="cx"> ), </span><span class="cx"> ( </span><span class="cx"> "INSERT INTO person (favoriteBall_id) VALUES (:favoriteBall_id)", </span><del>- lambda:{'favoriteBall_id':b.id} </del><ins>+ lambda ctx:{'favoriteBall_id':b.id} </ins><span class="cx"> ), </span><span class="cx"> # heres the post update on each one-to-many item </span><span class="cx"> ( </span><span class="cx"> "UPDATE ball SET person_id=:person_id WHERE ball.id = :ball_id", </span><del>- lambda:{'person_id':p.id,'ball_id':b.id} </del><ins>+ lambda ctx:{'person_id':p.id,'ball_id':b.id} </ins><span class="cx"> ), </span><span class="cx"> ( </span><span class="cx"> "UPDATE ball SET person_id=:person_id WHERE ball.id = :ball_id", </span><del>- lambda:{'person_id':p.id,'ball_id':b2.id} </del><ins>+ lambda ctx:{'person_id':p.id,'ball_id':b2.id} </ins><span class="cx"> ), </span><span class="cx"> ( </span><span class="cx"> "UPDATE ball SET person_id=:person_id WHERE ball.id = :ball_id", </span><del>- lambda:{'person_id':p.id,'ball_id':b3.id} </del><ins>+ lambda ctx:{'person_id':p.id,'ball_id':b3.id} </ins><span class="cx"> ), </span><span class="cx"> ( </span><span class="cx"> "UPDATE ball SET person_id=:person_id WHERE ball.id = :ball_id", </span><del>- lambda:{'person_id':p.id,'ball_id':b4.id} </del><ins>+ lambda ctx:{'person_id':p.id,'ball_id':b4.id} </ins><span class="cx"> ), </span><span class="cx"> ], </span><span class="cx"> with_sequences=[ </span><span class="cx"> ( </span><span class="cx"> "INSERT INTO ball (id, person_id) VALUES (:id, :person_id)", </span><del>- lambda:{'id':db.last_inserted_ids()[0], 'person_id':None} </del><ins>+ lambda ctx:{'id':ctx.last_inserted_ids()[0], 'person_id':None} </ins><span class="cx"> ), </span><span class="cx"> ( </span><span class="cx"> "INSERT INTO ball (id, person_id) VALUES (:id, :person_id)", </span><del>- lambda:{'id':db.last_inserted_ids()[0], 'person_id':None} </del><ins>+ lambda ctx:{'id':ctx.last_inserted_ids()[0], 'person_id':None} </ins><span class="cx"> ), </span><span class="cx"> ( </span><span class="cx"> "INSERT INTO ball (id, person_id) VALUES (:id, :person_id)", </span><del>- lambda:{'id':db.last_inserted_ids()[0], 'person_id':None} </del><ins>+ lambda ctx:{'id':ctx.last_inserted_ids()[0], 'person_id':None} </ins><span class="cx"> ), </span><span class="cx"> ( </span><span class="cx"> "INSERT INTO ball (id, person_id) VALUES (:id, :person_id)", </span><del>- lambda:{'id':db.last_inserted_ids()[0], 'person_id':None} </del><ins>+ lambda ctx:{'id':ctx.last_inserted_ids()[0], 'person_id':None} </ins><span class="cx"> ), </span><span class="cx"> ( </span><span class="cx"> "INSERT INTO person (id, favoriteBall_id) VALUES (:id, :favoriteBall_id)", </span><del>- lambda:{'id':db.last_inserted_ids()[0], 'favoriteBall_id':b.id} </del><ins>+ lambda ctx:{'id':ctx.last_inserted_ids()[0], 'favoriteBall_id':b.id} </ins><span class="cx"> ), </span><span class="cx"> ( </span><span class="cx"> "UPDATE ball SET person_id=:person_id WHERE ball.id = :ball_id", </span><del>- lambda:{'person_id':p.id,'ball_id':b.id} </del><ins>+ lambda ctx:{'person_id':p.id,'ball_id':b.id} </ins><span class="cx"> ), </span><span class="cx"> ( </span><span class="cx"> "UPDATE ball SET person_id=:person_id WHERE ball.id = :ball_id", </span><del>- lambda:{'person_id':p.id,'ball_id':b2.id} </del><ins>+ lambda ctx:{'person_id':p.id,'ball_id':b2.id} </ins><span class="cx"> ), </span><span class="cx"> ( </span><span class="cx"> "UPDATE ball SET person_id=:person_id WHERE ball.id = :ball_id", </span><del>- lambda:{'person_id':p.id,'ball_id':b3.id} </del><ins>+ lambda ctx:{'person_id':p.id,'ball_id':b3.id} </ins><span class="cx"> ), </span><span class="cx"> ( </span><span class="cx"> "UPDATE ball SET person_id=:person_id WHERE ball.id = :ball_id", </span><del>- lambda:{'person_id':p.id,'ball_id':b4.id} </del><ins>+ lambda ctx:{'person_id':p.id,'ball_id':b4.id} </ins><span class="cx"> ), </span><span class="cx"> ]) </span><span class="cx"> </span><span class="lines">@@ -459,29 +459,29 @@ </span><span class="cx"> self.assert_sql(db, lambda: objectstore.get_session().flush(), [ </span><span class="cx"> ( </span><span class="cx"> "UPDATE ball SET person_id=:person_id WHERE ball.id = :ball_id", </span><del>- lambda:{'person_id': None, 'ball_id': b.id} </del><ins>+ lambda ctx:{'person_id': None, 'ball_id': b.id} </ins><span class="cx"> ), </span><span class="cx"> ( </span><span class="cx"> "UPDATE ball SET person_id=:person_id WHERE ball.id = :ball_id", </span><del>- lambda:{'person_id': None, 'ball_id': b2.id} </del><ins>+ lambda ctx:{'person_id': None, 'ball_id': b2.id} </ins><span class="cx"> ), </span><span class="cx"> ( </span><span class="cx"> "UPDATE ball SET person_id=:person_id WHERE ball.id = :ball_id", </span><del>- lambda:{'person_id': None, 'ball_id': b3.id} </del><ins>+ lambda ctx:{'person_id': None, 'ball_id': b3.id} </ins><span class="cx"> ), </span><span class="cx"> ( </span><span class="cx"> "UPDATE ball SET person_id=:person_id WHERE ball.id = :ball_id", </span><del>- lambda:{'person_id': None, 'ball_id': b4.id} </del><ins>+ lambda ctx:{'person_id': None, 'ball_id': b4.id} </ins><span class="cx"> ), </span><span class="cx"> ( </span><span class="cx"> "DELETE FROM person WHERE person.id = :id", </span><del>- lambda:[{'id':p.id}] </del><ins>+ lambda ctx:[{'id':p.id}] </ins><span class="cx"> ), </span><span class="cx"> ( </span><span class="cx"> "DELETE FROM ball WHERE ball.id = :id", </span><span class="cx"> None </span><span class="cx"> # the order of deletion is not predictable, but its roughly: </span><del>- # lambda:[{'id': b.id}, {'id': b2.id}, {'id': b3.id}, {'id': b4.id}] </del><ins>+ # lambda ctx:[{'id': b.id}, {'id': b2.id}, {'id': b3.id}, {'id': b4.id}] </ins><span class="cx"> ) </span><span class="cx"> ]) </span><span class="cx"> </span></span></pre></div> <a id="sqlalchemybranchesschematestobjectstorepy"></a> <div class="modfile"><h4>Modified: sqlalchemy/branches/schema/test/objectstore.py (1291 => 1292)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/schema/test/objectstore.py 2006-04-19 01:32:30 UTC (rev 1291) +++ sqlalchemy/branches/schema/test/objectstore.py 2006-04-19 02:00:16 UTC (rev 1292) </span><span class="lines">@@ -666,26 +666,26 @@ </span><span class="cx"> ), </span><span class="cx"> { </span><span class="cx"> "UPDATE email_addresses SET email_address=:email_address WHERE email_addresses.address_id = :email_addresses_address_id": </span><del>- lambda: {'email_address': 'im...@fo...r', 'email_addresses_address_id': objects[2].address_id} </del><ins>+ lambda ctx: {'email_address': 'im...@fo...r', 'email_addresses_address_id': objects[2].address_id} </ins><span class="cx"> , </span><span class="cx"> </span><span class="cx"> "UPDATE email_addresses SET user_id=:user_id WHERE email_addresses.address_id = :email_addresses_address_id": </span><del>- lambda: {'user_id': objects[3].user.user_id, 'email_addresses_address_id': objects[3].address_id} </del><ins>+ lambda ctx: {'user_id': objects[3].user.user_id, 'email_addresses_address_id': objects[3].address_id} </ins><span class="cx"> }, </span><span class="cx"> </span><span class="cx"> ], </span><span class="cx"> with_sequences=[ </span><span class="cx"> ( </span><span class="cx"> "INSERT INTO users (user_id, user_name) VALUES (:user_id, :user_name)", </span><del>- lambda:{'user_name': 'imnewlyadded', 'user_id':db.last_inserted_ids()[0]} </del><ins>+ lambda ctx:{'user_name': 'imnewlyadded', 'user_id':ctx.last_inserted_ids()[0]} </ins><span class="cx"> ), </span><span class="cx"> { </span><span class="cx"> "UPDATE email_addresses SET email_address=:email_address WHERE email_addresses.address_id = :email_addresses_address_id": </span><del>- lambda: {'email_address': 'im...@fo...r', 'email_addresses_address_id': objects[2].address_id} </del><ins>+ lambda ctx: {'email_address': 'im...@fo...r', 'email_addresses_address_id': objects[2].address_id} </ins><span class="cx"> , </span><span class="cx"> </span><span class="cx"> "UPDATE email_addresses SET user_id=:user_id WHERE email_addresses.address_id = :email_addresses_address_id": </span><del>- lambda: {'user_id': objects[3].user.user_id, 'email_addresses_address_id': objects[3].address_id} </del><ins>+ lambda ctx: {'user_id': objects[3].user.user_id, 'email_addresses_address_id': objects[3].address_id} </ins><span class="cx"> }, </span><span class="cx"> </span><span class="cx"> ]) </span><span class="lines">@@ -762,7 +762,7 @@ </span><span class="cx"> with_sequences=[ </span><span class="cx"> ( </span><span class="cx"> "INSERT INTO email_addresses (address_id, user_id, email_address) VALUES (:address_id, :user_id, :email_address)", </span><del>- lambda:{'email_address': 'hi', 'user_id': 7, 'address_id':db.last_inserted_ids()[0]} </del><ins>+ lambda ctx:{'email_address': 'hi', 'user_id': 7, 'address_id':ctx.last_inserted_ids()[0]} </ins><span class="cx"> ), </span><span class="cx"> ] </span><span class="cx"> ) </span><span class="lines">@@ -924,20 +924,20 @@ </span><span class="cx"> {'name': 'yellow'} </span><span class="cx"> }, </span><span class="cx"> ("INSERT INTO itemkeywords (item_id, keyword_id) VALUES (:item_id, :keyword_id)", </span><del>- lambda: [{'item_id': objects[5].item_id, 'keyword_id': k.keyword_id}] </del><ins>+ lambda ctx: [{'item_id': objects[5].item_id, 'keyword_id': k.keyword_id}] </ins><span class="cx"> ) </span><span class="cx"> ], </span><span class="cx"> </span><span class="cx"> with_sequences = [ </span><span class="cx"> { </span><span class="cx"> "UPDATE items SET item_name=:item_name WHERE items.item_id = :items_item_id": </span><del>- [{'item_name': 'item4updated', 'items_item_id': objects[4].item_id}] </del><ins>+ {'item_name': 'item4updated', 'items_item_id': objects[4].item_id} </ins><span class="cx"> , </span><span class="cx"> "INSERT INTO keywords (keyword_id, name) VALUES (:keyword_id, :name)": </span><del>- lambda: {'name': 'yellow', 'keyword_id':db.last_inserted_ids()[0]} </del><ins>+ lambda ctx: {'name': 'yellow', 'keyword_id':ctx.last_inserted_ids()[0]} </ins><span class="cx"> }, </span><span class="cx"> ("INSERT INTO itemkeywords (item_id, keyword_id) VALUES (:item_id, :keyword_id)", </span><del>- lambda: [{'item_id': objects[5].item_id, 'keyword_id': k.keyword_id}] </del><ins>+ lambda ctx: [{'item_id': objects[5].item_id, 'keyword_id': k.keyword_id}] </ins><span class="cx"> ) </span><span class="cx"> ] </span><span class="cx"> ) </span><span class="lines">@@ -951,7 +951,7 @@ </span><span class="cx"> ), </span><span class="cx"> ( </span><span class="cx"> "INSERT INTO itemkeywords (item_id, keyword_id) VALUES (:item_id, :keyword_id)", </span><del>- lambda: [{'item_id': objects[2].item_id, 'keyword_id': k.keyword_id}] </del><ins>+ lambda ctx: [{'item_id': objects[2].item_id, 'keyword_id': k.keyword_id}] </ins><span class="cx"> ) </span><span class="cx"> ]) </span><span class="cx"> </span><span class="lines">@@ -1138,19 +1138,19 @@ </span><span class="cx"> with_sequences = [ </span><span class="cx"> ( </span><span class="cx"> "INSERT INTO users (user_id, user_name) VALUES (:user_id, :user_name)", </span><del>- lambda: {'user_name': 'thesub', 'user_id':db.last_inserted_ids()[0]} </del><ins>+ lambda ctx: {'user_name': 'thesub', 'user_id':ctx.last_inserted_ids()[0]} </ins><span class="cx"> ), </span><span class="cx"> ( </span><span class="cx"> "INSERT INTO users (user_id, user_name) VALUES (:user_id, :user_name)", </span><del>- lambda: {'user_name': 'assdkfj', 'user_id':db.last_inserted_ids()[0]} </del><ins>+ lambda ctx: {'user_name': 'assdkfj', 'user_id':ctx.last_inserted_ids()[0]} </ins><span class="cx"> ), </span><span class="cx"> ( </span><span class="cx"> "INSERT INTO email_addresses (address_id, rel_user_id, email_address) VALUES (:address_id, :rel_user_id, :email_address)", </span><del>- lambda:{'rel_user_id': 1, 'email_address': 'ba...@fo...', 'address_id':db.last_inserted_ids()[0]} </del><ins>+ lambda ctx:{'rel_user_id': 1, 'email_address': 'ba...@fo...', 'address_id':ctx.last_inserted_ids()[0]} </ins><span class="cx"> ), </span><span class="cx"> ( </span><span class="cx"> "INSERT INTO email_addresses (address_id, rel_user_id, email_address) VALUES (:address_id, :rel_user_id, :email_address)", </span><del>- lambda:{'rel_user_id': 2, 'email_address': 'th...@as...', 'address_id':db.last_inserted_ids()[0]} </del><ins>+ lambda ctx:{'rel_user_id': 2, 'email_address': 'th...@as...', 'address_id':ctx.last_inserted_ids()[0]} </ins><span class="cx"> ) </span><span class="cx"> ] </span><span class="cx"> ) </span></span></pre></div> <a id="sqlalchemybranchesschematesttestbasepy"></a> <div class="modfile"><h4>Modified: sqlalchemy/branches/schema/test/testbase.py (1291 => 1292)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/schema/test/testbase.py 2006-04-19 01:32:30 UTC (rev 1291) +++ sqlalchemy/branches/schema/test/testbase.py 2006-04-19 02:00:16 UTC (rev 1292) </span><span class="lines">@@ -30,6 +30,7 @@ </span><span class="cx"> parser.add_option("--db", action="store", dest="db", default="sqlite", help="prefab database uri (sqlite, sqlite_file, postgres, mysql, oracle, oracle8, mssql)") </span><span class="cx"> parser.add_option("--mockpool", action="store_true", dest="mockpool", help="use mock pool") </span><span class="cx"> parser.add_option("--nothreadlocal", action="store_true", dest="nothreadlocal", help="dont use thread-local mod") </span><ins>+ parser.add_option("--enginestrategy", action="store", default=None, dest="enginestrategy", help="engine strategy (plain or threadlocal, defaults to SA default)") </ins><span class="cx"> </span><span class="cx"> (options, args) = parser.parse_args() </span><span class="cx"> sys.argv[1:] = args </span><span class="lines">@@ -62,8 +63,10 @@ </span><span class="cx"> raise "Could not create engine. specify --db <sqlite|sqlite_file|postgres|mysql|oracle|oracle8|mssql> to test runner." </span><span class="cx"> </span><span class="cx"> if not options.nothreadlocal: </span><del>- sqlalchemy.install_mods('threadlocal') - </del><ins>+ __import__('sqlalchemy.mods.threadlocal') + + if options.enginestrategy is not None: + opts['strategy'] = options.enginestrategy </ins><span class="cx"> if options.mockpool: </span><span class="cx"> db = engine.create_engine(db_uri, echo=echo, default_ordering=True, poolclass=MockPool, **opts) </span><span class="cx"> else: </span><span class="lines">@@ -201,7 +204,7 @@ </span><span class="cx"> </span><span class="cx"> (query, params) = item </span><span class="cx"> if callable(params): </span><del>- params = params() </del><ins>+ params = params(ctx) </ins><span class="cx"> </span><span class="cx"> query = self.convert_statement(query) </span><span class="cx"> </span></span></pre> </div> </div> </body> </html> |
From: <co...@sq...> - 2006-04-19 01:32:46
|
<!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>[1291] sqlalchemy/branches/schema/lib/sqlalchemy/databases/postgres.py: import</title> </head> <body> <div id="msg"> <dl> <dt>Revision</dt> <dd>1291</dd> <dt>Author</dt> <dd>zzzeek</dd> <dt>Date</dt> <dd>2006-04-18 20:32:30 -0500 (Tue, 18 Apr 2006)</dd> </dl> <h3>Log Message</h3> <pre>import</pre> <h3>Modified Paths</h3> <ul> <li><a href="#sqlalchemybranchesschemalibsqlalchemydatabasespostgrespy">sqlalchemy/branches/schema/lib/sqlalchemy/databases/postgres.py</a></li> </ul> </div> <div id="patch"> <h3>Diff</h3> <a id="sqlalchemybranchesschemalibsqlalchemydatabasespostgrespy"></a> <div class="modfile"><h4>Modified: sqlalchemy/branches/schema/lib/sqlalchemy/databases/postgres.py (1290 => 1291)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/schema/lib/sqlalchemy/databases/postgres.py 2006-04-19 01:28:56 UTC (rev 1290) +++ sqlalchemy/branches/schema/lib/sqlalchemy/databases/postgres.py 2006-04-19 01:32:30 UTC (rev 1291) </span><span class="lines">@@ -4,7 +4,7 @@ </span><span class="cx"> # This module is part of SQLAlchemy and is released under </span><span class="cx"> # the MIT License: http://www.opensource.org/licenses/mit-license.php </span><span class="cx"> </span><del>-import sys, StringIO, string, types, re </del><ins>+import datetime, sys, StringIO, string, types, re </ins><span class="cx"> </span><span class="cx"> import sqlalchemy.util as util </span><span class="cx"> import sqlalchemy.sql as sql </span></span></pre> </div> </div> </body> </html> |
From: <co...@sq...> - 2006-04-19 01:29: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>[1290] sqlalchemy/branches/schema/lib/sqlalchemy/databases/postgres.py: updates to pg</title> </head> <body> <div id="msg"> <dl> <dt>Revision</dt> <dd>1290</dd> <dt>Author</dt> <dd>zzzeek</dd> <dt>Date</dt> <dd>2006-04-18 20:28:56 -0500 (Tue, 18 Apr 2006)</dd> </dl> <h3>Log Message</h3> <pre>updates to pg</pre> <h3>Modified Paths</h3> <ul> <li><a href="#sqlalchemybranchesschemalibsqlalchemydatabasespostgrespy">sqlalchemy/branches/schema/lib/sqlalchemy/databases/postgres.py</a></li> </ul> </div> <div id="patch"> <h3>Diff</h3> <a id="sqlalchemybranchesschemalibsqlalchemydatabasespostgrespy"></a> <div class="modfile"><h4>Modified: sqlalchemy/branches/schema/lib/sqlalchemy/databases/postgres.py (1289 => 1290)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/schema/lib/sqlalchemy/databases/postgres.py 2006-04-18 20:00:40 UTC (rev 1289) +++ sqlalchemy/branches/schema/lib/sqlalchemy/databases/postgres.py 2006-04-19 01:28:56 UTC (rev 1290) </span><span class="lines">@@ -17,6 +17,11 @@ </span><span class="cx"> import information_schema as ischema </span><span class="cx"> </span><span class="cx"> try: </span><ins>+ import mx.DateTime.DateTime as mxDateTime +except: + mxDateTime = None + +try: </ins><span class="cx"> import psycopg2 as psycopg </span><span class="cx"> #import psycopg2.psycopg1 as psycopg </span><span class="cx"> except: </span><span class="lines">@@ -43,15 +48,26 @@ </span><span class="cx"> return "TIMESTAMP" </span><span class="cx"> class PG1DateTime(sqltypes.DateTime): </span><span class="cx"> def convert_bind_param(self, value, dialect): </span><del>- # TODO: perform appropriate postgres1 conversion between Python DateTime/MXDateTime - # this one doesnt seem to work with the "emulation" mode </del><span class="cx"> if value is not None: </span><ins>+ if isinstance(value, datetime.datetime): + seconds = float(str(value.second) + "." + + str(value.microsecond)) + mx_datetime = mxDateTime(value.year, value.month, value.day, + value.hour, value.minute, + seconds) + return psycopg.TimestampFromMx(mx_datetime) </ins><span class="cx"> return psycopg.TimestampFromMx(value) </span><span class="cx"> else: </span><span class="cx"> return None </span><span class="cx"> def convert_result_value(self, value, dialect): </span><del>- # TODO: perform appropriate postgres1 conversion between Python DateTime/MXDateTime - return value </del><ins>+ if value is None: + return None + second_parts = str(value.second).split(".") + seconds = int(second_parts[0]) + microseconds = int(second_parts[1]) + return datetime.datetime(value.year, value.month, value.day, + value.hour, value.minute, seconds, + microseconds) </ins><span class="cx"> def get_col_spec(self): </span><span class="cx"> return "TIMESTAMP" </span><span class="cx"> class PG2Date(sqltypes.Date): </span></span></pre> </div> </div> </body> </html> |
From: <co...@sq...> - 2006-04-18 20:00:58
|
<!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>[1289] sqlalchemy/branches/schema/test: mapper test working with PG</title> </head> <body> <div id="msg"> <dl> <dt>Revision</dt> <dd>1289</dd> <dt>Author</dt> <dd>zzzeek</dd> <dt>Date</dt> <dd>2006-04-18 15:00:40 -0500 (Tue, 18 Apr 2006)</dd> </dl> <h3>Log Message</h3> <pre>mapper test working with PG</pre> <h3>Modified Paths</h3> <ul> <li><a href="#sqlalchemybranchesschemalibsqlalchemyansisqlpy">sqlalchemy/branches/schema/lib/sqlalchemy/ansisql.py</a></li> <li><a href="#sqlalchemybranchesschemalibsqlalchemydatabasessqlitepy">sqlalchemy/branches/schema/lib/sqlalchemy/databases/sqlite.py</a></li> <li><a href="#sqlalchemybranchesschemalibsqlalchemysqlpy">sqlalchemy/branches/schema/lib/sqlalchemy/sql.py</a></li> <li><a href="#sqlalchemybranchesschematestmapperpy">sqlalchemy/branches/schema/test/mapper.py</a></li> </ul> </div> <div id="patch"> <h3>Diff</h3> <a id="sqlalchemybranchesschemalibsqlalchemyansisqlpy"></a> <div class="modfile"><h4>Modified: sqlalchemy/branches/schema/lib/sqlalchemy/ansisql.py (1288 => 1289)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/schema/lib/sqlalchemy/ansisql.py 2006-04-18 19:39:03 UTC (rev 1288) +++ sqlalchemy/branches/schema/lib/sqlalchemy/ansisql.py 2006-04-18 20:00:40 UTC (rev 1289) </span><span class="lines">@@ -45,7 +45,7 @@ </span><span class="cx"> </span><span class="cx"> class ANSICompiler(sql.Compiled): </span><span class="cx"> """default implementation of Compiled, which compiles ClauseElements into ANSI-compliant SQL strings.""" </span><del>- def __init__(self, dialect, statement, parameters=None, typemap=None, positional=None, paramstyle=None, **kwargs): </del><ins>+ def __init__(self, dialect, statement, parameters=None, **kwargs): </ins><span class="cx"> """constructs a new ANSICompiler object. </span><span class="cx"> </span><span class="cx"> dialect - Dialect to be used </span><span class="lines">@@ -63,7 +63,7 @@ </span><span class="cx"> self.wheres = {} </span><span class="cx"> self.strings = {} </span><span class="cx"> self.select_stack = [] </span><del>- self.typemap = typemap or {} </del><ins>+ self.typemap = {} </ins><span class="cx"> self.isinsert = False </span><span class="cx"> self.isupdate = False </span><span class="cx"> self.bindtemplate = ":%s" </span></span></pre></div> <a id="sqlalchemybranchesschemalibsqlalchemydatabasessqlitepy"></a> <div class="modfile"><h4>Modified: sqlalchemy/branches/schema/lib/sqlalchemy/databases/sqlite.py (1288 => 1289)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/schema/lib/sqlalchemy/databases/sqlite.py 2006-04-18 19:39:03 UTC (rev 1288) +++ sqlalchemy/branches/schema/lib/sqlalchemy/databases/sqlite.py 2006-04-18 20:00:40 UTC (rev 1289) </span><span class="lines">@@ -210,9 +210,6 @@ </span><span class="cx"> table.columns[col]._set_primary_key() </span><span class="cx"> </span><span class="cx"> class SQLiteCompiler(ansisql.ANSICompiler): </span><del>- def __init__(self, *args, **params): - params.setdefault('paramstyle', 'named') - ansisql.ANSICompiler.__init__(self, *args, **params) </del><span class="cx"> def limit_clause(self, select): </span><span class="cx"> text = "" </span><span class="cx"> if select.limit is not None: </span></span></pre></div> <a id="sqlalchemybranchesschemalibsqlalchemysqlpy"></a> <div class="modfile"><h4>Modified: sqlalchemy/branches/schema/lib/sqlalchemy/sql.py (1288 => 1289)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/schema/lib/sqlalchemy/sql.py 2006-04-18 19:39:03 UTC (rev 1288) +++ sqlalchemy/branches/schema/lib/sqlalchemy/sql.py 2006-04-18 20:00:40 UTC (rev 1289) </span><span class="lines">@@ -456,13 +456,24 @@ </span><span class="cx"> return multiparams[0] </span><span class="cx"> else: </span><span class="cx"> return params </span><del>- def compile(self, engine=None, parameters=None, typemap=None, compiler=None, dialect=None): - """compiles this SQL expression using its underlying Engine to produce - a Compiler object. Alternatively, a Compiler can be provided, or provided via a Dialect. - If no Compiler/Dialect/Engine arguments are given and this object is not bound to an Engine, - an ANSICompiler is used with an ANSIDialect. </del><ins>+ def compile(self, engine=None, parameters=None, compiler=None, dialect=None): + """compiles this SQL expression. + + Uses the given Compiler, or the given Dialect or Engine to create a Compiler. If no compiler + arguments are given, tries to use the underlying Engine this ClauseElement is bound + to to create a Compiler, if any. Finally, if there is no bound Engine, uses an ANSIDialect + to create a default Compiler. + </ins><span class="cx"> bindparams is a dictionary representing the default bind parameters to be used with </span><del>- the statement. """ </del><ins>+ the statement. if the bindparams is a list, it is assumed to be a list of dictionaries + and the first dictionary in the list is used with which to compile against. + The bind parameters can in some cases determine the output of the compilation, such as for UPDATE + and INSERT statements the bind parameters that are present determine the SET and VALUES clause of + those statements. + """ + + if (isinstance(parameters, list) or isinstance(parameters, tuple)): + parameters = parameters[0] </ins><span class="cx"> </span><span class="cx"> if compiler is None: </span><span class="cx"> if dialect is not None: </span></span></pre></div> <a id="sqlalchemybranchesschematestmapperpy"></a> <div class="modfile"><h4>Modified: sqlalchemy/branches/schema/test/mapper.py (1288 => 1289)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/schema/test/mapper.py 2006-04-18 19:39:03 UTC (rev 1288) +++ sqlalchemy/branches/schema/test/mapper.py 2006-04-18 20:00:40 UTC (rev 1289) </span><span class="lines">@@ -391,8 +391,9 @@ </span><span class="cx"> o2 = l[2] </span><span class="cx"> print o2.description </span><span class="cx"> </span><ins>+ orderby = str(orders.default_order_by()[0].compile(engine=db)) </ins><span class="cx"> self.assert_sql(db, go, [ </span><del>- ("SELECT orders.order_id AS orders_order_id, orders.user_id AS orders_user_id, orders.isopen AS orders_isopen FROM orders ORDER BY orders.%s" % orders.default_order_by()[0].key, {}), </del><ins>+ ("SELECT orders.order_id AS orders_order_id, orders.user_id AS orders_user_id, orders.isopen AS orders_isopen FROM orders ORDER BY %s" % orderby, {}), </ins><span class="cx"> ("SELECT orders.description AS orders_description FROM orders WHERE orders.order_id = :orders_order_id", {'orders_order_id':3}) </span><span class="cx"> ]) </span><span class="cx"> </span><span class="lines">@@ -419,8 +420,10 @@ </span><span class="cx"> l = m.select() </span><span class="cx"> o2 = l[2] </span><span class="cx"> print o2.opened, o2.description, o2.userident </span><ins>+ + orderby = str(orders.default_order_by()[0].compile(db)) </ins><span class="cx"> self.assert_sql(db, go, [ </span><del>- ("SELECT orders.order_id AS orders_order_id FROM orders ORDER BY orders.%s" % orders.default_order_by()[0].key, {}), </del><ins>+ ("SELECT orders.order_id AS orders_order_id FROM orders ORDER BY %s" % orderby, {}), </ins><span class="cx"> ("SELECT orders.user_id AS orders_user_id, orders.description AS orders_description, orders.isopen AS orders_isopen FROM orders WHERE orders.order_id = :orders_order_id", {'orders_order_id':3}) </span><span class="cx"> ]) </span><span class="cx"> </span><span class="lines">@@ -431,8 +434,10 @@ </span><span class="cx"> def go(): </span><span class="cx"> l = m2.select() </span><span class="cx"> print l[2].user_id </span><ins>+ + orderby = str(orders.default_order_by()[0].compile(db)) </ins><span class="cx"> self.assert_sql(db, go, [ </span><del>- ("SELECT orders.order_id AS orders_order_id, orders.description AS orders_description, orders.isopen AS orders_isopen FROM orders ORDER BY orders.%s" % orders.default_order_by()[0].key, {}), </del><ins>+ ("SELECT orders.order_id AS orders_order_id, orders.description AS orders_description, orders.isopen AS orders_isopen FROM orders ORDER BY %s" % orderby, {}), </ins><span class="cx"> ("SELECT orders.user_id AS orders_user_id FROM orders WHERE orders.order_id = :orders_order_id", {'orders_order_id':3}) </span><span class="cx"> ]) </span><span class="cx"> objectstore.clear() </span><span class="lines">@@ -441,7 +446,7 @@ </span><span class="cx"> l = m3.select() </span><span class="cx"> print l[3].user_id </span><span class="cx"> self.assert_sql(db, go, [ </span><del>- ("SELECT orders.order_id AS orders_order_id, orders.user_id AS orders_user_id, orders.description AS orders_description, orders.isopen AS orders_isopen FROM orders ORDER BY orders.%s" % orders.default_order_by()[0].key, {}), </del><ins>+ ("SELECT orders.order_id AS orders_order_id, orders.user_id AS orders_user_id, orders.description AS orders_description, orders.isopen AS orders_isopen FROM orders ORDER BY %s" % orderby, {}), </ins><span class="cx"> ]) </span><span class="cx"> </span><span class="cx"> def testdeepoptions(self): </span></span></pre> </div> </div> </body> </html> |
From: <co...@sq...> - 2006-04-18 19:39:18
|
<!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>[1288] sqlalchemy/branches/schema/lib/sqlalchemy: pg oids sorta working, auto-rollback when exceptions raise so the show can go on...</title> </head> <body> <div id="msg"> <dl> <dt>Revision</dt> <dd>1288</dd> <dt>Author</dt> <dd>zzzeek</dd> <dt>Date</dt> <dd>2006-04-18 14:39:03 -0500 (Tue, 18 Apr 2006)</dd> </dl> <h3>Log Message</h3> <pre>pg oids sorta working, auto-rollback when exceptions raise so the show can go on...</pre> <h3>Modified Paths</h3> <ul> <li><a href="#sqlalchemybranchesschemalibsqlalchemyansisqlpy">sqlalchemy/branches/schema/lib/sqlalchemy/ansisql.py</a></li> <li><a href="#sqlalchemybranchesschemalibsqlalchemydatabasesinformation_schemapy">sqlalchemy/branches/schema/lib/sqlalchemy/databases/information_schema.py</a></li> <li><a href="#sqlalchemybranchesschemalibsqlalchemydatabasespostgrespy">sqlalchemy/branches/schema/lib/sqlalchemy/databases/postgres.py</a></li> <li><a href="#sqlalchemybranchesschemalibsqlalchemyenginebasepy">sqlalchemy/branches/schema/lib/sqlalchemy/engine/base.py</a></li> <li><a href="#sqlalchemybranchesschemalibsqlalchemysqlpy">sqlalchemy/branches/schema/lib/sqlalchemy/sql.py</a></li> </ul> </div> <div id="patch"> <h3>Diff</h3> <a id="sqlalchemybranchesschemalibsqlalchemyansisqlpy"></a> <div class="modfile"><h4>Modified: sqlalchemy/branches/schema/lib/sqlalchemy/ansisql.py (1287 => 1288)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/schema/lib/sqlalchemy/ansisql.py 2006-04-18 19:06:04 UTC (rev 1287) +++ sqlalchemy/branches/schema/lib/sqlalchemy/ansisql.py 2006-04-18 19:39:03 UTC (rev 1288) </span><span class="lines">@@ -173,7 +173,7 @@ </span><span class="cx"> if column.table is None or column.table.name is None: </span><span class="cx"> self.strings[column] = column.name </span><span class="cx"> else: </span><del>- if False and column.table.oid_column is column: </del><ins>+ if column.table.oid_column is column: </ins><span class="cx"> n = self.dialect.oid_column_name() </span><span class="cx"> if n is not None: </span><span class="cx"> self.strings[column] = "%s.%s" % (column.table.name, n) </span></span></pre></div> <a id="sqlalchemybranchesschemalibsqlalchemydatabasesinformation_schemapy"></a> <div class="modfile"><h4>Modified: sqlalchemy/branches/schema/lib/sqlalchemy/databases/information_schema.py (1287 => 1288)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/schema/lib/sqlalchemy/databases/information_schema.py 2006-04-18 19:06:04 UTC (rev 1287) +++ sqlalchemy/branches/schema/lib/sqlalchemy/databases/information_schema.py 2006-04-18 19:39:03 UTC (rev 1288) </span><span class="lines">@@ -7,22 +7,22 @@ </span><span class="cx"> from sqlalchemy import * </span><span class="cx"> from sqlalchemy.ansisql import * </span><span class="cx"> </span><del>-generic_engine = ansisql.create_engine() </del><ins>+ischema = MetaData() </ins><span class="cx"> </span><del>-gen_schemata = schema.Table("schemata", generic_engine, </del><ins>+schemata = schema.Table("schemata", ischema, </ins><span class="cx"> Column("catalog_name", String), </span><span class="cx"> Column("schema_name", String), </span><span class="cx"> Column("schema_owner", String), </span><del>- dbschema="information_schema") </del><ins>+ schema="information_schema") </ins><span class="cx"> </span><del>-gen_tables = schema.Table("tables", generic_engine, </del><ins>+tables = schema.Table("tables", ischema, </ins><span class="cx"> Column("table_catalog", String), </span><span class="cx"> Column("table_schema", String), </span><span class="cx"> Column("table_name", String), </span><span class="cx"> Column("table_type", String), </span><del>- dbschema="information_schema") </del><ins>+ schema="information_schema") </ins><span class="cx"> </span><del>-gen_columns = schema.Table("columns", generic_engine, </del><ins>+columns = schema.Table("columns", ischema, </ins><span class="cx"> Column("table_schema", String), </span><span class="cx"> Column("table_name", String), </span><span class="cx"> Column("column_name", String), </span><span class="lines">@@ -33,30 +33,42 @@ </span><span class="cx"> Column("numeric_precision", Integer), </span><span class="cx"> Column("numeric_scale", Integer), </span><span class="cx"> Column("column_default", Integer), </span><del>- dbschema="information_schema") </del><ins>+ schema="information_schema") </ins><span class="cx"> </span><del>-gen_constraints = schema.Table("table_constraints", generic_engine, </del><ins>+constraints = schema.Table("table_constraints", ischema, </ins><span class="cx"> Column("table_schema", String), </span><span class="cx"> Column("table_name", String), </span><span class="cx"> Column("constraint_name", String), </span><span class="cx"> Column("constraint_type", String), </span><del>- dbschema="information_schema") </del><ins>+ schema="information_schema") </ins><span class="cx"> </span><del>-gen_column_constraints = schema.Table("constraint_column_usage", generic_engine, </del><ins>+column_constraints = schema.Table("constraint_column_usage", ischema, </ins><span class="cx"> Column("table_schema", String), </span><span class="cx"> Column("table_name", String), </span><span class="cx"> Column("column_name", String), </span><span class="cx"> Column("constraint_name", String), </span><del>- dbschema="information_schema") </del><ins>+ schema="information_schema") </ins><span class="cx"> </span><del>-gen_key_constraints = schema.Table("key_column_usage", generic_engine, </del><ins>+pg_key_constraints = schema.Table("key_column_usage", ischema, </ins><span class="cx"> Column("table_schema", String), </span><span class="cx"> Column("table_name", String), </span><span class="cx"> Column("column_name", String), </span><span class="cx"> Column("constraint_name", String), </span><del>- dbschema="information_schema") </del><ins>+ schema="information_schema") </ins><span class="cx"> </span><del>-gen_ref_constraints = schema.Table("referential_constraints", generic_engine, </del><ins>+mysql_key_constraints = schema.Table("key_column_usage", ischema, + Column("table_schema", String), + Column("table_name", String), + Column("column_name", String), + Column("constraint_name", String), + Column("referenced_table_schema", String), + Column("referenced_table_name", String), + Column("referenced_column_name", String), + schema="information_schema") + +key_constraints = pg_key_constraints + +ref_constraints = schema.Table("referential_constraints", ischema, </ins><span class="cx"> Column("constraint_catalog", String), </span><span class="cx"> Column("constraint_schema", String), </span><span class="cx"> Column("constraint_name", String), </span><span class="lines">@@ -66,7 +78,7 @@ </span><span class="cx"> Column("match_option", String), </span><span class="cx"> Column("update_rule", String), </span><span class="cx"> Column("delete_rule", String), </span><del>- dbschema="information_schema") </del><ins>+ schema="information_schema") </ins><span class="cx"> </span><span class="cx"> class ISchema(object): </span><span class="cx"> def __init__(self, engine): </span><span class="lines">@@ -88,37 +100,25 @@ </span><span class="cx"> return self.cache[name] </span><span class="cx"> </span><span class="cx"> </span><del>-def reflecttable(engine, table, ischema_names, use_mysql=False): - columns = gen_columns.toengine(engine) - constraints = gen_constraints.toengine(engine) </del><ins>+def reflecttable(connection, table, ischema_names, use_mysql=False): </ins><span class="cx"> </span><span class="cx"> if use_mysql: </span><span class="cx"> # no idea which INFORMATION_SCHEMA spec is correct, mysql or postgres </span><del>- key_constraints = schema.Table("key_column_usage", engine, - Column("table_schema", String), - Column("table_name", String), - Column("column_name", String), - Column("constraint_name", String), - Column("referenced_table_schema", String), - Column("referenced_table_name", String), - Column("referenced_column_name", String), - schema="information_schema", useexisting=True) </del><ins>+ key_constraints = mysql_key_constraints </ins><span class="cx"> else: </span><del>- column_constraints = gen_column_constraints.toengine(engine) - key_constraints = gen_key_constraints.toengine(engine) - - </del><ins>+ key_constraints = pg_key_constraints + </ins><span class="cx"> if table.schema is not None: </span><span class="cx"> current_schema = table.schema </span><span class="cx"> else: </span><del>- current_schema = engine.get_default_schema_name() </del><ins>+ current_schema = connection.default_schema_name() </ins><span class="cx"> </span><span class="cx"> s = select([columns], </span><span class="cx"> sql.and_(columns.c.table_name==table.name, </span><span class="cx"> columns.c.table_schema==current_schema), </span><span class="cx"> order_by=[columns.c.ordinal_position]) </span><span class="cx"> </span><del>- c = s.execute() </del><ins>+ c = connection.execute(s) </ins><span class="cx"> while True: </span><span class="cx"> row = c.fetchone() </span><span class="cx"> if row is None: </span><span class="lines">@@ -160,7 +160,7 @@ </span><span class="cx"> s.append_whereclause(constraints.c.table_name==table.name) </span><span class="cx"> s.append_whereclause(constraints.c.table_schema==current_schema) </span><span class="cx"> colmap = [constraints.c.constraint_type, key_constraints.c.column_name, key_constraints.c.referenced_table_schema, key_constraints.c.referenced_table_name, key_constraints.c.referenced_column_name] </span><del>- c = s.execute() </del><ins>+ c = connection.execute(s) </ins><span class="cx"> </span><span class="cx"> while True: </span><span class="cx"> row = c.fetchone() </span><span class="lines">@@ -178,6 +178,8 @@ </span><span class="cx"> if type=='PRIMARY KEY': </span><span class="cx"> table.c[constrained_column]._set_primary_key() </span><span class="cx"> elif type=='FOREIGN KEY': </span><del>- remotetable = Table(referred_table, engine, autoload = True, schema=referred_schema) </del><ins>+ if current_schema == referred_schema: + referred_schema = table.schema + remotetable = Table(referred_table, table.metadata, autoload=True, autoload_with=connection, schema=referred_schema) </ins><span class="cx"> table.c[constrained_column].append_item(schema.ForeignKey(remotetable.c[referred_column])) </span><span class="cx"> </span></span></pre></div> <a id="sqlalchemybranchesschemalibsqlalchemydatabasespostgrespy"></a> <div class="modfile"><h4>Modified: sqlalchemy/branches/schema/lib/sqlalchemy/databases/postgres.py (1287 => 1288)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/schema/lib/sqlalchemy/databases/postgres.py 2006-04-18 19:06:04 UTC (rev 1287) +++ sqlalchemy/branches/schema/lib/sqlalchemy/databases/postgres.py 2006-04-18 19:39:03 UTC (rev 1288) </span><span class="lines">@@ -169,7 +169,7 @@ </span><span class="cx"> return </span><span class="cx"> </span><span class="cx"> def post_exec(self, engine, proxy, compiled, parameters, **kwargs): </span><del>- if getattr(compiled, "isinsert", False) and self.context.last_inserted_ids is None: </del><ins>+ if getattr(compiled, "isinsert", False) and self._last_inserted_ids is None: </ins><span class="cx"> if not engine.dialect.use_oids: </span><span class="cx"> pass </span><span class="cx"> # will raise invalid error when they go to get them </span><span class="lines">@@ -178,7 +178,7 @@ </span><span class="cx"> cursor = proxy() </span><span class="cx"> if cursor.lastrowid is not None and table is not None and len(table.primary_key): </span><span class="cx"> s = sql.select(table.primary_key, table.oid_column == cursor.lastrowid) </span><del>- c = s.compile() </del><ins>+ c = s.compile(engine=engine) </ins><span class="cx"> cursor = proxy(str(c), c.get_params()) </span><span class="cx"> row = cursor.fetchone() </span><span class="cx"> self._last_inserted_ids = [v for v in row] </span><span class="lines">@@ -226,9 +226,9 @@ </span><span class="cx"> def defaultrunner(self, engine, proxy): </span><span class="cx"> return PGDefaultRunner(engine, proxy) </span><span class="cx"> </span><del>- def get_default_schema_name(self): </del><ins>+ def get_default_schema_name(self, connection): </ins><span class="cx"> if not hasattr(self, '_default_schema_name'): </span><del>- self._default_schema_name = text("select current_schema()", self).scalar() </del><ins>+ self._default_schema_name = connection.scalar("select current_schema()", None) </ins><span class="cx"> return self._default_schema_name </span><span class="cx"> </span><span class="cx"> def last_inserted_ids(self): </span><span class="lines">@@ -257,15 +257,13 @@ </span><span class="cx"> def dbapi(self): </span><span class="cx"> return self.module </span><span class="cx"> </span><del>- def reflecttable(self, table): </del><ins>+ def reflecttable(self, connection, table): </ins><span class="cx"> if self.version == 2: </span><span class="cx"> ischema_names = pg2_ischema_names </span><span class="cx"> else: </span><span class="cx"> ischema_names = pg1_ischema_names </span><span class="cx"> </span><del>- # give ischema the given table's engine with which to look up - # other tables, not 'self', since it could be a ProxyEngine - ischema.reflecttable(table.engine, table, ischema_names) </del><ins>+ ischema.reflecttable(connection, table, ischema_names) </ins><span class="cx"> </span><span class="cx"> class PGCompiler(ansisql.ANSICompiler): </span><span class="cx"> </span></span></pre></div> <a id="sqlalchemybranchesschemalibsqlalchemyenginebasepy"></a> <div class="modfile"><h4>Modified: sqlalchemy/branches/schema/lib/sqlalchemy/engine/base.py (1287 => 1288)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/schema/lib/sqlalchemy/engine/base.py 2006-04-18 19:06:04 UTC (rev 1287) +++ sqlalchemy/branches/schema/lib/sqlalchemy/engine/base.py 2006-04-18 19:39:03 UTC (rev 1288) </span><span class="lines">@@ -79,8 +79,8 @@ </span><span class="cx"> """subclasses override this method to provide the DBAPI module used to establish </span><span class="cx"> connections.""" </span><span class="cx"> raise NotImplementedError() </span><del>- def get_default_schema_name(self, proxy): - """returns the currently selected schema given an execution proxy""" </del><ins>+ def get_default_schema_name(self, connection): + """returns the currently selected schema given an connection""" </ins><span class="cx"> raise NotImplementedError() </span><span class="cx"> def execution_context(self): </span><span class="cx"> """returns a new ExecutionContext object.""" </span><span class="lines">@@ -229,7 +229,9 @@ </span><span class="cx"> def reflecttable(self, table, **kwargs): </span><span class="cx"> """reflects the columns in the given table from the database.""" </span><span class="cx"> return self.engine.reflecttable(table, connection=self, **kwargs) </span><del>- </del><ins>+ def default_schema_name(self): + return self.engine.dialect.get_default_schema_name(self) + </ins><span class="cx"> def _execute_raw(self, statement, parameters=None, cursor=None, echo=None, context=None, **kwargs): </span><span class="cx"> if cursor is None: </span><span class="cx"> cursor = self.connection.cursor() </span><span class="lines">@@ -255,11 +257,13 @@ </span><span class="cx"> try: </span><span class="cx"> self.engine.dialect.do_execute(c, statement, parameters, context=context) </span><span class="cx"> except Exception, e: </span><ins>+ self.engine.dialect.do_rollback(self.connection) </ins><span class="cx"> raise exceptions.SQLError(statement, parameters, e) </span><span class="cx"> def _executemany(self, c, statement, parameters, context=None): </span><span class="cx"> try: </span><span class="cx"> self.engine.dialect.do_executemany(c, statement, parameters, context=context) </span><span class="cx"> except Exception, e: </span><ins>+ self.engine.dialect.do_rollback(self.connection) </ins><span class="cx"> raise exceptions.SQLError(statement, parameters, e) </span><span class="cx"> def proxy(self, statement=None, parameters=None): </span><span class="cx"> """executes the given statement string and parameter object. </span><span class="lines">@@ -340,7 +344,6 @@ </span><span class="cx"> """returns a sql.text() object for performing literal queries.""" </span><span class="cx"> return sql.text(text, engine=self, *args, **kwargs) </span><span class="cx"> </span><del>- </del><span class="cx"> def _run_visitor(self, visitorcallable, element, connection=None, **kwargs): </span><span class="cx"> if connection is None: </span><span class="cx"> conn = self.connect() </span></span></pre></div> <a id="sqlalchemybranchesschemalibsqlalchemysqlpy"></a> <div class="modfile"><h4>Modified: sqlalchemy/branches/schema/lib/sqlalchemy/sql.py (1287 => 1288)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/schema/lib/sqlalchemy/sql.py 2006-04-18 19:06:04 UTC (rev 1287) +++ sqlalchemy/branches/schema/lib/sqlalchemy/sql.py 2006-04-18 19:39:03 UTC (rev 1288) </span><span class="lines">@@ -465,7 +465,9 @@ </span><span class="cx"> the statement. """ </span><span class="cx"> </span><span class="cx"> if compiler is None: </span><del>- if engine is not None: </del><ins>+ if dialect is not None: + compiler = dialect.compiler(self, parameters) + elif engine is not None: </ins><span class="cx"> compiler = engine.compiler(self, parameters) </span><span class="cx"> elif self.engine is not None: </span><span class="cx"> compiler = self.engine.compiler(self, parameters) </span></span></pre> </div> </div> </body> </html> |
From: <co...@sq...> - 2006-04-18 19:06: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>[1287] sqlalchemy/branches/schema/test: reflection, some mapper fixes...</title> </head> <body> <div id="msg"> <dl> <dt>Revision</dt> <dd>1287</dd> <dt>Author</dt> <dd>zzzeek</dd> <dt>Date</dt> <dd>2006-04-18 14:06:04 -0500 (Tue, 18 Apr 2006)</dd> </dl> <h3>Log Message</h3> <pre>reflection, some mapper fixes...</pre> <h3>Modified Paths</h3> <ul> <li><a href="#sqlalchemybranchesschemalibsqlalchemydatabasessqlitepy">sqlalchemy/branches/schema/lib/sqlalchemy/databases/sqlite.py</a></li> <li><a href="#sqlalchemybranchesschemalibsqlalchemyenginebasepy">sqlalchemy/branches/schema/lib/sqlalchemy/engine/base.py</a></li> <li><a href="#sqlalchemybranchesschemalibsqlalchemymappingobjectstorepy">sqlalchemy/branches/schema/lib/sqlalchemy/mapping/objectstore.py</a></li> <li><a href="#sqlalchemybranchesschemalibsqlalchemymappingunitofworkpy">sqlalchemy/branches/schema/lib/sqlalchemy/mapping/unitofwork.py</a></li> <li><a href="#sqlalchemybranchesschemalibsqlalchemyschemapy">sqlalchemy/branches/schema/lib/sqlalchemy/schema.py</a></li> <li><a href="#sqlalchemybranchesschematestreflectionpy">sqlalchemy/branches/schema/test/reflection.py</a></li> <li><a href="#sqlalchemybranchesschematestrelationshipspy">sqlalchemy/branches/schema/test/relationships.py</a></li> </ul> </div> <div id="patch"> <h3>Diff</h3> <a id="sqlalchemybranchesschemalibsqlalchemydatabasessqlitepy"></a> <div class="modfile"><h4>Modified: sqlalchemy/branches/schema/lib/sqlalchemy/databases/sqlite.py (1286 => 1287)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/schema/lib/sqlalchemy/databases/sqlite.py 2006-04-18 18:35:39 UTC (rev 1286) +++ sqlalchemy/branches/schema/lib/sqlalchemy/databases/sqlite.py 2006-04-18 19:06:04 UTC (rev 1287) </span><span class="lines">@@ -154,8 +154,8 @@ </span><span class="cx"> def push_session(self): </span><span class="cx"> raise InvalidRequestError("SQLite doesn't support nested sessions") </span><span class="cx"> </span><del>- def reflecttable(self, table): - c = self.execute("PRAGMA table_info(" + table.name + ")", {}) </del><ins>+ def reflecttable(self, connection, table): + c = connection.execute("PRAGMA table_info(" + table.name + ")", {}) </ins><span class="cx"> while True: </span><span class="cx"> row = c.fetchone() </span><span class="cx"> if row is None: </span><span class="lines">@@ -174,7 +174,7 @@ </span><span class="cx"> #print "args! " +repr(args) </span><span class="cx"> coltype = coltype(*[int(a) for a in args]) </span><span class="cx"> table.append_item(schema.Column(name, coltype, primary_key = primary_key, nullable = nullable)) </span><del>- c = self.execute("PRAGMA foreign_key_list(" + table.name + ")", {}) </del><ins>+ c = connection.execute("PRAGMA foreign_key_list(" + table.name + ")", {}) </ins><span class="cx"> while True: </span><span class="cx"> row = c.fetchone() </span><span class="cx"> if row is None: </span><span class="lines">@@ -183,10 +183,10 @@ </span><span class="cx"> #print "row! " + repr(row) </span><span class="cx"> # look up the table based on the given table's engine, not 'self', </span><span class="cx"> # since it could be a ProxyEngine </span><del>- remotetable = Table(tablename, table.engine, autoload = True) </del><ins>+ remotetable = schema.Table(tablename, table.metadata, autoload=True, autoload_with=connection) </ins><span class="cx"> table.c[localcol].append_item(schema.ForeignKey(remotetable.c[remotecol])) </span><span class="cx"> # check for UNIQUE indexes </span><del>- c = self.execute("PRAGMA index_list(" + table.name + ")", {}) </del><ins>+ c = connection.execute("PRAGMA index_list(" + table.name + ")", {}) </ins><span class="cx"> unique_indexes = [] </span><span class="cx"> while True: </span><span class="cx"> row = c.fetchone() </span><span class="lines">@@ -196,7 +196,7 @@ </span><span class="cx"> unique_indexes.append(row[1]) </span><span class="cx"> # loop thru unique indexes for one that includes the primary key </span><span class="cx"> for idx in unique_indexes: </span><del>- c = self.execute("PRAGMA index_info(" + idx + ")", {}) </del><ins>+ c = connection.execute("PRAGMA index_info(" + idx + ")", {}) </ins><span class="cx"> cols = [] </span><span class="cx"> while True: </span><span class="cx"> row = c.fetchone() </span></span></pre></div> <a id="sqlalchemybranchesschemalibsqlalchemyenginebasepy"></a> <div class="modfile"><h4>Modified: sqlalchemy/branches/schema/lib/sqlalchemy/engine/base.py (1286 => 1287)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/schema/lib/sqlalchemy/engine/base.py 2006-04-18 18:35:39 UTC (rev 1286) +++ sqlalchemy/branches/schema/lib/sqlalchemy/engine/base.py 2006-04-18 19:06:04 UTC (rev 1287) </span><span class="lines">@@ -72,8 +72,8 @@ </span><span class="cx"> </span><span class="cx"> compiler is called within the context of the compile() method.""" </span><span class="cx"> raise NotImplementedError() </span><del>- def reflecttable(self, engine, table): - """given an Engine and a Table object, reflects its columns and properties from the database.""" </del><ins>+ def reflecttable(self, connection, table): + """given an Connection and a Table object, reflects its columns and properties from the database.""" </ins><span class="cx"> raise NotImplementedError() </span><span class="cx"> def dbapi(self): </span><span class="cx"> """subclasses override this method to provide the DBAPI module used to establish </span><span class="lines">@@ -211,18 +211,24 @@ </span><span class="cx"> proxy(str(compiled), parameters) </span><span class="cx"> context.post_exec(self.engine, proxy, compiled, parameters, **kwargs) </span><span class="cx"> return ResultProxy(self.engine, self, cursor, context, typemap=compiled.typemap) </span><ins>+ + # poor man's multimethod/generic function thingy </ins><span class="cx"> executors = { </span><span class="cx"> sql.ClauseElement : execute_clauseelement, </span><span class="cx"> sql.Compiled : execute_compiled, </span><span class="cx"> schema.SchemaItem:execute_default, </span><span class="cx"> str.__mro__[-2] : execute_text </span><span class="cx"> } </span><ins>+ </ins><span class="cx"> def create(self, entity, **kwargs): </span><span class="cx"> """creates a table or index given an appropriate schema object.""" </span><span class="cx"> return self.engine.create(entity, connection=self, **kwargs) </span><span class="cx"> def drop(self, entity, **kwargs): </span><span class="cx"> """drops a table or index given an appropriate schema object.""" </span><span class="cx"> return self.engine.drop(entity, connection=self, **kwargs) </span><ins>+ def reflecttable(self, table, **kwargs): + """reflects the columns in the given table from the database.""" + return self.engine.reflecttable(table, connection=self, **kwargs) </ins><span class="cx"> </span><span class="cx"> def _execute_raw(self, statement, parameters=None, cursor=None, echo=None, context=None, **kwargs): </span><span class="cx"> if cursor is None: </span><span class="lines">@@ -360,9 +366,17 @@ </span><span class="cx"> def connect(self, **kwargs): </span><span class="cx"> return Connection(self, **kwargs) </span><span class="cx"> </span><del>- def reflecttable(self, table): </del><ins>+ def reflecttable(self, table, connection=None): </ins><span class="cx"> """given a Table object, reflects its columns and properties from the database.""" </span><del>- self.dialect.reflecttable(self, table) </del><ins>+ if connection is None: + conn = self.connect() + else: + conn = connection + try: + self.dialect.reflecttable(conn, table) + finally: + if connection is None: + conn.close() </ins><span class="cx"> </span><span class="cx"> def raw_connection(self): </span><span class="cx"> """returns a DBAPI connection.""" </span></span></pre></div> <a id="sqlalchemybranchesschemalibsqlalchemymappingobjectstorepy"></a> <div class="modfile"><h4>Modified: sqlalchemy/branches/schema/lib/sqlalchemy/mapping/objectstore.py (1286 => 1287)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/schema/lib/sqlalchemy/mapping/objectstore.py 2006-04-18 18:35:39 UTC (rev 1286) +++ sqlalchemy/branches/schema/lib/sqlalchemy/mapping/objectstore.py 2006-04-18 19:06:04 UTC (rev 1287) </span><span class="lines">@@ -185,8 +185,19 @@ </span><span class="cx"> for o in obj: </span><span class="cx"> self._bind_to(o) </span><span class="cx"> self.uow.register_clean(o) </span><del>- </del><ins>+ + def add(self, *obj): + """given some objects, if they have no identity they will be registered as new in this session. + if they have an identity, its verified that they are already part of this session.""" + for o in obj: + if hasattr(o, '_instance_key'): + if not self.uow.has_key(o._instance_key): + raise InvalidRequestError("Instance '%s' is not bound to this Session" % repr(o)) + else: + self.register_new(o) + </ins><span class="cx"> def register_new(self, *obj): </span><ins>+ """registers the given objects as "new" and binds them to this session.""" </ins><span class="cx"> for o in obj: </span><span class="cx"> self._bind_to(o) </span><span class="cx"> self.uow.register_new(o) </span></span></pre></div> <a id="sqlalchemybranchesschemalibsqlalchemymappingunitofworkpy"></a> <div class="modfile"><h4>Modified: sqlalchemy/branches/schema/lib/sqlalchemy/mapping/unitofwork.py (1286 => 1287)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/schema/lib/sqlalchemy/mapping/unitofwork.py 2006-04-18 18:35:39 UTC (rev 1286) +++ sqlalchemy/branches/schema/lib/sqlalchemy/mapping/unitofwork.py 2006-04-18 19:06:04 UTC (rev 1287) </span><span class="lines">@@ -42,7 +42,12 @@ </span><span class="cx"> attributes.ListElement.__init__(self, obj, key, data=data, **kwargs) </span><span class="cx"> self.deleteremoved = deleteremoved </span><span class="cx"> def list_value_changed(self, obj, key, item, listval, isdelete): </span><del>- sess = get_session(obj) </del><ins>+ sess = get_session(obj, raiseerror=True) + if sess is None: + return + # add the child item to this session. if its already got an identity then its + # expected to be there already. + sess.add(item) </ins><span class="cx"> if not isdelete and sess.deleted.contains(item): </span><span class="cx"> #raise InvalidRequestError("re-inserting a deleted value into a list") </span><span class="cx"> del sess.deleted[item] </span><span class="lines">@@ -61,10 +66,12 @@ </span><span class="cx"> attributes.AttributeManager.__init__(self) </span><span class="cx"> </span><span class="cx"> def value_changed(self, obj, key, value): </span><del>- if hasattr(obj, '_instance_key'): - get_session(obj).register_dirty(obj) - else: - get_session(obj).register_new(obj) </del><ins>+ sess = get_session(obj, raiseerror=False) + if sess is not None: + if hasattr(obj, '_instance_key'): + sess.register_dirty(obj) + else: + sess.register_new(obj) </ins><span class="cx"> </span><span class="cx"> def create_prop(self, class_, key, uselist, callable_, **kwargs): </span><span class="cx"> return UOWProperty(class_, self, key, uselist, callable_, **kwargs) </span><span class="lines">@@ -174,6 +181,8 @@ </span><span class="cx"> self.attributes.commit(obj) </span><span class="cx"> </span><span class="cx"> def register_new(self, obj): </span><ins>+ if hasattr(obj, '_instance_key'): + raise InvalidRequestError("Object '%s' already has an identity - it cant be registered as new" % repr(obj)) </ins><span class="cx"> if not self.new.contains(obj): </span><span class="cx"> self.new.append(obj) </span><span class="cx"> </span></span></pre></div> <a id="sqlalchemybranchesschemalibsqlalchemyschemapy"></a> <div class="modfile"><h4>Modified: sqlalchemy/branches/schema/lib/sqlalchemy/schema.py (1286 => 1287)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/schema/lib/sqlalchemy/schema.py 2006-04-18 18:35:39 UTC (rev 1286) +++ sqlalchemy/branches/schema/lib/sqlalchemy/schema.py 2006-04-18 19:06:04 UTC (rev 1287) </span><span class="lines">@@ -63,6 +63,7 @@ </span><span class="cx"> name = str(name) # in case of incoming unicode </span><span class="cx"> schema = kwargs.get('schema', None) </span><span class="cx"> autoload = kwargs.pop('autoload', False) </span><ins>+ autoload_with = kwargs.pop('autoload_with', False) </ins><span class="cx"> redefine = kwargs.pop('redefine', False) </span><span class="cx"> mustexist = kwargs.pop('mustexist', False) </span><span class="cx"> useexisting = kwargs.pop('useexisting', False) </span><span class="lines">@@ -83,7 +84,10 @@ </span><span class="cx"> # we do it after the table is in the singleton dictionary to support </span><span class="cx"> # circular foreign keys </span><span class="cx"> if autoload: </span><del>- metadata.engine.reflecttable(table) </del><ins>+ if autoload_with: + autoload_with.reflecttable(table) + else: + metadata.engine.reflecttable(table) </ins><span class="cx"> # initialize all the column, etc. objects. done after </span><span class="cx"> # reflection to allow user-overrides </span><span class="cx"> table._init_items(*args) </span></span></pre></div> <a id="sqlalchemybranchesschematestreflectionpy"></a> <div class="modfile"><h4>Modified: sqlalchemy/branches/schema/test/reflection.py (1286 => 1287)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/schema/test/reflection.py 2006-04-18 18:35:39 UTC (rev 1286) +++ sqlalchemy/branches/schema/test/reflection.py 2006-04-18 19:06:04 UTC (rev 1287) </span><span class="lines">@@ -1,8 +1,6 @@ </span><span class="cx"> </span><span class="cx"> import sqlalchemy.ansisql as ansisql </span><span class="cx"> import sqlalchemy.databases.postgres as postgres </span><del>-import sqlalchemy.databases.oracle as oracle -import sqlalchemy.databases.sqlite as sqllite </del><span class="cx"> </span><span class="cx"> from sqlalchemy import * </span><span class="cx"> </span><span class="lines">@@ -14,7 +12,7 @@ </span><span class="cx"> def testbasic(self): </span><span class="cx"> # really trip it up with a circular reference </span><span class="cx"> </span><del>- use_function_defaults = testbase.db.engine.__module__.endswith('postgres') or testbase.db.engine.__module__.endswith('oracle') </del><ins>+ use_function_defaults = testbase.db.engine.name == 'postgres' or testbase.db.engine.name == 'oracle' </ins><span class="cx"> </span><span class="cx"> use_string_defaults = use_function_defaults or testbase.db.engine.__module__.endswith('sqlite') </span><span class="cx"> </span><span class="lines">@@ -123,9 +121,10 @@ </span><span class="cx"> table.drop() </span><span class="cx"> </span><span class="cx"> def testtoengine(self): </span><del>- db = ansisql.engine() </del><ins>+ meta = MetaData('md1') + meta2 = MetaData('md2') </ins><span class="cx"> </span><del>- table = Table('mytable', db, </del><ins>+ table = Table('mytable', meta, </ins><span class="cx"> Column('myid', Integer, key = 'id'), </span><span class="cx"> Column('name', String, key = 'name', nullable=False), </span><span class="cx"> Column('description', String, key = 'description'), </span><span class="lines">@@ -133,14 +132,14 @@ </span><span class="cx"> </span><span class="cx"> print repr(table) </span><span class="cx"> </span><del>- pgdb = postgres.engine({}) </del><ins>+ table2 = table.tometadata(meta2) </ins><span class="cx"> </span><del>- pgtable = table.toengine(pgdb) </del><ins>+ print repr(table2) </ins><span class="cx"> </span><del>- print repr(pgtable) - assert pgtable.c.id.nullable - assert not pgtable.c.name.nullable - assert pgtable.c.description.nullable </del><ins>+ assert table is not table2 + assert table2.c.id.nullable + assert not table2.c.name.nullable + assert table2.c.description.nullable </ins><span class="cx"> </span><span class="cx"> def testoverride(self): </span><span class="cx"> table = Table( </span></span></pre></div> <a id="sqlalchemybranchesschematestrelationshipspy"></a> <div class="modfile"><h4>Modified: sqlalchemy/branches/schema/test/relationships.py (1286 => 1287)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/schema/test/relationships.py 2006-04-18 18:35:39 UTC (rev 1286) +++ sqlalchemy/branches/schema/test/relationships.py 2006-04-18 19:06:04 UTC (rev 1287) </span><span class="lines">@@ -70,12 +70,12 @@ </span><span class="cx"> global c </span><span class="cx"> a = A(); a.name = "a1" </span><span class="cx"> b = B(); b.name = "b1" </span><ins>+ session.add(a,b) </ins><span class="cx"> c = C(); c.name = "c1"; c.a_row = a </span><span class="cx"> # we must have more than one d row or it won't fail </span><span class="cx"> d1 = D(); d1.name = "d1"; d1.b_row = b; d1.c_row = c </span><span class="cx"> d2 = D(); d2.name = "d2"; d2.b_row = b; d2.c_row = c </span><span class="cx"> d3 = D(); d3.name = "d3"; d3.b_row = b; d3.c_row = c </span><del>- session.register_new(a,b,c,d1,d2,d3) </del><span class="cx"> </span><span class="cx"> def tearDown(self): </span><span class="cx"> conn = session.connect() </span></span></pre> </div> </div> </body> </html> |
From: <co...@sq...> - 2006-04-18 18:35:48
|
<!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>[1286] sqlalchemy/branches/schema/lib/sqlalchemy/mapping/mapper.py: merged fix 1285</title> </head> <body> <div id="msg"> <dl> <dt>Revision</dt> <dd>1286</dd> <dt>Author</dt> <dd>zzzeek</dd> <dt>Date</dt> <dd>2006-04-18 13:35:39 -0500 (Tue, 18 Apr 2006)</dd> </dl> <h3>Log Message</h3> <pre>merged fix 1285</pre> <h3>Modified Paths</h3> <ul> <li><a href="#sqlalchemybranchesschemalibsqlalchemymappingmapperpy">sqlalchemy/branches/schema/lib/sqlalchemy/mapping/mapper.py</a></li> </ul> </div> <div id="patch"> <h3>Diff</h3> <a id="sqlalchemybranchesschemalibsqlalchemymappingmapperpy"></a> <div class="modfile"><h4>Modified: sqlalchemy/branches/schema/lib/sqlalchemy/mapping/mapper.py (1285 => 1286)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/schema/lib/sqlalchemy/mapping/mapper.py 2006-04-18 18:24:07 UTC (rev 1285) +++ sqlalchemy/branches/schema/lib/sqlalchemy/mapping/mapper.py 2006-04-18 18:35:39 UTC (rev 1286) </span><span class="lines">@@ -88,6 +88,8 @@ </span><span class="cx"> self.table = table </span><span class="cx"> </span><span class="cx"> if inherits is not None: </span><ins>+ if self.class_.__mro__[1] != inherits.class_: + raise ArgumentError("Class '%s' does not inherit from '%s'" % (self.class_.__name__, inherits.class_.__name__)) </ins><span class="cx"> self.primarytable = inherits.primarytable </span><span class="cx"> # inherit_condition is optional. </span><span class="cx"> if not table is inherits.noninherited_table: </span><span class="lines">@@ -214,7 +216,7 @@ </span><span class="cx"> if not self.props.has_key(key): </span><span class="cx"> self.props[key] = prop.copy() </span><span class="cx"> self.props[key].parent = self </span><del>- self.props[key].key = None # force re-init </del><ins>+ # self.props[key].key = None # force re-init </ins><span class="cx"> l = [(key, prop) for key, prop in self.props.iteritems()] </span><span class="cx"> for key, prop in l: </span><span class="cx"> if getattr(prop, 'key', None) is None: </span></span></pre> </div> </div> </body> </html> |