sqlalchemy-commits Mailing List for SQLAlchemy (Page 370)
Brought to you by:
zzzeek
You can subscribe to this list here.
2006 |
Jan
|
Feb
(74) |
Mar
(167) |
Apr
(127) |
May
(190) |
Jun
(119) |
Jul
(77) |
Aug
(82) |
Sep
(84) |
Oct
(153) |
Nov
(45) |
Dec
(54) |
---|---|---|---|---|---|---|---|---|---|---|---|---|
2007 |
Jan
(109) |
Feb
(80) |
Mar
(110) |
Apr
(106) |
May
(92) |
Jun
(147) |
Jul
(288) |
Aug
(307) |
Sep
(108) |
Oct
(156) |
Nov
(147) |
Dec
(134) |
2008 |
Jan
(126) |
Feb
(91) |
Mar
(184) |
Apr
(208) |
May
(212) |
Jun
(54) |
Jul
(106) |
Aug
(80) |
Sep
(58) |
Oct
(80) |
Nov
(119) |
Dec
(220) |
2009 |
Jan
(202) |
Feb
(50) |
Mar
(70) |
Apr
(46) |
May
(80) |
Jun
(61) |
Jul
(146) |
Aug
(81) |
Sep
(71) |
Oct
(74) |
Nov
(66) |
Dec
(82) |
2010 |
Jan
(112) |
Feb
(169) |
Mar
(235) |
Apr
(77) |
May
(22) |
Jun
(31) |
Jul
(46) |
Aug
(46) |
Sep
(70) |
Oct
(36) |
Nov
(37) |
Dec
(79) |
2011 |
Jan
(46) |
Feb
(54) |
Mar
(65) |
Apr
(73) |
May
(31) |
Jun
(46) |
Jul
(40) |
Aug
(36) |
Sep
(44) |
Oct
(33) |
Nov
(19) |
Dec
(10) |
2012 |
Jan
(60) |
Feb
(37) |
Mar
(35) |
Apr
(28) |
May
(27) |
Jun
(50) |
Jul
(33) |
Aug
(88) |
Sep
(64) |
Oct
(74) |
Nov
(62) |
Dec
(41) |
2013 |
Jan
(30) |
Feb
(37) |
Mar
(39) |
Apr
(52) |
May
(40) |
Jun
(85) |
Jul
(74) |
Aug
(76) |
Sep
(26) |
Oct
(76) |
Nov
(63) |
Dec
(65) |
2014 |
Jan
(68) |
Feb
(82) |
Mar
(87) |
Apr
(24) |
May
(66) |
Jun
(34) |
Jul
(86) |
Aug
(75) |
Sep
(70) |
Oct
(41) |
Nov
(23) |
Dec
(53) |
2015 |
Jan
(40) |
Feb
(39) |
Mar
(69) |
Apr
(64) |
May
(40) |
Jun
(43) |
Jul
(20) |
Aug
(48) |
Sep
(38) |
Oct
(28) |
Nov
(34) |
Dec
(44) |
2016 |
Jan
(82) |
Feb
(49) |
Mar
(25) |
Apr
(21) |
May
(19) |
Jun
(46) |
Jul
(38) |
Aug
(21) |
Sep
(33) |
Oct
(44) |
Nov
(26) |
Dec
(10) |
2017 |
Jan
(52) |
Feb
(18) |
Mar
(61) |
Apr
(43) |
May
(57) |
Jun
(36) |
Jul
(37) |
Aug
|
Sep
|
Oct
|
Nov
|
Dec
|
From: <co...@sq...> - 2006-03-25 21:15: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>[1207] sqlalchemy/trunk/test: removed all "tablename + '_' + columname" code and replaced with column._label, to take </title> </head> <body> <div id="msg"> <dl> <dt>Revision</dt> <dd>1207</dd> <dt>Author</dt> <dd>zzzeek</dd> <dt>Date</dt> <dd>2006-03-25 15:14:54 -0600 (Sat, 25 Mar 2006)</dd> </dl> <h3>Log Message</h3> <pre>removed all "tablename + '_' + columname" code and replaced with column._label, to take advantage of column labeling rules bind param compilation,when it unique-ifys the name of bind params, maintains the length of the bind parameter name instead of appending to it</pre> <h3>Modified Paths</h3> <ul> <li><a href="#sqlalchemytrunklibsqlalchemyansisqlpy">sqlalchemy/trunk/lib/sqlalchemy/ansisql.py</a></li> <li><a href="#sqlalchemytrunklibsqlalchemymappingmapperpy">sqlalchemy/trunk/lib/sqlalchemy/mapping/mapper.py</a></li> <li><a href="#sqlalchemytrunklibsqlalchemymappingpropertiespy">sqlalchemy/trunk/lib/sqlalchemy/mapping/properties.py</a></li> <li><a href="#sqlalchemytrunklibsqlalchemysqlpy">sqlalchemy/trunk/lib/sqlalchemy/sql.py</a></li> <li><a href="#sqlalchemytrunktestselectpy">sqlalchemy/trunk/test/select.py</a></li> </ul> </div> <div id="patch"> <h3>Diff</h3> <a id="sqlalchemytrunklibsqlalchemyansisqlpy"></a> <div class="modfile"><h4>Modified: sqlalchemy/trunk/lib/sqlalchemy/ansisql.py (1206 => 1207)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/trunk/lib/sqlalchemy/ansisql.py 2006-03-25 20:32:10 UTC (rev 1206) +++ sqlalchemy/trunk/lib/sqlalchemy/ansisql.py 2006-03-25 21:14:54 UTC (rev 1207) </span><span class="lines">@@ -262,7 +262,10 @@ </span><span class="cx"> # redefine the generated name of the bind param in the case </span><span class="cx"> # that we have multiple conflicting bind parameters. </span><span class="cx"> while self.binds.setdefault(key, bindparam) is not bindparam: </span><del>- key = "%s_%d" % (bindparam.key, count) </del><ins>+ # insure the name doesn't expand the length of the string + # in case we're at the edge of max identifier length + tag = "_%d" % count + key = bindparam.key[0 : len(bindparam.key) - len(tag)] + tag </ins><span class="cx"> count += 1 </span><span class="cx"> bindparam.key = key </span><span class="cx"> self.strings[bindparam] = self.bindparam_string(key) </span></span></pre></div> <a id="sqlalchemytrunklibsqlalchemymappingmapperpy"></a> <div class="modfile"><h4>Modified: sqlalchemy/trunk/lib/sqlalchemy/mapping/mapper.py (1206 => 1207)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/trunk/lib/sqlalchemy/mapping/mapper.py 2006-03-25 20:32:10 UTC (rev 1206) +++ sqlalchemy/trunk/lib/sqlalchemy/mapping/mapper.py 2006-03-25 21:14:54 UTC (rev 1207) </span><span class="lines">@@ -610,7 +610,7 @@ </span><span class="cx"> if not isinsert: </span><span class="cx"> # doing an UPDATE? put primary key values as "WHERE" parameters </span><span class="cx"> # matching the bindparam we are creating below, i.e. "<tablename>_<colname>" </span><del>- params[col.table.name + "_" + col.key] = self._getattrbycolumn(obj, col) </del><ins>+ params[col._label] = self._getattrbycolumn(obj, col) </ins><span class="cx"> else: </span><span class="cx"> # doing an INSERT, primary key col ? </span><span class="cx"> # if the primary key values are not populated, </span><span class="lines">@@ -658,7 +658,7 @@ </span><span class="cx"> if len(update): </span><span class="cx"> clause = sql.and_() </span><span class="cx"> for col in self.pks_by_table[table]: </span><del>- clause.clauses.append(col == sql.bindparam(col.table.name + "_" + col.key)) </del><ins>+ clause.clauses.append(col == sql.bindparam(col._label)) </ins><span class="cx"> statement = table.update(clause) </span><span class="cx"> rows = 0 </span><span class="cx"> for rec in update: </span></span></pre></div> <a id="sqlalchemytrunklibsqlalchemymappingpropertiespy"></a> <div class="modfile"><h4>Modified: sqlalchemy/trunk/lib/sqlalchemy/mapping/properties.py (1206 => 1207)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/trunk/lib/sqlalchemy/mapping/properties.py 2006-03-25 20:32:10 UTC (rev 1206) +++ sqlalchemy/trunk/lib/sqlalchemy/mapping/properties.py 2006-03-25 21:14:54 UTC (rev 1207) </span><span class="lines">@@ -601,7 +601,7 @@ </span><span class="cx"> if self.use_get: </span><span class="cx"> ident = [] </span><span class="cx"> for primary_key in self.mapper.pks_by_table[self.mapper.table]: </span><del>- ident.append(params[self.mapper.table.name + "_" + primary_key.name]) </del><ins>+ ident.append(params[primary_key._label]) </ins><span class="cx"> return self.mapper.get(*ident) </span><span class="cx"> elif self.order_by is not False: </span><span class="cx"> order_by = self.order_by </span><span class="lines">@@ -643,12 +643,12 @@ </span><span class="cx"> circular = isinstance(binary.left, schema.Column) and isinstance(binary.right, schema.Column) and binary.left.table is binary.right.table </span><span class="cx"> if isinstance(binary.left, schema.Column) and isinstance(binary.right, schema.Column) and ((not circular and binary.left.table is table) or (circular and binary.right is foreignkey)): </span><span class="cx"> binary.left = binds.setdefault(binary.left, </span><del>- sql.BindParamClause(binary.right.table.name + "_" + binary.right.name, None, shortname = binary.left.name)) </del><ins>+ sql.BindParamClause(binary.right._label, None, shortname = binary.left.name)) </ins><span class="cx"> binary.swap() </span><span class="cx"> </span><span class="cx"> if isinstance(binary.right, schema.Column) and isinstance(binary.left, schema.Column) and ((not circular and binary.right.table is table) or (circular and binary.left is foreignkey)): </span><span class="cx"> binary.right = binds.setdefault(binary.right, </span><del>- sql.BindParamClause(binary.left.table.name + "_" + binary.left.name, None, shortname = binary.right.name)) </del><ins>+ sql.BindParamClause(binary.left._label, None, shortname = binary.right.name)) </ins><span class="cx"> </span><span class="cx"> if secondaryjoin is not None: </span><span class="cx"> lazywhere = sql.and_(primaryjoin, secondaryjoin) </span></span></pre></div> <a id="sqlalchemytrunklibsqlalchemysqlpy"></a> <div class="modfile"><h4>Modified: sqlalchemy/trunk/lib/sqlalchemy/sql.py (1206 => 1207)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/trunk/lib/sqlalchemy/sql.py 2006-03-25 20:32:10 UTC (rev 1206) +++ sqlalchemy/trunk/lib/sqlalchemy/sql.py 2006-03-25 21:14:54 UTC (rev 1207) </span><span class="lines">@@ -1039,7 +1039,7 @@ </span><span class="cx"> if self.table.name is None: </span><span class="cx"> return BindParamClause(self.text, obj, shortname=self.text, type=self.type) </span><span class="cx"> else: </span><del>- return BindParamClause(self.table.name + "_" + self.text, obj, shortname = self.text, type=self.type) </del><ins>+ return BindParamClause(self._label, obj, shortname = self.text, type=self.type) </ins><span class="cx"> def _make_proxy(self, selectable, name = None): </span><span class="cx"> c = ColumnClause(name or self.text, selectable) </span><span class="cx"> c._original = self.original </span></span></pre></div> <a id="sqlalchemytrunktestselectpy"></a> <div class="modfile"><h4>Modified: sqlalchemy/trunk/test/select.py (1206 => 1207)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/trunk/test/select.py 2006-03-25 20:32:10 UTC (rev 1206) +++ sqlalchemy/trunk/test/select.py 2006-03-25 21:14:54 UTC (rev 1207) </span><span class="lines">@@ -176,7 +176,7 @@ </span><span class="cx"> order_by = ['dist', places.c.nm] </span><span class="cx"> ) </span><span class="cx"> </span><del>- self.runtest(q,"SELECT places.id, places.nm, zips.zipcode, latlondist((SELECT zips.latitude AS latitude FROM zips WHERE zips.zipcode = :zips_zipcode_1), (SELECT zips.longitude AS longitude FROM zips WHERE zips.zipcode = :zips_zipcode_2)) AS dist FROM places, zips WHERE zips.zipcode = :zips_zipcode ORDER BY dist, places.nm") </del><ins>+ self.runtest(q,"SELECT places.id, places.nm, zips.zipcode, latlondist((SELECT zips.latitude AS latitude FROM zips WHERE zips.zipcode = :zips_zipco_1), (SELECT zips.longitude AS longitude FROM zips WHERE zips.zipcode = :zips_zipco_2)) AS dist FROM places, zips WHERE zips.zipcode = :zips_zipcode ORDER BY dist, places.nm") </ins><span class="cx"> </span><span class="cx"> zalias = zips.alias('main_zip') </span><span class="cx"> qlat = select([zips.c.latitude], zips.c.zipcode == zalias.c.zipcode, scalar=True) </span><span class="lines">@@ -199,8 +199,8 @@ </span><span class="cx"> or_(table2.c.othername=='asdf', table2.c.othername == 'foo', table2.c.otherid == 9), </span><span class="cx"> "sysdate() = today()", </span><span class="cx"> )), </span><del>- "SELECT mytable.myid, mytable.name, mytable.description FROM mytable, myothertable WHERE mytable.myid = :mytable_myid AND (myothertable.othername = :myothertable_othername OR myothertable.othername = :myothertable_othername_1 OR myothertable.otherid = :myothertable_otherid) AND sysdate() = today()", - checkparams = {'myothertable_othername': 'asdf', 'myothertable_othername_1':'foo', 'myothertable_otherid': 9, 'mytable_myid': 12} </del><ins>+ "SELECT mytable.myid, mytable.name, mytable.description FROM mytable, myothertable WHERE mytable.myid = :mytable_myid AND (myothertable.othername = :myothertable_othername OR myothertable.othername = :myothertable_otherna_1 OR myothertable.otherid = :myothertable_otherid) AND sysdate() = today()", + checkparams = {'myothertable_othername': 'asdf', 'myothertable_otherna_1':'foo', 'myothertable_otherid': 9, 'mytable_myid': 12} </ins><span class="cx"> ) </span><span class="cx"> </span><span class="cx"> def testoperators(self): </span><span class="lines">@@ -210,13 +210,13 @@ </span><span class="cx"> ) </span><span class="cx"> </span><span class="cx"> self.runtest( </span><del>- literal("a") + literal("b") * literal("c"), ":literal + :literal_1 * :literal_2", db </del><ins>+ literal("a") + literal("b") * literal("c"), ":literal + :liter_1 * :liter_2", db </ins><span class="cx"> ) </span><span class="cx"> </span><span class="cx"> def testmultiparam(self): </span><span class="cx"> self.runtest( </span><span class="cx"> select(["*"], or_(table1.c.myid == 12, table1.c.myid=='asdf', table1.c.myid == 'foo')), </span><del>- "SELECT * FROM mytable WHERE mytable.myid = :mytable_myid OR mytable.myid = :mytable_myid_1 OR mytable.myid = :mytable_myid_2" </del><ins>+ "SELECT * FROM mytable WHERE mytable.myid = :mytable_myid OR mytable.myid = :mytable_my_1 OR mytable.myid = :mytable_my_2" </ins><span class="cx"> ) </span><span class="cx"> </span><span class="cx"> def testorderby(self): </span><span class="lines">@@ -339,13 +339,13 @@ </span><span class="cx"> </span><span class="cx"> def testliteral(self): </span><span class="cx"> self.runtest(select([literal("foo") + literal("bar")], from_obj=[table1]), </span><del>- "SELECT :literal + :literal_1 FROM mytable") </del><ins>+ "SELECT :literal + :liter_1 FROM mytable") </ins><span class="cx"> </span><span class="cx"> def testfunction(self): </span><span class="cx"> """tests the generation of functions using the func keyword""" </span><span class="cx"> # test an expression with a function </span><span class="cx"> self.runtest(func.lala(3, 4, literal("five"), table1.c.myid) * table2.c.otherid, </span><del>- "lala(:lala, :lala_1, :literal, mytable.myid) * myothertable.otherid") </del><ins>+ "lala(:lala, :la_1, :literal, mytable.myid) * myothertable.otherid") </ins><span class="cx"> </span><span class="cx"> # test it in a SELECT </span><span class="cx"> self.runtest(select([func.count(table1.c.myid)]), </span><span class="lines">@@ -416,7 +416,7 @@ </span><span class="cx"> self.runtest(x, "SELECT mytable.myid, mytable.name, mytable.description \ </span><span class="cx"> FROM mytable WHERE mytable.myid = :mytable_myid UNION \ </span><span class="cx"> SELECT mytable.myid, mytable.name, mytable.description \ </span><del>-FROM mytable WHERE mytable.myid = :mytable_myid_1 ORDER BY mytable.myid") </del><ins>+FROM mytable WHERE mytable.myid = :mytable_my_1 ORDER BY mytable.myid") </ins><span class="cx"> </span><span class="cx"> self.runtest( </span><span class="cx"> union( </span><span class="lines">@@ -487,7 +487,7 @@ </span><span class="cx"> </span><span class="cx"> def testin(self): </span><span class="cx"> self.runtest(select([table1], table1.c.myid.in_(1, 2, 3)), </span><del>- "SELECT mytable.myid, mytable.name, mytable.description FROM mytable WHERE mytable.myid IN (:mytable_myid, :mytable_myid_1, :mytable_myid_2)") </del><ins>+ "SELECT mytable.myid, mytable.name, mytable.description FROM mytable WHERE mytable.myid IN (:mytable_myid, :mytable_my_1, :mytable_my_2)") </ins><span class="cx"> </span><span class="cx"> self.runtest(select([table1], table1.c.myid.in_(select([table2.c.otherid]))), </span><span class="cx"> "SELECT mytable.myid, mytable.name, mytable.description FROM mytable WHERE mytable.myid IN (SELECT myothertable.otherid AS otherid FROM myothertable)") </span><span class="lines">@@ -549,7 +549,7 @@ </span><span class="cx"> values = { </span><span class="cx"> table1.c.name : table1.c.name + "lala", </span><span class="cx"> table1.c.myid : func.do_stuff(table1.c.myid, literal('hoho')) </span><del>- }), "UPDATE mytable SET myid=do_stuff(mytable.myid, :literal_2), name=mytable.name + :mytable_name WHERE mytable.myid = hoho(:hoho) AND mytable.name = :literal + mytable.name + :literal_1") </del><ins>+ }), "UPDATE mytable SET myid=do_stuff(mytable.myid, :liter_2), name=mytable.name + :mytable_name WHERE mytable.myid = hoho(:hoho) AND mytable.name = :literal + mytable.name + :liter_1") </ins><span class="cx"> </span><span class="cx"> def testcorrelatedupdate(self): </span><span class="cx"> # test against a straight text subquery </span></span></pre> </div> </div> </body> </html> |
From: <co...@sq...> - 2006-03-25 20:32:20
|
<!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>[1206] sqlalchemy/trunk/lib/sqlalchemy/engine.py: added *args **kwargs pass-thru to transaction()</title> </head> <body> <div id="msg"> <dl> <dt>Revision</dt> <dd>1206</dd> <dt>Author</dt> <dd>zzzeek</dd> <dt>Date</dt> <dd>2006-03-25 14:32:10 -0600 (Sat, 25 Mar 2006)</dd> </dl> <h3>Log Message</h3> <pre>added *args **kwargs pass-thru to transaction()</pre> <h3>Modified Paths</h3> <ul> <li><a href="#sqlalchemytrunklibsqlalchemyenginepy">sqlalchemy/trunk/lib/sqlalchemy/engine.py</a></li> </ul> </div> <div id="patch"> <h3>Diff</h3> <a id="sqlalchemytrunklibsqlalchemyenginepy"></a> <div class="modfile"><h4>Modified: sqlalchemy/trunk/lib/sqlalchemy/engine.py (1205 => 1206)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/trunk/lib/sqlalchemy/engine.py 2006-03-25 20:28:01 UTC (rev 1205) +++ sqlalchemy/trunk/lib/sqlalchemy/engine.py 2006-03-25 20:32:10 UTC (rev 1206) </span><span class="lines">@@ -492,12 +492,14 @@ </span><span class="cx"> for engine in engines: </span><span class="cx"> engine.commit() </span><span class="cx"> </span><del>- def transaction(self, func): </del><ins>+ def transaction(self, func, *args, **kwargs): </ins><span class="cx"> """executes the given function within a transaction boundary. this is a shortcut for </span><del>- explicitly calling begin() and commit() and optionally rollback() when execptions are raised.""" </del><ins>+ explicitly calling begin() and commit() and optionally rollback() when execptions are raised. + The given *args and **kwargs will be passed to the function as well, which could be handy + in constructing decorators.""" </ins><span class="cx"> self.begin() </span><span class="cx"> try: </span><del>- func() </del><ins>+ func(*args, **kwargs) </ins><span class="cx"> except: </span><span class="cx"> self.rollback() </span><span class="cx"> raise </span></span></pre> </div> </div> </body> </html> |
From: <co...@sq...> - 2006-03-25 20:28: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>[1205] sqlalchemy/trunk/doc/build/content/datamapping.myt: method add</title> </head> <body> <div id="msg"> <dl> <dt>Revision</dt> <dd>1205</dd> <dt>Author</dt> <dd>zzzeek</dd> <dt>Date</dt> <dd>2006-03-25 14:28:01 -0600 (Sat, 25 Mar 2006)</dd> </dl> <h3>Log Message</h3> <pre>method add</pre> <h3>Modified Paths</h3> <ul> <li><a href="#sqlalchemytrunkdocbuildcontentdatamappingmyt">sqlalchemy/trunk/doc/build/content/datamapping.myt</a></li> </ul> </div> <div id="patch"> <h3>Diff</h3> <a id="sqlalchemytrunkdocbuildcontentdatamappingmyt"></a> <div class="modfile"><h4>Modified: sqlalchemy/trunk/doc/build/content/datamapping.myt (1204 => 1205)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/trunk/doc/build/content/datamapping.myt 2006-03-25 20:27:25 UTC (rev 1204) +++ sqlalchemy/trunk/doc/build/content/datamapping.myt 2006-03-25 20:28:01 UTC (rev 1205) </span><span class="lines">@@ -65,7 +65,7 @@ </span><span class="cx"> </span><span class="cx"> userlist = User.mapper.select_by(user_id=12) </span><span class="cx"> </&> </span><del>- <p>There is also a full-blown "monkeypatch" function that creates a primary mapper, attaches the above mapper class property, and also the methods <code>get, get_by, select, select_by, selectone, selectfirst, commit</code> and <code>delete</code>:</p> </del><ins>+ <p>There is also a full-blown "monkeypatch" function that creates a primary mapper, attaches the above mapper class property, and also the methods <code>get, get_by, select, select_by, selectone, selectfirst, commit, expire, refresh, expunge</code> and <code>delete</code>:</p> </ins><span class="cx"> <&|formatting.myt:code&> </span><span class="cx"> # "assign" a mapper to the User class/users table </span><span class="cx"> assign_mapper(User, users) </span></span></pre> </div> </div> </body> </html> |
From: <co...@sq...> - 2006-03-25 20:27: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>[1204] sqlalchemy/trunk/lib/sqlalchemy/mapping/__init__.py: added expire/refresh/expunge to assign_mapper</title> </head> <body> <div id="msg"> <dl> <dt>Revision</dt> <dd>1204</dd> <dt>Author</dt> <dd>zzzeek</dd> <dt>Date</dt> <dd>2006-03-25 14:27:25 -0600 (Sat, 25 Mar 2006)</dd> </dl> <h3>Log Message</h3> <pre>added expire/refresh/expunge to assign_mapper</pre> <h3>Modified Paths</h3> <ul> <li><a href="#sqlalchemytrunklibsqlalchemymapping__init__py">sqlalchemy/trunk/lib/sqlalchemy/mapping/__init__.py</a></li> </ul> </div> <div id="patch"> <h3>Diff</h3> <a id="sqlalchemytrunklibsqlalchemymapping__init__py"></a> <div class="modfile"><h4>Modified: sqlalchemy/trunk/lib/sqlalchemy/mapping/__init__.py (1203 => 1204)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/trunk/lib/sqlalchemy/mapping/__init__.py 2006-03-25 19:52:37 UTC (rev 1203) +++ sqlalchemy/trunk/lib/sqlalchemy/mapping/__init__.py 2006-03-25 20:27:25 UTC (rev 1204) </span><span class="lines">@@ -113,8 +113,17 @@ </span><span class="cx"> objectstore.commit(self) </span><span class="cx"> def delete(self): </span><span class="cx"> objectstore.delete(self) </span><ins>+ def expire(self): + objectstore.expire(self) + def refresh(self): + objectstore.refresh(self) + def expunge(self): + objectstore.expunge(self) </ins><span class="cx"> class_.commit = commit </span><span class="cx"> class_.delete = delete </span><ins>+ class_.expire = expire + class_.refresh = refresh + class_.expunge = expunge </ins><span class="cx"> </span><span class="cx"> def cascade_mappers(*classes_or_mappers): </span><span class="cx"> """given a list of classes and/or mappers, identifies the foreign key relationships </span></span></pre> </div> </div> </body> </html> |
From: <co...@sq...> - 2006-03-25 19:52: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>[1203] sqlalchemy/trunk/doc/docs.css: ah well the overflow doesnt work</title> </head> <body> <div id="msg"> <dl> <dt>Revision</dt> <dd>1203</dd> <dt>Author</dt> <dd>zzzeek</dd> <dt>Date</dt> <dd>2006-03-25 13:52:37 -0600 (Sat, 25 Mar 2006)</dd> </dl> <h3>Log Message</h3> <pre>ah well the overflow doesnt work</pre> <h3>Modified Paths</h3> <ul> <li><a href="#sqlalchemytrunkdocdocscss">sqlalchemy/trunk/doc/docs.css</a></li> </ul> </div> <div id="patch"> <h3>Diff</h3> <a id="sqlalchemytrunkdocdocscss"></a> <div class="modfile"><h4>Modified: sqlalchemy/trunk/doc/docs.css (1202 => 1203)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/trunk/doc/docs.css 2006-03-25 19:46:04 UTC (rev 1202) +++ sqlalchemy/trunk/doc/docs.css 2006-03-25 19:52:37 UTC (rev 1203) </span><span class="lines">@@ -97,7 +97,7 @@ </span><span class="cx"> } </span><span class="cx"> </span><span class="cx"> pre { </span><del>- overflow:auto; </del><ins>+ /*overflow:auto;*/ </ins><span class="cx"> } </span><span class="cx"> .codeline { </span><span class="cx"> font-family:courier, serif; </span></span></pre> </div> </div> </body> </html> |
From: <co...@sq...> - 2006-03-25 19:46: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>[1202] sqlalchemy/trunk/doc/build/content/unitofwork.myt: dev</title> </head> <body> <div id="msg"> <dl> <dt>Revision</dt> <dd>1202</dd> <dt>Author</dt> <dd>zzzeek</dd> <dt>Date</dt> <dd>2006-03-25 13:46:04 -0600 (Sat, 25 Mar 2006)</dd> </dl> <h3>Log Message</h3> <pre>dev</pre> <h3>Modified Paths</h3> <ul> <li><a href="#sqlalchemytrunkdocbuildcontentunitofworkmyt">sqlalchemy/trunk/doc/build/content/unitofwork.myt</a></li> </ul> </div> <div id="patch"> <h3>Diff</h3> <a id="sqlalchemytrunkdocbuildcontentunitofworkmyt"></a> <div class="modfile"><h4>Modified: sqlalchemy/trunk/doc/build/content/unitofwork.myt (1201 => 1202)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/trunk/doc/build/content/unitofwork.myt 2006-03-25 19:34:34 UTC (rev 1201) +++ sqlalchemy/trunk/doc/build/content/unitofwork.myt 2006-03-25 19:46:04 UTC (rev 1202) </span><span class="lines">@@ -379,13 +379,14 @@ </span><span class="cx"> <&|doclib.myt:item, name="scope", description="Custom Session Objects/Custom Scopes" &> </span><span class="cx"> </span><span class="cx"> <p>For users who want to make their own Session subclass, or replace the algorithm used to return scoped Session objects (i.e. the objectstore.get_session() method):</p> </span><del>- <&|formatting.myt:code&> </del><ins>+ <&|formatting.myt:code, title="Create a Session"&> </ins><span class="cx"> # make a new Session </span><span class="cx"> s = objectstore.Session() </span><span class="cx"> </span><span class="cx"> # set it as the current thread-local session </span><span class="cx"> objectstore.session_registry.set(s) </span><del>- </del><ins>+ </&> + <&|formatting.myt:code, title="Create a custom Registry Algorithm"&> </ins><span class="cx"> # set the objectstore's session registry to a different algorithm </span><span class="cx"> </span><span class="cx"> def create_session(): </span><span class="lines">@@ -404,9 +405,6 @@ </span><span class="cx"> <&|formatting.myt:code&> </span><span class="cx"> # make an engine with echo_uow </span><span class="cx"> engine = create_engine('myengine...', echo_uow=True) </span><del>- - # globally turn on echo - objectstore.LOG = True </del><span class="cx"> </&> </span><span class="cx"> <p>Commits will then dump to the standard output displays like the following:</p> </span><span class="cx"> <&|formatting.myt:code, syntaxtype=None&> </span></span></pre> </div> </div> </body> </html> |
From: <co...@sq...> - 2006-03-25 19:34:44
|
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head><style type="text/css"><!-- #msg dl { border: 1px #006 solid; background: #369; padding: 6px; color: #fff; } #msg dt { float: left; width: 6em; font-weight: bold; } #msg dt:after { content:':';} #msg dl, #msg dt, #msg ul, #msg li { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; } #msg dl a { font-weight: bold} #msg dl a:link { color:#fc3; } #msg dl a:active { color:#ff0; } #msg dl a:visited { color:#cc6; } h3 { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; font-weight: bold; } #msg pre { overflow: auto; background: #ffc; border: 1px #fc0 solid; padding: 6px; } #msg ul, pre { overflow: auto; } #patch { width: 100%; } #patch h4 {font-family: verdana,arial,helvetica,sans-serif;font-size:10pt;padding:8px;background:#369;color:#fff;margin:0;} #patch .propset h4, #patch .binary h4 {margin:0;} #patch pre {padding:0;line-height:1.2em;margin:0;} #patch .diff {width:100%;background:#eee;padding: 0 0 10px 0;overflow:auto;} #patch .propset .diff, #patch .binary .diff {padding:10px 0;} #patch span {display:block;padding:0 10px;} #patch .modfile, #patch .addfile, #patch .delfile, #patch .propset, #patch .binary, #patch .copfile {border:1px solid #ccc;margin:10px 0;} #patch ins {background:#dfd;text-decoration:none;display:block;padding:0 10px;} #patch del {background:#fdd;text-decoration:none;display:block;padding:0 10px;} #patch .lines, .info {color:#888;background:#fff;} --></style> <title>[1201] sqlalchemy/trunk/doc/build/content/unitofwork.myt: dev</title> </head> <body> <div id="msg"> <dl> <dt>Revision</dt> <dd>1201</dd> <dt>Author</dt> <dd>zzzeek</dd> <dt>Date</dt> <dd>2006-03-25 13:34:34 -0600 (Sat, 25 Mar 2006)</dd> </dl> <h3>Log Message</h3> <pre>dev</pre> <h3>Modified Paths</h3> <ul> <li><a href="#sqlalchemytrunkdocbuildcontentunitofworkmyt">sqlalchemy/trunk/doc/build/content/unitofwork.myt</a></li> </ul> </div> <div id="patch"> <h3>Diff</h3> <a id="sqlalchemytrunkdocbuildcontentunitofworkmyt"></a> <div class="modfile"><h4>Modified: sqlalchemy/trunk/doc/build/content/unitofwork.myt (1200 => 1201)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/trunk/doc/build/content/unitofwork.myt 2006-03-25 18:53:51 UTC (rev 1200) +++ sqlalchemy/trunk/doc/build/content/unitofwork.myt 2006-03-25 19:34:34 UTC (rev 1201) </span><span class="lines">@@ -54,9 +54,53 @@ </span><span class="cx"> [<__main__.User object at 0x712630>, <__main__.Address object at 0x712a70>] </span><span class="cx"> """ %> </span><span class="cx"> </&> </span><ins>+ + <p>The identity of each object instance is available via the _instance_key property attached to each object instance, and is a tuple consisting of the object's class and an additional tuple of primary key values, in the order that they appear within the table definition:</p> + <&|formatting.myt:code&> + >>> obj._instance_key + (<class 'test.tables.User'>, (7,)) + </&> + + <p> + At the moment that an object is assigned this key, it is also added to the current thread's unit-of-work's identity map. + </p> </ins><span class="cx"> </span><ins>+ <p>The get() method on a mapper, which retrieves an object based on primary key identity, also checks in the current identity map first to save a database round-trip if possible. In the case of an object lazy-loading a single child object, the get() method is used as well, so scalar-based lazy loads may in some cases not query the database; this is particularly important for backreference relationships as it can save a lot of queries.</p> + + <p>Methods on mappers and the objectstore module, which are relevant to identity include the following:</p> + <&|formatting.myt:code&> + # assume 'm' is a mapper + m = mapper(User, users) + + # get the identity key corresponding to a primary key + key = m.identity_key(7) + + # for composite key, list out the values in the order they + # appear in the table + key = m.identity_key(12, 'rev2') + + # get the identity key given a primary key + # value as a tuple and a class + key = objectstore.get_id_key((12, 'rev2'), User) + + # get the identity key for an object, whether or not it actually + # has one attached to it (m is the mapper for obj's class) + key = m.instance_key(obj) + + # is this key in the current identity map? + session.has_key(key) + + # is this object in the current identity map? + session.has_instance(obj) + + # get this object from the current identity map based on + # singular/composite primary key, or if not go + # and load from the database + obj = m.get(12, 'rev2') </ins><span class="cx"> </&> </span><span class="cx"> </span><ins>+ </&> + </ins><span class="cx"> <&|doclib.myt:item, name="changed", description="Whats Changed ?" &> </span><span class="cx"> <p>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 <b>newly created</b> objects, records of all objects whose attributes have been <b>modified</b>, records of all objects that have been marked as <b>deleted</b>, and records of all <b>modified list-based attributes</b> where additions or deletions have occurred. These lists are used when a <code>commit()</code> call is issued to save all changes. After the commit occurs, these lists are all cleared out.</p> </span><span class="cx"> </span><span class="lines">@@ -156,7 +200,7 @@ </span><span class="cx"> <p>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.</p> </span><span class="cx"> </span><span class="cx"> <&|doclib.myt:item, name="whatis", description="What Commit is, and Isn't" &> </span><del>- <p>The purpose of the Commit 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 update the identifying object attributes that correspond directly to database columns. <b>And thats it.</b> This means, it is not going to change anything about your objects as they exist in memory, with the exception of synchronizing the identifier attributes on saved and updated objects as they correspond directly to newly inserted or updated rows, which typically include only primary key and foreign key attributes that in most cases are integers. A brief list of what will <b>not</b> happen includes:</p> </del><ins>+ <p>The purpose of the Commit 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. <b>And thats it.</b> It does not affect any <code>relation</code>-based object attributes, that is attributes that reference other objects or lists of other objects, in any way. A brief list of what will <b>not</b> happen includes:</p> </ins><span class="cx"> <ul> </span><span class="cx"> <li>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.</li> </span><span class="cx"> <li>It will not set or remove any scalar references to other objects, even if the corresponding database identifier columns have been committed.</li> </span><span class="lines">@@ -164,7 +208,8 @@ </span><span class="cx"> <p>This means, if you set <code>address.user_id</code> to 5, that integer attribute will be saved, but it will not place an <code>Address</code> object in the <code>addresses</code> attribute of the corresponding <code>User</code> 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 <code>Address</code> 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 <code>User</code>, it will remain.</p> </span><span class="cx"> <P>So the primary guideline for dealing with commit() is, <b>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.</b> 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.</p> </span><span class="cx"> </span><del>- <p>A terrific feature of SQLAlchemy which is also a supreme source of confusion is the backreference feature, described in <&formatting.myt:link, path="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. <b>This feature has nothing to do whatsoever with the Unit of Work.</b> 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 r! emoved to/from each other, independent of any kind of database operation. It does not change the golden rule, that the developer is reponsible for maintaining in-memory object relationships.</p> </del><ins>+ <p>A terrific feature of SQLAlchemy which is also a supreme source of confusion is the backreference feature, described in <&formatting.myt:link, path="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. <b>This feature has nothing to do whatsoever with the Unit of Work.*</b> 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 r! emoved to/from each other, independent of any kind of database operation. It does not change the golden rule, that the developer is reponsible for maintaining in-memory object relationships.</p> + <p>* there is an internal relationship between two <code>relations</code> that have a backreference, which state that a change operation is only logged once to the unit of work instead of two separate changes since the two changes are "equivalent", so a backreference does affect the information that is sent to the Unit of Work. But the Unit of Work itself has no knowledge of this arrangement and has no ability to affect it.</p> </ins><span class="cx"> </&> </span><span class="cx"> </&> </span><span class="cx"> </span><span class="lines">@@ -191,7 +236,7 @@ </span><span class="cx"> # or via Session </span><span class="cx"> objectstore.get_session().clear() </span><span class="cx"> </&> </span><del>- <p>This is the easiest way to "start fresh", as in a web application that wants to have a newly loaded graph of objects on each request. Any object instances before the clear operation should be discarded.</p> </del><ins>+ <p>This is the easiest way to "start fresh", as in a web application that wants to have a newly loaded graph of objects on each request. Any object instances created before the clear operation should either be discarded or at least not used with any Mapper or Unit Of Work operations (with the exception of <code>import_instance()</code>), as they no longer have any relationship to the current Unit of Work, and their behavior with regards to the current session is undefined.</p> </ins><span class="cx"> </&> </span><span class="cx"> </span><span class="cx"> <&|doclib.myt:item, name="refreshexpire", description="Refresh / Expire" &> </span><span class="lines">@@ -215,48 +260,27 @@ </span><span class="cx"> </&> </span><span class="cx"> </span><span class="cx"> <&|doclib.myt:item, name="import", description="Import Instance" &> </span><ins>+ <p>The _instance_key attribute placed on object instances is designed to work with objects that are serialized into strings and brought back again. As it contains no references to internal structures or database connections, applications that use caches or session storage which require serialization (i.e. pickling) can store SQLAlchemy-loaded objects. However, as mentioned earlier, an object with a particular database identity is only allowed to exist uniquely within the current unit-of-work scope. So, upon deserializing such an object, it has to "check in" with the current Session. This is achieved via the <code>import_instance()</code> method:</p> + <&|formatting.myt:code&> + # deserialize an object + myobj = pickle.loads(mystring) + + # "import" it. if the objectstore already had this object in the + # identity map, then you get back the one from the current session. + myobj = session.import_instance(myobj) + </&> + <p>Note that the import_instance() function will either mark the deserialized object as the official copy in the current identity map, which includes updating its _instance_key with the current application's class instance, or it will discard it and return the corresponding object that was already present. Thats why its important to receive the return results from the method and use the result as the official object instance.</p> </ins><span class="cx"> </&> </span><del>- - </&> - <&|doclib.myt:item, name="begincommit", description="Begin/Commit" &> - <p>The current thread's UnitOfWork object keeps track of objects that are modified. It maintains the following lists:</p> - <&|formatting.myt:code&> - # new objects that were just constructed - objectstore.get_session().new - - # objects that exist in the database, that were modified - objectstore.get_session().dirty - - # objects that have been marked as deleted via objectstore.delete() - objectstore.get_session().deleted - </&> - <p>To commit the changes stored in those lists, just issue a commit. This can be called via <span class="codeline">objectstore.session().commit()</span>, or through the module-level convenience method in the objectstore module:</p> - <&|formatting.myt:code&> - objectstore.commit() - </&> - <p>The commit operation takes place within a SQL-level transaction, so any failures that occur will roll back the state of everything to before the commit took place.</p> - <p>When mappers are created for classes, new object construction automatically places objects in the "new" list on the UnitOfWork, and object modifications automatically place objects in the "dirty" list. To mark objects as to be deleted, use the "delete" method on UnitOfWork, or the module level version:</p> - <&|formatting.myt:code&> - objectstore.delete(myobj1, myobj2, ...) - </&> - - <p>Commit() can also take a list of objects which narrow its scope to looking at just those objects to save:</p> - <&|formatting.myt:code&> - objectstore.commit(myobj1, myobj2, ...) - </&> - <p>Committing just a subset of instances should be used carefully, as it may result in an inconsistent save state between dependent objects (it should manage to locate loaded dependencies and save those also, but it hasnt been tested much).</p> - - <&|doclib.myt:item, name="begin", description="Controlling Scope with begin()" &> - <p><b>status</b> - release 0.1.1/SVN head</p> - <p>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.</p> </del><ins>+ + <&|doclib.myt:item, name="begin", description="Begin" &> + <p>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.</p> </ins><span class="cx"> <p>The begin() method returns a transactional object, upon which you can call commit() or rollback(). <b>Only this transactional object controls the transaction</b> - commit() upon the Session will do nothing until commit() or rollback() is called upon the transactional object.</p> </span><span class="cx"> <&|formatting.myt:code&> </span><span class="cx"> # modify an object </span><span class="cx"> myobj1.foo = "something new" </span><span class="cx"> </span><del>- # begin an objectstore scope - # this is equivalent to objectstore.get_session().begin() - trans = objectstore.begin() </del><ins>+ # begin + trans = session.begin() </ins><span class="cx"> </span><span class="cx"> # modify another object </span><span class="cx"> myobj2.lala = "something new" </span><span class="lines">@@ -266,6 +290,11 @@ </span><span class="cx"> </&> </span><span class="cx"> <p>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.</p> </span><span class="cx"> </&> </span><ins>+ + </&> + + <&|doclib.myt:item, name="advscope", description="Advanced UnitOfWork Management"&> + </ins><span class="cx"> <&|doclib.myt:item, name="transactionnesting", description="Nesting UnitOfWork in a Database Transaction" &> </span><span class="cx"> <p>The UOW commit operation places its INSERT/UPDATE/DELETE operations within the scope of a database transaction controlled by a SQLEngine: </span><span class="cx"> <&|formatting.myt:code&> </span><span class="lines">@@ -291,70 +320,10 @@ </span><span class="cx"> </&> </span><span class="cx"> </span><span class="cx"> </&> </span><del>- </&> - <&|doclib.myt:item, name="identity", description="The Identity Map" &> - <p>All object instances which are saved to the database, or loaded from the database, are given an identity by the mapper/objectstore. This identity is available via the _instance_key property attached to each object instance, and is a tuple consisting of the table's class, the SQLAlchemy-specific "hash key" of the table its persisted to, and an additional tuple of primary key values, in the order that they appear within the table definition:</p> - <&|formatting.myt:code&> - >>> obj._instance_key - (<class 'test.tables.User'>, "Table('users',SQLiteSQLEngine(([':memory:'], {})),schema=None)", (7,)) - </&> - <p>Note that this identity is a database identity, not an in-memory identity. An application can have several different objects in different unit-of-work scopes that have the same database identity, or an object can be removed from memory, and constructed again later, with the same database identity. What can <b>never</b> happen is for two copies of the same object to exist in the same unit-of-work scope with the same database identity; this is guaranteed by the <b>identity map</b>. - </p> - <p> - At the moment that an object is assigned this key, it is also added to the current thread's unit-of-work's identity map. The identity map is just a WeakValueDictionary which maintains the one and only reference to a particular object within the current unit of work scope. It is used when result rows are fetched from the database to insure that only one copy of a particular object actually comes from that result set in the case that eager loads or other joins are used, or if the object had already been loaded from a previous result set. The get() method on a mapper, which retrieves an object based on primary key identity, also checks in the current identity map first to save a database round-trip if possible. In the case of an object lazy-loading a single child object, the get() method is also used. - </p> - <p>Methods on mappers and the objectstore module, which are relevant to identity include the following:</p> - <&|formatting.myt:code&> - # assume 'm' is a mapper - m = mapper(User, users) - - # get the identity key corresponding to a primary key - key = m.identity_key(7) - - # for composite key, list out the values in the order they - # appear in the table - key = m.identity_key(12, 'rev2') </del><span class="cx"> </span><del>- # get the identity key given a primary key - # value as a tuple, a class, and a table - key = objectstore.get_id_key((12, 'rev2'), User, users) - - # get the identity key for an object, whether or not it actually - # has one attached to it (m is the mapper for obj's class) - key = m.instance_key(obj) - - # same thing, from the objectstore (works for any obj type) - key = objectstore.instance_key(obj) - - # is this key in the current identity map? - objectstore.has_key(key) - - # is this object in the current identity map? - objectstore.has_instance(obj) </del><span class="cx"> </span><del>- # get this object from the current identity map based on - # singular/composite primary key, or if not go - # and load from the database - obj = m.get(12, 'rev2') - </&> - </&> - <&|doclib.myt:item, name="import", description="Bringing External Instances into the UnitOfWork" &> - <p>The _instance_key attribute is designed to work with objects that are serialized into strings and brought back again. As it contains no references to internal structures or database connections, applications that use caches or session storage which require serialization (i.e. pickling) can store SQLAlchemy-loaded objects. However, as mentioned earlier, an object with a particular database identity is only allowed to exist uniquely within the current unit-of-work scope. So, upon deserializing such an object, it has to "check in" with the current unit-of-work/identity map combination, to insure that it is the only unique instance. This is achieved via the <span class="codeline">import_instance()</span> function in objectstore:</p> - <&|formatting.myt:code&> - # deserialize an object - myobj = pickle.loads(mystring) - - # "import" it. if the objectstore already had this object in the - # identity map, then you get back the one from the current session. - myobj = objectstore.import_instance(myobj) - </&> -<p>Note that the import_instance() function will either mark the deserialized object as the official copy in the current identity map, which includes updating its _instance_key with the current application's class instance, or it will discard it and return the corresponding object that was already present.</p> - </&> - - <&|doclib.myt:item, name="advscope", description="Advanced UnitOfWork Management"&> - </del><span class="cx"> <&|doclib.myt:item, name="object", description="Per-Object Sessions" &> </span><del>- <p>Sessions can be created on an ad-hoc basis and used for individual groups of objects and operations. This has the effect of bypassing the entire "global"/"threadlocal" UnitOfWork system and explicitly using a particular Session:</p> </del><ins>+ <p>Sessions can be created on an ad-hoc basis and used for individual groups of objects and operations. This has the effect of bypassing the normal thread-local Session and explicitly using a particular Session:</p> </ins><span class="cx"> <&|formatting.myt:code&> </span><span class="cx"> # make a new Session with a global UnitOfWork </span><span class="cx"> s = objectstore.Session() </span><span class="lines">@@ -381,7 +350,31 @@ </span><span class="cx"> finally: </span><span class="cx"> objectstore.pop_session() </span><span class="cx"> </&> </span><ins>+ <&|doclib.myt:item, name="nested", description="Nested Transaction Sessions" &> + <p>Sessions also now support a "nested transaction" feature whereby a second Session can use a different database connection. This can be used inside of a larger database transaction to issue commits to the database that will be committed independently of the larger transaction's status:</p> + <&|formatting.myt:code&> + engine.begin() + try: + a = MyObj() + b = MyObj() + + sess = Session(nest_on=engine) + objectstore.push_session(sess) + try: + c = MyObj() + objectstore.commit() # will commit "c" to the database, + # even if the external transaction rolls back + finally: + objectstore.pop_session() + + objectstore.commit() # commit "a" and "b" to the database + engine.commit() + except: + engine.rollback() + raise </ins><span class="cx"> </&> </span><ins>+ </&> + </&> </ins><span class="cx"> </span><span class="cx"> <&|doclib.myt:item, name="scope", description="Custom Session Objects/Custom Scopes" &> </span><span class="cx"> </span></span></pre> </div> </div> </body> </html> |
From: <co...@sq...> - 2006-03-25 18:54: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>[1200] sqlalchemy/trunk/doc/build/content/unitofwork.myt: doc dev...</title> </head> <body> <div id="msg"> <dl> <dt>Revision</dt> <dd>1200</dd> <dt>Author</dt> <dd>zzzeek</dd> <dt>Date</dt> <dd>2006-03-25 12:53:51 -0600 (Sat, 25 Mar 2006)</dd> </dl> <h3>Log Message</h3> <pre>doc dev...</pre> <h3>Modified Paths</h3> <ul> <li><a href="#sqlalchemytrunkdocbuildcontentunitofworkmyt">sqlalchemy/trunk/doc/build/content/unitofwork.myt</a></li> </ul> </div> <div id="patch"> <h3>Diff</h3> <a id="sqlalchemytrunkdocbuildcontentunitofworkmyt"></a> <div class="modfile"><h4>Modified: sqlalchemy/trunk/doc/build/content/unitofwork.myt (1199 => 1200)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/trunk/doc/build/content/unitofwork.myt 2006-03-25 18:13:01 UTC (rev 1199) +++ sqlalchemy/trunk/doc/build/content/unitofwork.myt 2006-03-25 18:53:51 UTC (rev 1200) </span><span class="lines">@@ -156,19 +156,30 @@ </span><span class="cx"> <p>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.</p> </span><span class="cx"> </span><span class="cx"> <&|doclib.myt:item, name="whatis", description="What Commit is, and Isn't" &> </span><del>- <p>The purpose of the Commit operation is to instruct the Unit of Work to analyze its lists of modified objects, assemble them into a dependency graph, and fire off the appopriate INSERT, UPDATE, and DELETE statements via the mappers related to those objects. <b>And thats it.</b> This means, it is not going to change anything about your objects as they exist in memory, with the exception of populating scalar object attributes with newly generated default column values which normally only involves primary and foreign key identifiers. A brief list of what will <b>not</b> happen includes:</p> </del><ins>+ <p>The purpose of the Commit 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 update the identifying object attributes that correspond directly to database columns. <b>And thats it.</b> This means, it is not going to change anything about your objects as they exist in memory, with the exception of synchronizing the identifier attributes on saved and updated objects as they correspond directly to newly inserted or updated rows, which typically include only primary key and foreign key attributes that in most cases are integers. A brief list of what will <b>not</b> happen includes:</p> </ins><span class="cx"> <ul> </span><del>- <li>It will not append or delete any items from any list-based attributes. Any objects that have been inserted or deleted will be updated as such in the database, but if a deleted object instance is still attached to a parent object, it will remain.</li> - <li>It will not set or remove any scalar object attributes, even if the database identifier columns have been committed. This means, if you set <code>user.address_id</code> to 5, that will be saved, but it will not place an <code>Address</code> object on the <code>user</code> object. Similarly, if the <code>Address</code> object is deleted but is still attached to the <code>User</code>, it will remain.</li> </del><ins>+ <li>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.</li> + <li>It will not set or remove any scalar references to other objects, even if the corresponding database identifier columns have been committed.</li> </ins><span class="cx"> </ul> </span><del>- <P>So the primary guideline for dealing with commit() is, <b>the developer is responsible for maintaining the objects in memory, the unit of work is responsible for maintaining the database representation.</b></p> </del><ins>+ <p>This means, if you set <code>address.user_id</code> to 5, that integer attribute will be saved, but it will not place an <code>Address</code> object in the <code>addresses</code> attribute of the corresponding <code>User</code> 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 <code>Address</code> 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 <code>User</code>, it will remain.</p> + <P>So the primary guideline for dealing with commit() is, <b>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.</b> 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.</p> </ins><span class="cx"> </span><del>- <p>A terrific feature of SQLAlchemy which is also a supreme source of confusion is the backreference feature, described in <&formatting.myt:link, path="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. 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. <b>This feature has nothing to do whatsoever with the Unit of Work.</b> It is strictly a small convenience feature added to support an extremely common pattern. Besides this one little feature, <b>the developer must maintain in-memory object relationships manually</b>. Note that we are talking about the <b>manipu! lation</b> of objects, not the initial loading of them which is handled by the mapper.</p> </del><ins>+ <p>A terrific feature of SQLAlchemy which is also a supreme source of confusion is the backreference feature, described in <&formatting.myt:link, path="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. <b>This feature has nothing to do whatsoever with the Unit of Work.</b> 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 re! moved to/from each other, independent of any kind of database operation. It does not change the golden rule, that the developer is reponsible for maintaining in-memory object relationships.</p> </ins><span class="cx"> </&> </span><span class="cx"> </&> </span><span class="cx"> </span><span class="cx"> <&|doclib.myt:item, name="delete", description="Delete" &> </span><ins>+ <P>The delete call places an object or objects into the Unit of Work's list of objects to be marked as deleted:</p> + <&|formatting.myt:code&> + # mark three objects to be deleted + objectstore.get_session().delete(obj1, obj2, obj3) + + # commit + objectstore.get_session().commit() </ins><span class="cx"> </&> </span><ins>+ <p>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 <code>private=True</code> option, DELETE statements will be issued for objects within that relationship in addition to that of the primary deleted object; this is called a <b>cascading delete</b>.</p> + <p>As stated before, the purpose of delete is strictly to issue DELETE statements to the database. It does not affect the in-memory structure of objects, other than changing the identifying attributes on objects, such as setting foreign key identifiers on updated rows to None. It has no effect on the status of references between object instances, nor any effect on the Python garbage-collection status of objects.</p> + </&> </ins><span class="cx"> </span><span class="cx"> <&|doclib.myt:item, name="clear", description="Clear" &> </span><span class="cx"> <p>To clear out the current thread's UnitOfWork, which has the effect of discarding the Identity Map and the lists of all objects that have been modified, just issue a clear: </span><span class="lines">@@ -184,10 +195,24 @@ </span><span class="cx"> </&> </span><span class="cx"> </span><span class="cx"> <&|doclib.myt:item, name="refreshexpire", description="Refresh / Expire" &> </span><ins>+ <p>To assist with the Unit of Work's "sticky" behavior, individual objects can have all of their attributes immediately re-loaded from the database, or marked as "expired" which will cause a re-load to occur upon the next access of any of the object's mapped attributes. This includes all relationships, so lazy-loaders will be re-initialized, eager relationships will be repopulated. Any changes marked on the object are discarded:</p> + <&|formatting.myt:code&> + # immediately re-load attributes on obj1, obj2 + session.refresh(obj1, obj2) + + # expire objects obj1, obj2, attributes will be reloaded + # on the next access: + session.expire(obj1, obj2, obj3) </ins><span class="cx"> </&> </span><ins>+ </&> </ins><span class="cx"> </span><span class="cx"> <&|doclib.myt:item, name="expunge", description="Expunge" &> </span><ins>+ <P>Expunge simply removes all record of an object from the current Session. This includes the identity map, and all history-tracking lists:</p> + <&|formatting.myt:code&> + session.expunge(obj1) </ins><span class="cx"> </&> </span><ins>+ <p>Use <code>expunge</code> when youd like to remove an object altogether from memory, such as before calling <code>del</code> on it, which will prevent any "ghost" operations occuring when the session is committed.</p> + </&> </ins><span class="cx"> </span><span class="cx"> <&|doclib.myt:item, name="import", description="Import Instance" &> </span><span class="cx"> </&> </span></span></pre> </div> </div> </body> </html> |
From: <co...@sq...> - 2006-03-25 18:13: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>[1199] sqlalchemy/trunk/doc: doc dev</title> </head> <body> <div id="msg"> <dl> <dt>Revision</dt> <dd>1199</dd> <dt>Author</dt> <dd>zzzeek</dd> <dt>Date</dt> <dd>2006-03-25 12:13:01 -0600 (Sat, 25 Mar 2006)</dd> </dl> <h3>Log Message</h3> <pre>doc dev</pre> <h3>Modified Paths</h3> <ul> <li><a href="#sqlalchemytrunkdocbuildcontentunitofworkmyt">sqlalchemy/trunk/doc/build/content/unitofwork.myt</a></li> <li><a href="#sqlalchemytrunkdocbuildlibhighlightpy">sqlalchemy/trunk/doc/build/lib/highlight.py</a></li> <li><a href="#sqlalchemytrunkdocdocscss">sqlalchemy/trunk/doc/docs.css</a></li> </ul> </div> <div id="patch"> <h3>Diff</h3> <a id="sqlalchemytrunkdocbuildcontentunitofworkmyt"></a> <div class="modfile"><h4>Modified: sqlalchemy/trunk/doc/build/content/unitofwork.myt (1198 => 1199)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/trunk/doc/build/content/unitofwork.myt 2006-03-25 17:23:41 UTC (rev 1198) +++ sqlalchemy/trunk/doc/build/content/unitofwork.myt 2006-03-25 18:13:01 UTC (rev 1199) </span><span class="lines">@@ -58,7 +58,7 @@ </span><span class="cx"> </&> </span><span class="cx"> </span><span class="cx"> <&|doclib.myt:item, name="changed", description="Whats Changed ?" &> </span><del>- <p>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 <b>newly created</b> objects, records of all objects whose attributes have been modified, records of all objects that have been marked as deleted, and records of all list-based attributes where additions or deletions have occurred. These lists are used when a <code>commit()</code> call is issued to save all changes. After the commit occurs, these lists are all cleared out.</p> </del><ins>+ <p>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 <b>newly created</b> objects, records of all objects whose attributes have been <b>modified</b>, records of all objects that have been marked as <b>deleted</b>, and records of all <b>modified list-based attributes</b> where additions or deletions have occurred. These lists are used when a <code>commit()</code> call is issued to save all changes. After the commit occurs, these lists are all cleared out.</p> </ins><span class="cx"> </span><span class="cx"> <p>These records are all tracked by a collection of <code>Set</code> objects (which are a SQLAlchemy-specific instance called a <code>HashSet</code>) that are also viewable off the Session:</p> </span><span class="cx"> <&|formatting.myt:code&> </span><span class="lines">@@ -76,64 +76,96 @@ </span><span class="cx"> </&> </span><span class="cx"> <p>Heres an interactive example, assuming the <code>User</code> and <code>Address</code> mapper setup first outlined in <&formatting.myt:link, path="datamapping_relations"&>:</p> </span><span class="cx"> <&|formatting.myt:code&> </span><del>- >>> session = objectstore.get_session() </del><ins>+ ">>>" # get the current thread's session + ">>>" session = objectstore.get_session() </ins><span class="cx"> </span><del>- >>> u = User(user_name='Fred') - >>> u.addresses.append(Address(city='New York')) - >>> u.addresses.append(Address(city='Boston')) </del><ins>+ ">>>" # create a new object, with a list-based attribute + ">>>" # containing two more new objects + ">>>" u = User(user_name='Fred') + ">>>" u.addresses.append(Address(city='New York')) + ">>>" u.addresses.append(Address(city='Boston')) </ins><span class="cx"> </span><del>- >>> session.new - [<__main__.User object at 0x713630>, <__main__.Address object at 0x713a70>, <__main__.Address object at 0x713b30>] </del><ins>+ ">>>" # objects are in the "new" list + ">>>" session.new + [<__main__.User object at 0x713630>, + <__main__.Address object at 0x713a70>, + <__main__.Address object at 0x713b30>] </ins><span class="cx"> </span><del>- >>> # view the "modified lists" member, reveals our two Address objects as well - >>> session.modified_lists </del><ins>+ ">>>" # view the "modified lists" member, + ">>>" # reveals our two Address objects as well, inside of a list + ">>>" session.modified_lists </ins><span class="cx"> [[<__main__.Address object at 0x713a70>, <__main__.Address object at 0x713b30>]] </span><span class="cx"> </span><del>- >>> # lets view what the class/ID is for the list objects - >>> ["%s %s" % (l.__class__, id(l)) for l in session.modified_lists] </del><ins>+ ">>>" # lets view what the class/ID is for the list object + ">>>" ["%s %s" % (l.__class__, id(l)) for l in session.modified_lists] </ins><span class="cx"> ['sqlalchemy.mapping.unitofwork.UOWListElement 7391872'] </span><span class="cx"> </span><del>- >>> # now commit - >>> session.commit() </del><ins>+ ">>>" # now commit + ">>>" session.commit() </ins><span class="cx"> </span><del>- >>> # new list is blank - >>> session.new </del><ins>+ ">>>" # the "new" list is now empty + ">>>" session.new </ins><span class="cx"> [] </span><del>- >>> # modified lists is blank - >>> session.modified_lists </del><ins>+ + ">>>" # the "modified lists" list is now empty + ">>>" session.modified_lists </ins><span class="cx"> [] </span><span class="cx"> </span><del>- >>> # now lets modify an object - >>> u.user_name='Ed' </del><ins>+ ">>>" # now lets modify an object + ">>>" u.user_name='Ed' </ins><span class="cx"> </span><del>- >>> # it gets placed in "dirty" - >>> session.dirty </del><ins>+ ">>>" # it gets placed in the "dirty" list + ">>>" session.dirty </ins><span class="cx"> [<__main__.User object at 0x713630>] </span><span class="cx"> </span><del>- >>> # delete one of the addresses - >>> session.delete(u.addresses[0]) - >>> # and also delete it off the User object, note that this is not automatic - >>> del u.addresses[0] - >>> session.deleted </del><ins>+ ">>>" # delete one of the addresses + ">>>" session.delete(u.addresses[0]) + + ">>>" # and also delete it off the User object, note that + ">>>" # this is *not automatic* when using session.delete() + ">>>" del u.addresses[0] + ">>>" session.deleted </ins><span class="cx"> [<__main__.Address object at 0x713a70>] </span><span class="cx"> </span><del>- >>> # commit - >>> session.commit() </del><ins>+ ">>>" # commit + ">>>" session.commit() </ins><span class="cx"> </span><del>- >>> # all lists are cleared out - >>> session.new, session.dirty, session.modified_lists, session.deleted </del><ins>+ ">>>" # all lists are cleared out + ">>>" session.new, session.dirty, session.modified_lists, session.deleted </ins><span class="cx"> ([], [], [], []) </span><span class="cx"> </span><del>- >>> #identity map has the User and the one remaining Address - >>> session.identity_map.values() </del><ins>+ ">>>" # identity map has the User and the one remaining Address + ">>>" session.identity_map.values() </ins><span class="cx"> [<__main__.Address object at 0x713b30>, <__main__.User object at 0x713630>] </span><span class="cx"> </&> </span><ins>+ <p>Unlike the identity map, the <code>new</code>, <code>dirty</code>, <code>modified_lists</code>, and <code>deleted</code> lists are <b>not weak referencing.</b> This means if you abandon all references to new or modified objects within a session, <b>they are still present</b> and will be saved on the next commit operation, unless they are removed from the Session explicitly (more on that later). The <code>new</code> list may change in a future release to be weak-referencing, however for the <code>deleted</code> 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.</p> </ins><span class="cx"> </&> </span><span class="cx"> </span><span class="cx"> <&|doclib.myt:item, name="commit", description="Commit" &> </span><del>- <p>This is the main gateway to what the Unit of Work does best, which is save everything ! </del><ins>+ <p>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: </ins><span class="cx"> </p> </span><ins>+ <&|formatting.myt:code&> + objectstore.get_session().commit() </ins><span class="cx"> </&> </span><ins>+ <p>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 <code>private</code> relationships for a delete operation:</p> + <&|formatting.myt:code&> + # saves only user1 and address2. all other modified + # objects remain present in the session. + objectstore.get_session().commit(user1, address2) + </&> + <p>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.</p> + + <&|doclib.myt:item, name="whatis", description="What Commit is, and Isn't" &> + <p>The purpose of the Commit operation is to instruct the Unit of Work to analyze its lists of modified objects, assemble them into a dependency graph, and fire off the appopriate INSERT, UPDATE, and DELETE statements via the mappers related to those objects. <b>And thats it.</b> This means, it is not going to change anything about your objects as they exist in memory, with the exception of populating scalar object attributes with newly generated default column values which normally only involves primary and foreign key identifiers. A brief list of what will <b>not</b> happen includes:</p> + <ul> + <li>It will not append or delete any items from any list-based attributes. Any objects that have been inserted or deleted will be updated as such in the database, but if a deleted object instance is still attached to a parent object, it will remain.</li> + <li>It will not set or remove any scalar object attributes, even if the database identifier columns have been committed. This means, if you set <code>user.address_id</code> to 5, that will be saved, but it will not place an <code>Address</code> object on the <code>user</code> object. Similarly, if the <code>Address</code> object is deleted but is still attached to the <code>User</code>, it will remain.</li> + </ul> + <P>So the primary guideline for dealing with commit() is, <b>the developer is responsible for maintaining the objects in memory, the unit of work is responsible for maintaining the database representation.</b></p> + + <p>A terrific feature of SQLAlchemy which is also a supreme source of confusion is the backreference feature, described in <&formatting.myt:link, path="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. 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. <b>This feature has nothing to do whatsoever with the Unit of Work.</b> It is strictly a small convenience feature added to support an extremely common pattern. Besides this one little feature, <b>the developer must maintain in-memory object relationships manually</b>. Note that we are talking about the <b>manipulation</b! > of objects, not the initial loading of them which is handled by the mapper.</p> + </&> + </&> </ins><span class="cx"> </span><span class="cx"> <&|doclib.myt:item, name="delete", description="Delete" &> </span><span class="cx"> </&> </span></span></pre></div> <a id="sqlalchemytrunkdocbuildlibhighlightpy"></a> <div class="modfile"><h4>Modified: sqlalchemy/trunk/doc/build/lib/highlight.py (1198 => 1199)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/trunk/doc/build/lib/highlight.py 2006-03-25 17:23:41 UTC (rev 1198) +++ sqlalchemy/trunk/doc/build/lib/highlight.py 2006-03-25 18:13:01 UTC (rev 1199) </span><span class="lines">@@ -176,7 +176,13 @@ </span><span class="cx"> curstyle = self.get_style(t[0], t[1]) </span><span class="cx"> </span><span class="cx"> (start, end) = self._line_grid(line, t[2], t[3]) </span><del>- tokens.append(line[start[1]:end[1]]) </del><ins>+ text = line[start[1]:end[1]] + + # special hardcoded rule to allow "interactive" demos without + # >>> getting sucked in as >> , > operators + if text == '">>>"': + text = '>>>' + tokens.append(text) </ins><span class="cx"> curc = t[3][1] </span><span class="cx"> curl = t[3][0] </span><span class="cx"> </span></span></pre></div> <a id="sqlalchemytrunkdocdocscss"></a> <div class="modfile"><h4>Modified: sqlalchemy/trunk/doc/docs.css (1198 => 1199)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/trunk/doc/docs.css 2006-03-25 17:23:41 UTC (rev 1198) +++ sqlalchemy/trunk/doc/docs.css 2006-03-25 18:13:01 UTC (rev 1199) </span><span class="lines">@@ -96,6 +96,9 @@ </span><span class="cx"> font-size:12px; </span><span class="cx"> } </span><span class="cx"> </span><ins>+pre { + overflow:auto; +} </ins><span class="cx"> .codeline { </span><span class="cx"> font-family:courier, serif; </span><span class="cx"> font-size:12px; </span></span></pre> </div> </div> </body> </html> |
From: <co...@sq...> - 2006-03-25 17:23:52
|
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head><style type="text/css"><!-- #msg dl { border: 1px #006 solid; background: #369; padding: 6px; color: #fff; } #msg dt { float: left; width: 6em; font-weight: bold; } #msg dt:after { content:':';} #msg dl, #msg dt, #msg ul, #msg li { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; } #msg dl a { font-weight: bold} #msg dl a:link { color:#fc3; } #msg dl a:active { color:#ff0; } #msg dl a:visited { color:#cc6; } h3 { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; font-weight: bold; } #msg pre { overflow: auto; background: #ffc; border: 1px #fc0 solid; padding: 6px; } #msg ul, pre { overflow: auto; } #patch { width: 100%; } #patch h4 {font-family: verdana,arial,helvetica,sans-serif;font-size:10pt;padding:8px;background:#369;color:#fff;margin:0;} #patch .propset h4, #patch .binary h4 {margin:0;} #patch pre {padding:0;line-height:1.2em;margin:0;} #patch .diff {width:100%;background:#eee;padding: 0 0 10px 0;overflow:auto;} #patch .propset .diff, #patch .binary .diff {padding:10px 0;} #patch span {display:block;padding:0 10px;} #patch .modfile, #patch .addfile, #patch .delfile, #patch .propset, #patch .binary, #patch .copfile {border:1px solid #ccc;margin:10px 0;} #patch ins {background:#dfd;text-decoration:none;display:block;padding:0 10px;} #patch del {background:#fdd;text-decoration:none;display:block;padding:0 10px;} #patch .lines, .info {color:#888;background:#fff;} --></style> <title>[1198] sqlalchemy/trunk/doc: doc devel</title> </head> <body> <div id="msg"> <dl> <dt>Revision</dt> <dd>1198</dd> <dt>Author</dt> <dd>zzzeek</dd> <dt>Date</dt> <dd>2006-03-25 11:23:41 -0600 (Sat, 25 Mar 2006)</dd> </dl> <h3>Log Message</h3> <pre>doc devel</pre> <h3>Modified Paths</h3> <ul> <li><a href="#sqlalchemytrunkdocbuildcontentdatamappingmyt">sqlalchemy/trunk/doc/build/content/datamapping.myt</a></li> <li><a href="#sqlalchemytrunkdocbuildcontentdocument_basemyt">sqlalchemy/trunk/doc/build/content/document_base.myt</a></li> <li><a href="#sqlalchemytrunkdocbuildcontentmetadatamyt">sqlalchemy/trunk/doc/build/content/metadata.myt</a></li> <li><a href="#sqlalchemytrunkdocbuildcontentunitofworkmyt">sqlalchemy/trunk/doc/build/content/unitofwork.myt</a></li> <li><a href="#sqlalchemytrunkdocdocscss">sqlalchemy/trunk/doc/docs.css</a></li> </ul> </div> <div id="patch"> <h3>Diff</h3> <a id="sqlalchemytrunkdocbuildcontentdatamappingmyt"></a> <div class="modfile"><h4>Modified: sqlalchemy/trunk/doc/build/content/datamapping.myt (1197 => 1198)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/trunk/doc/build/content/datamapping.myt 2006-03-25 15:25:29 UTC (rev 1197) +++ sqlalchemy/trunk/doc/build/content/datamapping.myt 2006-03-25 17:23:41 UTC (rev 1198) </span><span class="lines">@@ -6,9 +6,9 @@ </span><span class="cx"> </span><span class="cx"> The Mapper's role is to perform SQL operations upon the database, associating individual table rows with instances of those classes, and individual database columns with properties upon those instances, to transparently associate in-memory objects with a persistent database representation. </p> </span><span class="cx"> </span><del>-<p>When a Mapper is created to associate a Table object with a class, all of the columns defined in the Table object are associated with the class via property accessors, which add overriding functionality to the normal process of setting and getting object attributes. These property accessors also keep track of changes to object attributes; these changes will be stored to the database when the application "commits" the current transactional context (known as a <b>Unit of Work</b>). The <span class="codeline">__init__()</span> method of the object is also decorated to communicate changes when new instances of the object are created.</p> </del><ins>+<p>When a Mapper is created to associate a Table object with a class, all of the columns defined in the Table object are associated with the class via property accessors, which add overriding functionality to the normal process of setting and getting object attributes. These property accessors keep track of changes to object attributes; these changes will be stored to the database when the application "commits" the current transactional context (known as a <b>Unit of Work</b>). The <code>__init__()</code> method of the object is also decorated to communicate changes when new instances of the object are created.</p> </ins><span class="cx"> </span><del>-<p>The Mapper also provides the interface by which instances of the object are loaded from the database. The primary method for this is its <span class="codeline">select()</span> method, which has similar arguments to a <span class="codeline">sqlalchemy.sql.Select</span> object. But this select method executes automatically and returns results, instead of awaiting an execute() call. Instead of returning a cursor-like object, it returns an array of objects.</p> </del><ins>+<p>The Mapper also provides the interface by which instances of the object are loaded from the database. The primary method for this is its <code>select()</code> method, which has similar arguments to a <code>sqlalchemy.sql.Select</code> object. But this select method executes automatically and returns results, instead of awaiting an execute() call. Instead of returning a cursor-like object, it returns an array of objects.</p> </ins><span class="cx"> </span><span class="cx"> <p>The three elements to be defined, i.e. the Table metadata, the user-defined class, and the Mapper, are typically defined as module-level variables, and may be defined in any fashion suitable to the application, with the only requirement being that the class and table metadata are described before the mapper. For the sake of example, we will be defining these elements close together, but this should not be construed as a requirement; since SQLAlchemy is not a framework, those decisions are left to the developer or an external framework. </span><span class="cx"> </p> </span><span class="lines">@@ -65,15 +65,26 @@ </span><span class="cx"> </span><span class="cx"> userlist = User.mapper.select_by(user_id=12) </span><span class="cx"> </&> </span><del>- <p>There is also a full-blown "monkeypatch" function that creates a primary mapper, attaches the above mapper class property, and also the methods <span class="codeline">get, get_by, select, select_by, selectone, selectfirst, commit</span> and <span class="codeline">delete</span>:</p> </del><ins>+ <p>There is also a full-blown "monkeypatch" function that creates a primary mapper, attaches the above mapper class property, and also the methods <code>get, get_by, select, select_by, selectone, selectfirst, commit</code> and <code>delete</code>:</p> </ins><span class="cx"> <&|formatting.myt:code&> </span><ins>+ # "assign" a mapper to the User class/users table </ins><span class="cx"> assign_mapper(User, users) </span><ins>+ + # methods are attached to the class for selecting </ins><span class="cx"> userlist = User.select_by(user_id=12) </span><ins>+ + myuser = User.get(1) + + # mark an object as deleted for the next commit + myuser.delete() + + # commit the changes on a specific object + myotheruser.commit() </ins><span class="cx"> </&> </span><span class="cx"> <p>Other methods of associating mappers and finder methods with their corresponding classes, such as via common base classes or mixins, can be devised as well. SQLAlchemy does not aim to dictate application architecture and will always allow the broadest variety of architectural patterns, but may include more helper objects and suggested architectures in the future.</p> </span><span class="cx"> </&> </span><span class="cx"> <&|doclib.myt:item, name="overriding", description="Overriding Properties"&> </span><del>- <p>A common request is the ability to create custom class properties that override the behavior of setting/getting an attribute. Currently, the easiest way to do this in SQLAlchemy is just how its done normally; define your attribute with a different name, such as "_attribute", and use a property to get/set its value. The mapper just needs to be told of the special name:</p> </del><ins>+ <p>A common request is the ability to create custom class properties that override the behavior of setting/getting an attribute. Currently, the easiest way to do this in SQLAlchemy is how it would be done in any Python program; define your attribute with a different name, such as "_attribute", and use a property to get/set its value. The mapper just needs to be told of the special name:</p> </ins><span class="cx"> <&|formatting.myt:code&> </span><span class="cx"> class MyClass(object): </span><span class="cx"> def _set_email(self, email): </span><span class="lines">@@ -125,6 +136,10 @@ </span><span class="cx"> </span><span class="cx"> # using a WHERE criterion to get a scalar </span><span class="cx"> u = mapper.selectfirst(users.c.user_name=='john') </span><ins>+ + # selectone() is a stricter version of selectfirst() which + # will raise an exception if there is not exactly one row + u = mapper.selectone(users.c.user_name=='john') </ins><span class="cx"> </span><span class="cx"> # using a full select object </span><span class="cx"> result = mapper.select(users.select(users.c.user_name=='john')) </span><span class="lines">@@ -135,7 +150,7 @@ </span><span class="cx"> # or using a "text" object </span><span class="cx"> result = mapper.select(text("select * from users where user_name='fred'", engine=engine)) </span><span class="cx"> </&> </span><del>- <p>The last few examples above show the usage of the mapper's table object to provide the columns for a WHERE Clause. These columns are also accessible off of the mapped class directly. When a mapper is assigned to a class, it also attaches a special property accessor <span class="codeline">c</span> to the class itself, which can be used just like the table metadata to access the columns of the table:</p> </del><ins>+ <p>Some of the above examples above illustrate the usage of the mapper's Table object to provide the columns for a WHERE Clause. These columns are also accessible off of the mapped class directly. When a mapper is assigned to a class, it also attaches a special property accessor <code>c</code> to the class itself, which can be used just like the table metadata to access the columns of the table:</p> </ins><span class="cx"> <&|formatting.myt:code&> </span><span class="cx"> User.mapper = mapper(User, users) </span><span class="cx"> </span><span class="lines">@@ -143,9 +158,9 @@ </span><span class="cx"> </&> </span><span class="cx"> </&> </span><span class="cx"> <&|doclib.myt:item, name="saving", description="Saving Objects" &> </span><del>- <p>When objects corresponding to mapped classes are created or manipulated, all changes are logged by a package called <span class="codeline">sqlalchemy.mapping.objectstore</span>. The changes are then written to the database when an application calls <span class="codeline">objectstore.commit()</span>. This pattern is known as a <b>Unit of Work</b>, and has many advantages over saving individual objects or attributes on those objects with individual method invocations. Domain models can be built with far greater complexity with no concern over the order of saves and deletes, excessive database round-trips and write operations, or deadlocking issues. The commit() operation uses a transaction as well, and will also perform "concurrency checking" to insure the proper number of rows were in fact affected (not supported with the current MySQL drivers). Transactional resources are used ef! fectively in all cases; the unit of work handles all the details.</p> - - <p>When a mapper is created, the target class has its mapped properties decorated by specialized property accessors that track changes, and its <span class="codeline">__init__()</span> method is also decorated to mark new objects as "new".</p> </del><ins>+ <p>When objects corresponding to mapped classes are created or manipulated, all changes are logged by a package called <code>sqlalchemy.mapping.objectstore</code>. The changes are then written to the database when an application calls <code>objectstore.commit()</code>. This pattern is known as a <b>Unit of Work</b>, and has many advantages over saving individual objects or attributes on those objects with individual method invocations. Domain models can be built with far greater complexity with no concern over the order of saves and deletes, excessive database round-trips and write operations, or deadlocking issues. The commit() operation uses a transaction as well, and will also perform "concurrency checking" to insure the proper number of rows were in fact affected (not supported with the current MySQL drivers). Transactional resources are used effectively in all cases; the unit of work handles all th! e details.</p> + <p>The Unit of Work is a powerful tool, and has some important concepts that must be understood in order to use it effectively. While this section illustrates rudimentary Unit of Work usage, it is strongly encouraged to consult the <&formatting.myt:link, path="unitofwork"&> section for a full description on all its operations, including session control, deletion, and developmental guidelines.</p> + <p>When a mapper is created, the target class has its mapped properties decorated by specialized property accessors that track changes, and its <code>__init__()</code> method is also decorated to mark new objects as "new".</p> </ins><span class="cx"> <&|formatting.myt:code&> </span><span class="cx"> User.mapper = mapper(User, users) </span><span class="cx"> </span><span class="lines">@@ -209,13 +224,14 @@ </span><span class="cx"> </&> </span><span class="cx"> </span><span class="cx"> <p>Recent versions of SQLAlchemy will only put modified object attributes columns into the UPDATE statements generated upon commit. This is to conserve database traffic and also to successfully interact with a "deferred" attribute, which is a mapped object attribute against the mapper's primary table that isnt loaded until referenced by the application.</p> </span><ins>+ </ins><span class="cx"> </&> </span><span class="cx"> </span><span class="cx"> <&|doclib.myt:item, name="relations", description="Defining and Using Relationships" &> </span><del>-<p>So that covers how to map the columns in a table to an object, how to load objects, create new ones, and save changes. The next step is how to define an object's relationships to other database-persisted objects. This is done via the <span class="codeline">relation</span> function provided by the mapper module. So with our User class, lets also define the User has having one or more mailing addresses. First, the table metadata:</p> </del><ins>+<p>So that covers how to map the columns in a table to an object, how to load objects, create new ones, and save changes. The next step is how to define an object's relationships to other database-persisted objects. This is done via the <code>relation</code> function provided by the mapper module. So with our User class, lets also define the User has having one or more mailing addresses. First, the table metadata:</p> </ins><span class="cx"> <&|formatting.myt:code&> </span><span class="cx"> from sqlalchemy import * </span><del>- engine = create_engine('sqlite', {'filename':'mydb'}) </del><ins>+ engine = create_engine('sqlite://filename=mydb') </ins><span class="cx"> </span><span class="cx"> # define user table </span><span class="cx"> users = Table('users', engine, </span><span class="lines">@@ -250,7 +266,7 @@ </span><span class="cx"> self.state = state </span><span class="cx"> self.zip = zip </span><span class="cx"> </&> </span><del>-<p>And then a Mapper that will define a relationship of the User and the Address classes to each other as well as their table metadata. We will add an additional mapper keyword argument <span class="codeline">properties</span> which is a dictionary relating the name of an object property to a database relationship, in this case a <span class="codeline">relation</span> object against a newly defined mapper for the Address class:</p> </del><ins>+<p>And then a Mapper that will define a relationship of the User and the Address classes to each other as well as their table metadata. We will add an additional mapper keyword argument <code>properties</code> which is a dictionary relating the name of an object property to a database relationship, in this case a <code>relation</code> object against a newly defined mapper for the Address class:</p> </ins><span class="cx"> <&|formatting.myt:code&> </span><span class="cx"> User.mapper = mapper(User, users, properties = { </span><span class="cx"> 'addresses' : relation(mapper(Address, addresses)) </span><span class="lines">@@ -277,7 +293,7 @@ </span><span class="cx"> </&> </span><span class="cx"> <p>A lot just happened there! The Mapper object figured out how to relate rows in the addresses table to the users table, and also upon commit had to determine the proper order in which to insert rows. After the insert, all the User and Address objects have all their new primary and foreign keys populated.</p> </span><span class="cx"> </span><del>-<p>Also notice that when we created a Mapper on the User class which defined an 'addresses' relation, the newly created User instance magically had an "addresses" attribute which behaved like a list. This list is in reality a property accessor function, which returns an instance of <span class="codeline">sqlalchemy.util.HistoryArraySet</span>, which fulfills the full set of Python list accessors, but maintains a <b>unique</b> set of objects (based on their in-memory identity), and also tracks additions and deletions to the list:</p> </del><ins>+<p>Also notice that when we created a Mapper on the User class which defined an 'addresses' relation, the newly created User instance magically had an "addresses" attribute which behaved like a list. This list is in reality a property accessor function, which returns an instance of <code>sqlalchemy.util.HistoryArraySet</code>, which fulfills the full set of Python list accessors, but maintains a <b>unique</b> set of objects (based on their in-memory identity), and also tracks additions and deletions to the list:</p> </ins><span class="cx"> <&|formatting.myt:code&> </span><span class="cx"> del u.addresses[1] </span><span class="cx"> u.addresses.append(Address('27 New Place', 'Houston', 'TX', '34839')) </span><span class="lines">@@ -295,7 +311,7 @@ </span><span class="cx"> </span><span class="cx"> </&> </span><span class="cx"> <&|doclib.myt:item, name="private", description="Useful Feature: Private Relations" &> </span><del>-<p>So our one address that was removed from the list, was updated to have a user_id of <span class="codeline">None</span>, and a new address object was inserted to correspond to the new Address added to the User. But now, theres a mailing address with no user_id floating around in the database of no use to anyone. How can we avoid this ? This is acheived by using the <span class="codeline">private=True</span> parameter of <span class="codeline">relation</span>: </del><ins>+<p>So our one address that was removed from the list, was updated to have a user_id of <code>None</code>, and a new address object was inserted to correspond to the new Address added to the User. But now, theres a mailing address with no user_id floating around in the database of no use to anyone. How can we avoid this ? This is acheived by using the <code>private=True</code> parameter of <code>relation</code>: </ins><span class="cx"> </span><span class="cx"> <&|formatting.myt:code&> </span><span class="cx"> User.mapper = mapper(User, users, properties = { </span><span class="lines">@@ -315,10 +331,10 @@ </span><span class="cx"> </&> </span><span class="cx"> </span><span class="cx"> </&> </span><del>-<p>In this case, with the private flag set, the element that was removed from the addresses list was also removed from the database. By specifying the <span class="codeline">private</span> flag on a relation, it is indicated to the Mapper that these related objects exist only as children of the parent object, otherwise should be deleted.</p> </del><ins>+<p>In this case, with the private flag set, the element that was removed from the addresses list was also removed from the database. By specifying the <code>private</code> flag on a relation, it is indicated to the Mapper that these related objects exist only as children of the parent object, otherwise should be deleted.</p> </ins><span class="cx"> </&> </span><span class="cx"> <&|doclib.myt:item, name="backreferences", description="Useful Feature: Backreferences" &> </span><del>-<p>By creating relations with the <span class="codeline">backref</span> keyword, a bi-directional relationship can be created which will keep both ends of the relationship updated automatically, even without any database queries being executed. Below, the User mapper is created with an "addresses" property, and the corresponding Address mapper receives a "backreference" to the User object via the property name "user": </del><ins>+<p>By creating relations with the <code>backref</code> keyword, a bi-directional relationship can be created which will keep both ends of the relationship updated automatically, even without any database queries being executed. Below, the User mapper is created with an "addresses" property, and the corresponding Address mapper receives a "backreference" to the User object via the property name "user": </ins><span class="cx"> <&|formatting.myt:code&> </span><span class="cx"> Address.mapper = mapper(Address, addresses) </span><span class="cx"> User.mapper = mapper(User, users, properties = { </span><span class="lines">@@ -343,7 +359,7 @@ </span><span class="cx"> True </span><span class="cx"> </&> </span><span class="cx"> </span><del>-<p>The backreference feature also works with many-to-many relationships, which are described later. When creating a backreference, a corresponding property is placed on the child mapper. The default arguments to this property can be overridden using the <span class="codeline">backref()</span> function: </del><ins>+<p>The backreference feature also works with many-to-many relationships, which are described later. When creating a backreference, a corresponding property is placed on the child mapper. The default arguments to this property can be overridden using the <code>backref()</code> function: </ins><span class="cx"> <&|formatting.myt:code&> </span><span class="cx"> Address.mapper = mapper(Address, addresses) </span><span class="cx"> </span><span class="lines">@@ -355,7 +371,7 @@ </span><span class="cx"> </&> </span><span class="cx"> </&> </span><span class="cx"> <&|doclib.myt:item, name="cascade", description="Creating Relationships Automatically with cascade_mappers" &> </span><del>-<p>The mapper package has a helper function <span class="codeline">cascade_mappers()</span> which can simplify the task of linking several mappers together. Given a list of classes and/or mappers, it identifies the foreign key relationships between the given mappers or corresponding class mappers, and creates relation() objects representing those relationships, including a backreference. Attempts to find </del><ins>+<p>The mapper package has a helper function <code>cascade_mappers()</code> which can simplify the task of linking several mappers together. Given a list of classes and/or mappers, it identifies the foreign key relationships between the given mappers or corresponding class mappers, and creates relation() objects representing those relationships, including a backreference. Attempts to find </ins><span class="cx"> the "secondary" table in a many-to-many relationship as well. The names of the relations </span><span class="cx"> are a lowercase version of the related class. In the case of one-to-many or many-to-many, </span><span class="cx"> the name is "pluralized", which currently is based on the English language (i.e. an 's' or </span><span class="lines">@@ -380,7 +396,7 @@ </span><span class="cx"> </span><span class="cx"> </&> </span><span class="cx"> <&|doclib.myt:item, name="lazyload", description="Selecting from Relationships: Lazy Load" &> </span><del>- <P>We've seen how the <span class="codeline">relation</span> specifier affects the saving of an object and its child items, how does it affect selecting them? By default, the relation keyword indicates that the related property should be attached a <b>Lazy Loader</b> when instances of the parent object are loaded from the database; this is just a callable function that when accessed will invoke a second SQL query to load the child objects of the parent.</p> </del><ins>+ <P>We've seen how the <code>relation</code> specifier affects the saving of an object and its child items, how does it affect selecting them? By default, the relation keyword indicates that the related property should be attached a <b>Lazy Loader</b> when instances of the parent object are loaded from the database; this is just a callable function that when accessed will invoke a second SQL query to load the child objects of the parent.</p> </ins><span class="cx"> </span><span class="cx"> <&|formatting.myt:code&> </span><span class="cx"> # define a mapper </span><span class="lines">@@ -411,7 +427,7 @@ </span><span class="cx"> </span><span class="cx"> </&> </span><span class="cx"> <&|doclib.myt:item, name="relselectby", description="Useful Feature: Creating Joins via select_by" &> </span><del>- <p>In mappers that have relationships, the <span class="codeline">select_by</span> method and its cousins include special functionality that can be used to create joins. Just specify a key in the argument list which is not present in the primary mapper's list of properties or columns, but *is* present in the property list of one of its relationships: </del><ins>+ <p>In mappers that have relationships, the <code>select_by</code> method and its cousins include special functionality that can be used to create joins. Just specify a key in the argument list which is not present in the primary mapper's list of properties or columns, but *is* present in the property list of one of its relationships: </ins><span class="cx"> <&|formatting.myt:code&> </span><span class="cx"> <&formatting.myt:poplink&>l = User.mapper.select_by(street='123 Green Street') </span><span class="cx"> <&|formatting.myt:codepopper, link="sql" &>SELECT users.user_id AS users_user_id, </span><span class="lines">@@ -441,7 +457,7 @@ </span><span class="cx"> </&> </span><span class="cx"> <p>This operation will clear out all currently mapped object instances, and subsequent select statements will load fresh copies from the databse.</p> </span><span class="cx"> </span><del>- <p>To operate upon a single object, just use the <span class="codeline">remove</span> function:</p> </del><ins>+ <p>To operate upon a single object, just use the <code>remove</code> function:</p> </ins><span class="cx"> <&|formatting.myt:code&> </span><span class="cx"> # (this function coming soon) </span><span class="cx"> objectstore.remove(myobject) </span><span class="lines">@@ -497,7 +513,7 @@ </span><span class="cx"> <p>The join implied by passing the "street" parameter is converted into an "aliasized" clause by the eager loader, so that it does not conflict with the join used to eager load the child address objects.</p> </span><span class="cx"> </&> </span><span class="cx"> <&|doclib.myt:item, name="options", description="Switching Lazy/Eager, No Load" &> </span><del>- <p>The <span class="codeline">options</span> method of mapper provides an easy way to get alternate forms of a mapper from an original one. The most common use of this feature is to change the "eager/lazy" loading behavior of a particular mapper, via the functions <span class="codeline">eagerload()</span>, <span class="codeline">lazyload()</span> and <span class="codeline">noload()</span>: </del><ins>+ <p>The <code>options</code> method of mapper provides an easy way to get alternate forms of a mapper from an original one. The most common use of this feature is to change the "eager/lazy" loading behavior of a particular mapper, via the functions <code>eagerload()</code>, <code>lazyload()</code> and <code>noload()</code>: </ins><span class="cx"> </p> </span><span class="cx"> <&|formatting.myt:code&> </span><span class="cx"> # user mapper with lazy addresses </span><span class="lines">@@ -527,7 +543,7 @@ </span><span class="cx"> </span><span class="cx"> </span><span class="cx"> <&|doclib.myt:item, name="onetoone", description="One to One/Many to One" &> </span><del>-<p>The above examples focused on the "one-to-many" relationship. To do other forms of relationship is easy, as the <span class="codeline">relation</span> function can usually figure out what you want:</p> </del><ins>+<p>The above examples focused on the "one-to-many" relationship. To do other forms of relationship is easy, as the <code>relation</code> function can usually figure out what you want:</p> </ins><span class="cx"> </span><span class="cx"> <&|formatting.myt:code&> </span><span class="cx"> # a table to store a user's preferences for a site </span><span class="lines">@@ -604,7 +620,7 @@ </span><span class="cx"> </&> </span><span class="cx"> </span><span class="cx"> <&|doclib.myt:item, name="manytomany", description="Many to Many" &> </span><del>-<p>The <span class="codeline">relation</span> function handles a basic many-to-many relationship when you specify the association table:</p> </del><ins>+<p>The <code>relation</code> function handles a basic many-to-many relationship when you specify the association table:</p> </ins><span class="cx"> <&|formatting.myt:code&> </span><span class="cx"> articles = Table('articles', engine, </span><span class="cx"> Column('article_id', Integer, primary_key = True), </span></span></pre></div> <a id="sqlalchemytrunkdocbuildcontentdocument_basemyt"></a> <div class="modfile"><h4>Modified: sqlalchemy/trunk/doc/build/content/document_base.myt (1197 => 1198)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/trunk/doc/build/content/document_base.myt 2006-03-25 15:25:29 UTC (rev 1197) +++ sqlalchemy/trunk/doc/build/content/document_base.myt 2006-03-25 17:23:41 UTC (rev 1198) </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.4' </del><ins>+ version = '0.1.5' </ins><span class="cx"> </%attr> </span><span class="cx"> </span><span class="cx"> <%method title> </span></span></pre></div> <a id="sqlalchemytrunkdocbuildcontentmetadatamyt"></a> <div class="modfile"><h4>Modified: sqlalchemy/trunk/doc/build/content/metadata.myt (1197 => 1198)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/trunk/doc/build/content/metadata.myt 2006-03-25 15:25:29 UTC (rev 1197) +++ sqlalchemy/trunk/doc/build/content/metadata.myt 2006-03-25 17:23:41 UTC (rev 1198) </span><span class="lines">@@ -134,7 +134,7 @@ </span><span class="cx"> <&|doclib.myt:item, name="defaults", description="Column Defaults and OnUpdates" &> </span><span class="cx"> <p>SQLAlchemy includes flexible constructs in which to create default values for columns upon the insertion of rows, as well as upon update. These defaults can take several forms: a constant, a Python callable to be pre-executed before the SQL is executed, a SQL expression or function to be pre-executed before the SQL is executed, a pre-executed Sequence (for databases that support sequences), or a "passive" default, which is a default function triggered by the database itself upon insert, the value of which can then be post-fetched by the engine, provided the row provides a primary key in which to call upon.</p> </span><span class="cx"> <&|doclib.myt:item, name="oninsert", description="Pre-Executed Insert Defaults" &> </span><del>- <p>A basic default is most easily specified by the "default" keyword argument to Column:</p> </del><ins>+ <p>A basic default is most easily specified by the "default" keyword argument to Column. This defines a value, function, or SQL expression that will be pre-executed to produce the new value, before the row is inserted:</p> </ins><span class="cx"> <&|formatting.myt:code&> </span><span class="cx"> # a function to create primary key ids </span><span class="cx"> i = 0 </span><span class="lines">@@ -169,7 +169,7 @@ </span><span class="cx"> </&> </span><span class="cx"> </span><span class="cx"> <&|doclib.myt:item, name="onupdate", description="Pre-Executed OnUpdate Defaults" &> </span><del>- <p>Similar to an on-insert default is an on-update default, which is most easily specified by the "onupdate" keyword to Column, which also can be a constanct, plain Python function or SQL expression:</p> </del><ins>+ <p>Similar to an on-insert default is an on-update default, which is most easily specified by the "onupdate" keyword to Column, which also can be a constant, plain Python function or SQL expression:</p> </ins><span class="cx"> <&|formatting.myt:code&> </span><span class="cx"> t = Table("mytable", db, </span><span class="cx"> Column('id', Integer, primary_key=True), </span><span class="lines">@@ -178,7 +178,7 @@ </span><span class="cx"> Column('last_updated', DateTime, onupdate=func.current_timestamp()), </span><span class="cx"> ) </span><span class="cx"> </&> </span><del>- <p>To use a ColumnDefault explicitly for an on-update, use the "for_update" keyword argument:</p> </del><ins>+ <p>To use an explicit ColumnDefault object to specify an on-update, use the "for_update" keyword argument:</p> </ins><span class="cx"> <&|formatting.myt:code&> </span><span class="cx"> Column('mycolumn', String(30), ColumnDefault(func.get_data(), for_update=True)) </span><span class="cx"> </&> </span><span class="lines">@@ -219,7 +219,7 @@ </span><span class="cx"> primary_key = table.engine.last_inserted_ids() </span><span class="cx"> row = table.select(table.c.id == primary_key[0]) </span><span class="cx"> </&> </span><del>- <p>Tables that are reflected from the database which have default values set on them, will receive those defaults as PassiveDefaults.</p> </del><ins>+ <p>When Tables are reflected from the database using <code>autoload=True</code>, any DEFAULT values set on the columns will be reflected in the Table object as PassiveDefault instances.</p> </ins><span class="cx"> </span><span class="cx"> <&|doclib.myt:item, name="postgres", description="The Catch: Postgres Primary Key Defaults always Pre-Execute" &> </span><span class="cx"> <p>Current Postgres support does not rely upon OID's to determine the identity of a row. This is because the usage of OIDs has been deprecated with Postgres and they are disabled by default for table creates as of PG version 8. Pyscopg2's "cursor.lastrowid" function only returns OIDs. Therefore, when inserting a new row which has passive defaults set on the primary key columns, the default function is <b>still pre-executed</b> since SQLAlchemy would otherwise have no way of retrieving the row just inserted.</p> </span><span class="lines">@@ -242,7 +242,7 @@ </span><span class="cx"> </&> </span><span class="cx"> </&> </span><span class="cx"> <&|doclib.myt:item, name="indexes", description="Defining Indexes" &> </span><del>- <p>Indexes can be defined on table columns, including named indexes, non-unique or unique, multiple column. Indexes are included along with table create and drop statements. They are not used for any kind of run-time constraint checking...SQLAlchemy leaves that job to the expert on constraint checking, the database itself.</p> </del><ins>+ <p>Indexes can be defined on table columns, including named indexes, non-unique or unique, multiple column. Indexes are included along with table create and drop statements. They are not used for any kind of run-time constraint checking; SQLAlchemy leaves that job to the expert on constraint checking, the database itself.</p> </ins><span class="cx"> <&|formatting.myt:code&> </span><span class="cx"> mytable = Table('mytable', engine, </span><span class="cx"> </span></span></pre></div> <a id="sqlalchemytrunkdocbuildcontentunitofworkmyt"></a> <div class="modfile"><h4>Modified: sqlalchemy/trunk/doc/build/content/unitofwork.myt (1197 => 1198)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/trunk/doc/build/content/unitofwork.myt 2006-03-25 15:25:29 UTC (rev 1197) +++ sqlalchemy/trunk/doc/build/content/unitofwork.myt 2006-03-25 17:23:41 UTC (rev 1198) </span><span class="lines">@@ -9,20 +9,136 @@ </span><span class="cx"> <ul> </span><span class="cx"> <li>The ability to monitor scalar and list attributes on object instances, as well as object creates. This is handled via the attributes package.</li> </span><span class="cx"> <li>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 <b>topological sort</b>.</li> </span><del>- <li>The ability to "roll back" the attributes that have changed on an object instance since the last commit() operation. this is also handled by the attributes package.</li> </del><span class="cx"> <li>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.</li> </span><span class="cx"> <li>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.</li> </span><del>- <li>Thread-local operation. the Identity map as well as the Unit of work itself are normally instantiated and accessed in a manner that is local to the current thread. Another concurrently executing thread will therefore have its own Identity Map/Unit of Work, so unless an application explicitly shares objects between threads, the operation of the object relational mapping is automatically threadsafe. Unit of Work objects can also be constructed manually to allow any user-defined scoping.</li> </del><ins>+ <li>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.</li> </ins><span class="cx"> </ul></p> </span><span class="cx"> </&> </span><del>- <&|doclib.myt:item, name="getting", description="Accessing UnitOfWork Instances" &> - <p>The current unit of work is accessed via the Session interface. The Session is available in a thread-local context from the objectstore module as follows:</p> </del><ins>+ <&|doclib.myt:item, name="session", description="The Session Interface" &> + <p>The current unit of work is accessed via a Session object. The Session is available in a thread-local context from the objectstore module as follows:</p> </ins><span class="cx"> <&|formatting.myt:code&> </span><span class="cx"> # get the current thread's session </span><del>- s = objectstore.get_session() </del><ins>+ session = objectstore.get_session() </ins><span class="cx"> </&> </span><del>- <p>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>+ <p>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: </ins><span class="cx"> </p> </span><ins>+ <&|formatting.myt:code&> + # this... + objectstore.get_session().commit() + + # is the same as this: + objectstore.commit() + </&> + + <p>A description of the most important methods and concepts follows.</p> + + <&|doclib.myt:item, name="identitymap", description="Identity Map" &> + <p>The first concept to understand about the Unit of Work is that it is keeping track of all mapped objects which have been loaded from the database, as well as all mapped objects which have been saved to the database in the current session. This means that everytime you issue a <code>select</code> call to a mapper which returns results, all of those objects are now installed within the current Session, mapped to their identity.</p> + + <p>In particular, it is insuring that only <b>one</b> instance of a particular object, corresponding to a particular database identity, exists within the Session at one time. By "database identity" we mean a table or relational concept in the database combined with a particular primary key in that table. The session accomplishes this task using a dictionary known as an <b>Identity Map</b>. When <code>select</code> or <code>get</code> calls on mappers issue queries to the database, they will in nearly all cases go out to the database on each call to fetch results. However, when the mapper <b>instantiates</b> objects corresponding to the result set rows it receives, it will <b>check the current identity map first</b> before instantating a new object, and return <b>the same instance</b> already present in the identiy map if it already exists.</p> + + <p>Example:</p> + <&|formatting.myt:code&> + mymapper = mapper(MyClass, mytable) + + obj1 = mymapper.selectfirst(mytable.c.id==15) + obj2 = mymapper.selectfirst(mytable.c.id==15) + + >>> obj1 is obj2 + True + </&> + <p>The Identity Map is an instance of <code>weakref.WeakValueDictionary</code>, so that when an in-memory object falls out of scope, it will be removed automatically. However, this may not be instant if there are circular references upon the object. The current SA attributes implementation places some circular refs upon objects, although this may change in the future. There are other ways to remove object instances from the current session, as well as to clear the current session entirely, which are described later in this section.</p> + <p>To view the Session's identity map, it is accessible via the <code>identity_map</code> accessor, and is an instance of <code>weakref.WeakValueDictionary</code>:</p> + <&|formatting.myt:code&><% """ + >>> objectstore.get_session().identity_map.values() + [<__main__.User object at 0x712630>, <__main__.Address object at 0x712a70>] + """ %> + </&> + + </&> + + <&|doclib.myt:item, name="changed", description="Whats Changed ?" &> + <p>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 <b>newly created</b> objects, records of all objects whose attributes have been modified, records of all objects that have been marked as deleted, and records of all list-based attributes where additions or deletions have occurred. These lists are used when a <code>commit()</code> call is issued to save all changes. After the commit occurs, these lists are all cleared out.</p> + + <p>These records are all tracked by a collection of <code>Set</code> objects (which are a SQLAlchemy-specific instance called a <code>HashSet</code>) that are also viewable off the Session:</p> + <&|formatting.myt:code&> + # new objects that were just constructed + session.new + + # objects that exist in the database, that were modified + session.dirty + + # objects that have been marked as deleted via session.delete(obj) + session.deleted + + # list-based attributes thave been appended + session.modified_lists + </&> + <p>Heres an interactive example, assuming the <code>User</code> and <code>Address</code> mapper setup first outlined in <&formatting.myt:link, path="datamapping_relations"&>:</p> + <&|formatting.myt:code&> + >>> session = objectstore.get_session() + + >>> u = User(user_name='Fred') + >>> u.addresses.append(Address(city='New York')) + >>> u.addresses.append(Address(city='Boston')) + + >>> session.new + [<__main__.User object at 0x713630>, <__main__.Address object at 0x713a70>, <__main__.Address object at 0x713b30>] + + >>> # view the "modified lists" member, reveals our two Address objects as well + >>> session.modified_lists + [[<__main__.Address object at 0x713a70>, <__main__.Address object at 0x713b30>]] + + >>> # lets view what the class/ID is for the list objects + >>> ["%s %s" % (l.__class__, id(l)) for l in session.modified_lists] + ['sqlalchemy.mapping.unitofwork.UOWListElement 7391872'] + + >>> # now commit + >>> session.commit() + + >>> # new list is blank + >>> session.new + [] + >>> # modified lists is blank + >>> session.modified_lists + [] + + >>> # now lets modify an object + >>> u.user_name='Ed' + + >>> # it gets placed in "dirty" + >>> session.dirty + [<__main__.User object at 0x713630>] + + >>> # delete one of the addresses + >>> session.delete(u.addresses[0]) + >>> # and also delete it off the User object, note that this is not automatic + >>> del u.addresses[0] + >>> session.deleted + [<__main__.Address object at 0x713a70>] + + >>> # commit + >>> session.commit() + + >>> # all lists are cleared out + >>> session.new, session.dirty, session.modified_lists, session.deleted + ([], [], [], []) + + >>> #identity map has the User and the one remaining Address + >>> session.identity_map.values() + [<__main__.Address object at 0x713b30>, <__main__.User object at 0x713630>] + </&> + </&> + + <&|doclib.myt:item, name="commit", description="Commit" &> + <p>This is the main gateway to what the Unit of Work does best, which is save everything ! + </p> + </&> + + <&|doclib.myt:item, name="delete", description="Delete" &> + </&> + + <&|doclib.myt:item, name="clear", description="Clear" &> </ins><span class="cx"> <p>To clear out the current thread's UnitOfWork, which has the effect of discarding the Identity Map and the lists of all objects that have been modified, just issue a clear: </span><span class="cx"> </p> </span><span class="cx"> <&|formatting.myt:code&> </span><span class="lines">@@ -34,6 +150,17 @@ </span><span class="cx"> </&> </span><span class="cx"> <p>This is the easiest way to "start fresh", as in a web application that wants to have a newly loaded graph of objects on each request. Any object instances before the clear operation should be discarded.</p> </span><span class="cx"> </&> </span><ins>+ + <&|doclib.myt:item, name="refreshexpire", description="Refresh / Expire" &> + </&> + + <&|doclib.myt:item, name="expunge", description="Expunge" &> + </&> + + <&|doclib.myt:item, name="import", description="Import Instance" &> + </&> + + </&> </ins><span class="cx"> <&|doclib.myt:item, name="begincommit", description="Begin/Commit" &> </span><span class="cx"> <p>The current thread's UnitOfWork object keeps track of objects that are modified. It maintains the following lists:</p> </span><span class="cx"> <&|formatting.myt:code&> </span><span class="lines">@@ -170,7 +297,6 @@ </span><span class="cx"> <&|doclib.myt:item, name="advscope", description="Advanced UnitOfWork Management"&> </span><span class="cx"> </span><span class="cx"> <&|doclib.myt:item, name="object", description="Per-Object Sessions" &> </span><del>- <p><b>status</b> - 'using' function not yet released</p> </del><span class="cx"> <p>Sessions can be created on an ad-hoc basis and used for individual groups of objects and operations. This has the effect of bypassing the entire "global"/"threadlocal" UnitOfWork system and explicitly using a particular Session:</p> </span><span class="cx"> <&|formatting.myt:code&> </span><span class="cx"> # make a new Session with a global UnitOfWork </span></span></pre></div> <a id="sqlalchemytrunkdocdocscss"></a> <div class="modfile"><h4>Modified: sqlalchemy/trunk/doc/docs.css (1197 => 1198)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/trunk/doc/docs.css 2006-03-25 15:25:29 UTC (rev 1197) +++ sqlalchemy/trunk/doc/docs.css 2006-03-25 17:23:41 UTC (rev 1198) </span><span class="lines">@@ -91,6 +91,11 @@ </span><span class="cx"> padding:5px; </span><span class="cx"> } </span><span class="cx"> </span><ins>+code { + font-family:courier, serif; + font-size:12px; +} + </ins><span class="cx"> .codeline { </span><span class="cx"> font-family:courier, serif; </span><span class="cx"> font-size:12px; </span></span></pre> </div> </div> </body> </html> |
From: <co...@sq...> - 2006-03-25 15:25: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>[1197] sqlalchemy/trunk/lib/sqlalchemy: update to types doc</title> </head> <body> <div id="msg"> <dl> <dt>Revision</dt> <dd>1197</dd> <dt>Author</dt> <dd>zzzeek</dd> <dt>Date</dt> <dd>2006-03-25 09:25:29 -0600 (Sat, 25 Mar 2006)</dd> </dl> <h3>Log Message</h3> <pre>update to types doc commented out 'tutorial' from docuemnt_base until its complete float extends numeric type</pre> <h3>Modified Paths</h3> <ul> <li><a href="#sqlalchemytrunkdocbuildcontentdocument_basemyt">sqlalchemy/trunk/doc/build/content/document_base.myt</a></li> <li><a href="#sqlalchemytrunkdocbuildcontenttypesmyt">sqlalchemy/trunk/doc/build/content/types.myt</a></li> <li><a href="#sqlalchemytrunklibsqlalchemytypespy">sqlalchemy/trunk/lib/sqlalchemy/types.py</a></li> </ul> </div> <div id="patch"> <h3>Diff</h3> <a id="sqlalchemytrunkdocbuildcontentdocument_basemyt"></a> <div class="modfile"><h4>Modified: sqlalchemy/trunk/doc/build/content/document_base.myt (1196 => 1197)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/trunk/doc/build/content/document_base.myt 2006-03-25 02:49:10 UTC (rev 1196) +++ sqlalchemy/trunk/doc/build/content/document_base.myt 2006-03-25 15:25:29 UTC (rev 1197) </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></pre></div> <a id="sqlalchemytrunkdocbuildcontenttypesmyt"></a> <div class="modfile"><h4>Modified: sqlalchemy/trunk/doc/build/content/types.myt (1196 => 1197)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/trunk/doc/build/content/types.myt 2006-03-25 02:49:10 UTC (rev 1196) +++ sqlalchemy/trunk/doc/build/content/types.myt 2006-03-25 15:25:29 UTC (rev 1197) </span><span class="lines">@@ -6,23 +6,28 @@ </span><span class="cx"> </p> </span><span class="cx"> <&|doclib.myt:item, name="standard", description="Built-in Types" &> </span><span class="cx"> </span><del>-<p>SQLAlchemy comes with a set of standard generic datatypes, which are defined as classes. They are specified to table meta data using either the class itself, or an instance of the class. Creating an instance of the class allows you to specify parameters for the type, such as string length, numerical precision, etc. </del><ins>+<p>SQLAlchemy comes with a set of standard generic datatypes, which are defined as classes. </ins><span class="cx"> </p> </span><span class="cx"> <p>The standard set of generic types are:</p> </span><del>-<&|formatting.myt:code&> - # sqlalchemy.types package: </del><ins>+<&|formatting.myt:code, title="package sqlalchemy.types"&> </ins><span class="cx"> class String(TypeEngine): </span><span class="cx"> def __init__(self, length=None) </span><span class="cx"> </span><span class="cx"> class Integer(TypeEngine) </span><del>- </del><ins>+ + class SmallInteger(Integer) + </ins><span class="cx"> class Numeric(TypeEngine): </span><span class="cx"> def __init__(self, precision=10, length=2) </span><span class="cx"> </span><del>- class Float(TypeEngine): </del><ins>+ class Float(Numeric): </ins><span class="cx"> def __init__(self, precision=10) </span><span class="cx"> </span><span class="cx"> class DateTime(TypeEngine) </span><ins>+ + class Date(TypeEngine) + + class Time(TypeEngine) </ins><span class="cx"> </span><span class="cx"> class Binary(TypeEngine): </span><span class="cx"> def __init__(self, length=None) </span><span class="lines">@@ -31,10 +36,11 @@ </span><span class="cx"> </span><span class="cx"> # converts unicode strings to raw bytes </span><span class="cx"> # as bind params, raw bytes to unicode as </span><del>- # rowset values </del><ins>+ # rowset values, using the unicode encoding + # setting on the engine (defaults to 'utf-8') </ins><span class="cx"> class Unicode(String) </span><span class="cx"> </&> </span><del>-<p>More specific subclasses of these types are available, to allow finer grained control over types:</p> </del><ins>+<p>More specific subclasses of these types are available, which various database engines may choose to implement specifically, allowing finer grained control over types:</p> </ins><span class="cx"> <&|formatting.myt:code&> </span><span class="cx"> class FLOAT(Numeric) </span><span class="cx"> class TEXT(String) </span><span class="lines">@@ -50,14 +56,26 @@ </span><span class="cx"> class BOOLEAN(Boolean) </span><span class="cx"> </&> </span><span class="cx"> <p>When using a specific database engine, these types are adapted even further via a set of database-specific subclasses defined by the database engine.</p> </span><ins>+<p>There may eventually be more type objects that are defined for specific databases. An example of this would be Postgres' Array type. +</p> +<p>Type objects are specified to table meta data using either the class itself, or an instance of the class. Creating an instance of the class allows you to specify parameters for the type, such as string length, numerical precision, etc.:</p> +<&|formatting.myt:code&> + mytable = Table('mytable', engine, + # define type using a class + Column('my_id', Integer, primary_key=True), + + # define type using an object instance + Column('value', Number(7,4)) + ) </ins><span class="cx"> </&> </span><ins>+</&> </ins><span class="cx"> </span><span class="cx"> <&|doclib.myt:item, name="custom", description="Creating your Own Types" &> </span><del>-<p>Types also support pre-processing of query parameters as well as post-processing of result set data. You can make your own classes to perform these operations. They are specified by subclassing the desired type class as well as the special mixin TypeDecorator, which manages the adaptation of the underlying type to a database-specific type:</p> -<&|formatting.myt:code&> </del><ins>+<p>User-defined types can be created, to support either database-specific types, or customized pre-processing of query parameters as well as post-processing of result set data. You can make your own classes to perform these operations. They are specified by subclassing the desired type class:</p> +<&|formatting.myt:code, title="Basic Example"&> </ins><span class="cx"> import sqlalchemy.types as types </span><span class="cx"> </span><del>- class MyType(types.TypeDecorator, types.String): </del><ins>+ class MyType(types.String): </ins><span class="cx"> """basic type that decorates String, prefixes values with "PREFIX:" on </span><span class="cx"> the way in and strips it off on the way out.""" </span><span class="cx"> def convert_bind_param(self, value, engine): </span><span class="lines">@@ -65,6 +83,30 @@ </span><span class="cx"> def convert_result_value(self, value, engine): </span><span class="cx"> return value[7:] </span><span class="cx"> </&> </span><ins>+<p>A common desire is for a "pickle" type, which overrides a Binary object to provide pickling behavior: +<&|formatting.myt:code, title="Pickle Type"&> + import cPickle + + class PickleType(types.Binary): + def __init__(self, protocol=cPickle.HIGHEST_PROTOCOL): + """allows the pickle protocol to be specified""" + self.protocol = protocol + def convert_result_value(self, value, engine): + return cpickle.loads(super(PickleType, self).convert_result_value(value, engine), self.protocol) + def convert_bind_param(self, value, engine): + return super(PickleType, self).convert_bind_param(cpickle.dumps(value, self.protocol), engine) + def get_constructor_args(self): + return {'protocol':self.protocol} +</&> +<p>Which can be used like:</p> +<&|formatting.myt:code&> + mytable = Table('mytable', engine, + Column('id', Integer, primary_key=True), + Column('data', PickleType())) + + my_object = MyObject() + mytable.insert().execute(data=my_object) +</&> </ins><span class="cx"> <p>Another example, which illustrates a fully defined datatype. This just overrides the base type class TypeEngine:</p> </span><span class="cx"> <&|formatting.myt:code&> </span><span class="cx"> import sqlalchemy.types as types </span><span class="lines">@@ -78,9 +120,6 @@ </span><span class="cx"> return value </span><span class="cx"> def convert_result_value(self, value, engine): </span><span class="cx"> return value </span><del>- def adapt(self, typeobj): - """produces an adaptation of this object given a type which is a subclass of this object""" - return typeobj(self.precision) </del><span class="cx"> def adapt_args(self): </span><span class="cx"> """allows for the adaptation of this TypeEngine object into a new kind of type depending on its arguments.""" </span><span class="cx"> return self </span></span></pre></div> <a id="sqlalchemytrunklibsqlalchemytypespy"></a> <div class="modfile"><h4>Modified: sqlalchemy/trunk/lib/sqlalchemy/types.py (1196 => 1197)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/trunk/lib/sqlalchemy/types.py 2006-03-25 02:49:10 UTC (rev 1196) +++ sqlalchemy/trunk/lib/sqlalchemy/types.py 2006-03-25 15:25:29 UTC (rev 1197) </span><span class="lines">@@ -117,7 +117,7 @@ </span><span class="cx"> def get_constructor_args(self): </span><span class="cx"> return {'precision':self.precision, 'length':self.length} </span><span class="cx"> </span><del>-class Float(TypeEngine): </del><ins>+class Float(Numeric): </ins><span class="cx"> def __init__(self, precision = 10): </span><span class="cx"> self.precision = precision </span><span class="cx"> def get_constructor_args(self): </span></span></pre> </div> </div> </body> </html> |
From: <co...@sq...> - 2006-03-25 02:49:20
|
<!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>[1196] sqlalchemy/trunk/test: ResultProxy has an iterator interface</title> </head> <body> <div id="msg"> <dl> <dt>Revision</dt> <dd>1196</dd> <dt>Author</dt> <dd>zzzeek</dd> <dt>Date</dt> <dd>2006-03-24 20:49:10 -0600 (Fri, 24 Mar 2006)</dd> </dl> <h3>Log Message</h3> <pre>ResultProxy has an iterator interface</pre> <h3>Modified Paths</h3> <ul> <li><a href="#sqlalchemytrunklibsqlalchemyenginepy">sqlalchemy/trunk/lib/sqlalchemy/engine.py</a></li> <li><a href="#sqlalchemytrunktestquerypy">sqlalchemy/trunk/test/query.py</a></li> </ul> </div> <div id="patch"> <h3>Diff</h3> <a id="sqlalchemytrunklibsqlalchemyenginepy"></a> <div class="modfile"><h4>Modified: sqlalchemy/trunk/lib/sqlalchemy/engine.py (1195 => 1196)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/trunk/lib/sqlalchemy/engine.py 2006-03-25 02:25:59 UTC (rev 1195) +++ sqlalchemy/trunk/lib/sqlalchemy/engine.py 2006-03-25 02:49:10 UTC (rev 1196) </span><span class="lines">@@ -807,6 +807,14 @@ </span><span class="cx"> else: </span><span class="cx"> rec = self.props[key] </span><span class="cx"> return rec[0].convert_result_value(row[rec[1]], self.engine) </span><ins>+ + def __iter__(self): + while True: + row = self.fetchone() + if row is None: + raise StopIteration + else: + yield row </ins><span class="cx"> </span><span class="cx"> def fetchall(self): </span><span class="cx"> """fetches all rows, just like DBAPI cursor.fetchall().""" </span></span></pre></div> <a id="sqlalchemytrunktestquerypy"></a> <div class="modfile"><h4>Modified: sqlalchemy/trunk/test/query.py (1195 => 1196)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/trunk/test/query.py 2006-03-25 02:25:59 UTC (rev 1195) +++ sqlalchemy/trunk/test/query.py 2006-03-25 02:49:10 UTC (rev 1196) </span><span class="lines">@@ -4,6 +4,7 @@ </span><span class="cx"> </span><span class="cx"> import sqlalchemy.databases.sqlite as sqllite </span><span class="cx"> </span><ins>+import tables </ins><span class="cx"> db = testbase.db </span><span class="cx"> #db.echo='debug' </span><span class="cx"> from sqlalchemy import * </span><span class="lines">@@ -41,6 +42,16 @@ </span><span class="cx"> self.users.update(self.users.c.user_id == 7).execute(user_name = 'fred') </span><span class="cx"> print repr(self.users.select().execute().fetchall()) </span><span class="cx"> </span><ins>+ def testrowiteration(self): + self.users.insert().execute(user_id = 7, user_name = 'jack') + self.users.insert().execute(user_id = 8, user_name = 'ed') + self.users.insert().execute(user_id = 9, user_name = 'fred') + r = self.users.select().execute() + l = [] + for row in r: + l.append(row) + self.assert_(len(l) == 3) + </ins><span class="cx"> def testpassiveoverride(self): </span><span class="cx"> """primarily for postgres, tests that when we get a primary key column back </span><span class="cx"> from reflecting a table which has a default value on it, we pre-execute </span></span></pre> </div> </div> </body> </html> |
From: <co...@sq...> - 2006-03-25 02:26:13
|
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head><style type="text/css"><!-- #msg dl { border: 1px #006 solid; background: #369; padding: 6px; color: #fff; } #msg dt { float: left; width: 6em; font-weight: bold; } #msg dt:after { content:':';} #msg dl, #msg dt, #msg ul, #msg li { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; } #msg dl a { font-weight: bold} #msg dl a:link { color:#fc3; } #msg dl a:active { color:#ff0; } #msg dl a:visited { color:#cc6; } h3 { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; font-weight: bold; } #msg pre { overflow: auto; background: #ffc; border: 1px #fc0 solid; padding: 6px; } #msg ul, pre { overflow: auto; } #patch { width: 100%; } #patch h4 {font-family: verdana,arial,helvetica,sans-serif;font-size:10pt;padding:8px;background:#369;color:#fff;margin:0;} #patch .propset h4, #patch .binary h4 {margin:0;} #patch pre {padding:0;line-height:1.2em;margin:0;} #patch .diff {width:100%;background:#eee;padding: 0 0 10px 0;overflow:auto;} #patch .propset .diff, #patch .binary .diff {padding:10px 0;} #patch span {display:block;padding:0 10px;} #patch .modfile, #patch .addfile, #patch .delfile, #patch .propset, #patch .binary, #patch .copfile {border:1px solid #ccc;margin:10px 0;} #patch ins {background:#dfd;text-decoration:none;display:block;padding:0 10px;} #patch del {background:#fdd;text-decoration:none;display:block;padding:0 10px;} #patch .lines, .info {color:#888;background:#fff;} --></style> <title>[1195] sqlalchemy/trunk/examples/polymorph: got some support for mapping to a select that only selects some of the columns of an underlying table</title> </head> <body> <div id="msg"> <dl> <dt>Revision</dt> <dd>1195</dd> <dt>Author</dt> <dd>zzzeek</dd> <dt>Date</dt> <dd>2006-03-24 20:25:59 -0600 (Fri, 24 Mar 2006)</dd> </dl> <h3>Log Message</h3> <pre>got some support for mapping to a select that only selects some of the columns of an underlying table</pre> <h3>Modified Paths</h3> <ul> <li><a href="#sqlalchemytrunkexamplespolymorphpolymorph2py">sqlalchemy/trunk/examples/polymorph/polymorph2.py</a></li> <li><a href="#sqlalchemytrunklibsqlalchemymappingmapperpy">sqlalchemy/trunk/lib/sqlalchemy/mapping/mapper.py</a></li> <li><a href="#sqlalchemytrunklibsqlalchemymappingunitofworkpy">sqlalchemy/trunk/lib/sqlalchemy/mapping/unitofwork.py</a></li> </ul> </div> <div id="patch"> <h3>Diff</h3> <a id="sqlalchemytrunkexamplespolymorphpolymorph2py"></a> <div class="modfile"><h4>Modified: sqlalchemy/trunk/examples/polymorph/polymorph2.py (1194 => 1195)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/trunk/examples/polymorph/polymorph2.py 2006-03-25 01:58:27 UTC (rev 1194) +++ sqlalchemy/trunk/examples/polymorph/polymorph2.py 2006-03-25 02:25:59 UTC (rev 1195) </span><span class="lines">@@ -94,8 +94,6 @@ </span><span class="cx"> return False </span><span class="cx"> else: </span><span class="cx"> return True </span><del>- - </del><span class="cx"> </span><span class="cx"> people_mapper = mapper(Person, person_join, extension=PersonLoader()) </span><span class="cx"> </span></span></pre></div> <a id="sqlalchemytrunklibsqlalchemymappingmapperpy"></a> <div class="modfile"><h4>Modified: sqlalchemy/trunk/lib/sqlalchemy/mapping/mapper.py (1194 => 1195)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/trunk/lib/sqlalchemy/mapping/mapper.py 2006-03-25 01:58:27 UTC (rev 1194) +++ sqlalchemy/trunk/lib/sqlalchemy/mapping/mapper.py 2006-03-25 02:25:59 UTC (rev 1195) </span><span class="lines">@@ -18,6 +18,11 @@ </span><span class="cx"> # a dictionary mapping classes to their primary mappers </span><span class="cx"> mapper_registry = weakref.WeakKeyDictionary() </span><span class="cx"> </span><ins>+# a constant returned by _getattrbycolumn to indicate +# this mapper is not handling an attribute for a particular +# column +NO_ATTRIBUTE = object() + </ins><span class="cx"> class Mapper(object): </span><span class="cx"> """Persists object instances to and from schema.Table objects via the sql package. </span><span class="cx"> Instances of this class should be constructed through this package's mapper() or </span><span class="lines">@@ -534,19 +539,25 @@ </span><span class="cx"> params = {} </span><span class="cx"> return self.instances(statement.execute(**params), **kwargs) </span><span class="cx"> </span><del>- def _getpropbycolumn(self, column): </del><ins>+ def _getpropbycolumn(self, column, raiseerror=True): </ins><span class="cx"> try: </span><span class="cx"> prop = self.columntoproperty[column.original] </span><span class="cx"> except KeyError: </span><span class="cx"> try: </span><span class="cx"> prop = self.props[column.key] </span><ins>+ if not raiseerror: + return None </ins><span class="cx"> raise InvalidRequestError("Column '%s.%s' is not available, due to conflicting property '%s':%s" % (column.table.name, column.name, column.key, repr(prop))) </span><span class="cx"> except KeyError: </span><ins>+ if not raiseerror: + return None </ins><span class="cx"> raise InvalidRequestError("No column %s.%s is configured on mapper %s..." % (column.table.name, column.name, str(self))) </span><span class="cx"> return prop[0] </span><span class="cx"> </span><del>- def _getattrbycolumn(self, obj, column): - prop = self._getpropbycolumn(column) </del><ins>+ def _getattrbycolumn(self, obj, column, raiseerror=True): + prop = self._getpropbycolumn(column, raiseerror) + if prop is None: + return NO_ATTRIBUTE </ins><span class="cx"> return prop.getattr(obj) </span><span class="cx"> </span><span class="cx"> def _setattrbycolumn(self, obj, column, value): </span><span class="lines">@@ -615,7 +626,9 @@ </span><span class="cx"> # doing an UPDATE ? get the history for the attribute, with "passive" </span><span class="cx"> # so as not to trigger any deferred loads. if there is a new </span><span class="cx"> # value, add it to the bind parameters </span><del>- prop = self._getpropbycolumn(col) </del><ins>+ prop = self._getpropbycolumn(col, False) + if prop is None: + continue </ins><span class="cx"> history = prop.get_history(obj, passive=True) </span><span class="cx"> if history: </span><span class="cx"> a = history.added_items() </span><span class="lines">@@ -629,7 +642,9 @@ </span><span class="cx"> # default. if its None and theres no default, we still might </span><span class="cx"> # not want to put it in the col list but SQLIte doesnt seem to like that </span><span class="cx"> # if theres no columns at all </span><del>- value = self._getattrbycolumn(obj, col) </del><ins>+ value = self._getattrbycolumn(obj, col, False) + if value is NO_ATTRIBUTE: + continue </ins><span class="cx"> if col.default is None or value is not None: </span><span class="cx"> params[col.key] = value </span><span class="cx"> </span><span class="lines">@@ -682,13 +697,16 @@ </span><span class="cx"> clause.clauses.append(p == self._getattrbycolumn(obj, p)) </span><span class="cx"> row = table.select(clause).execute().fetchone() </span><span class="cx"> for c in table.c: </span><del>- if self._getattrbycolumn(obj, c) is None: </del><ins>+ if self._getattrbycolumn(obj, c, False) is None: </ins><span class="cx"> self._setattrbycolumn(obj, c, row[c]) </span><span class="cx"> else: </span><span class="cx"> for c in table.c: </span><span class="cx"> if c.primary_key or not params.has_key(c.name): </span><span class="cx"> continue </span><del>- if self._getattrbycolumn(obj, c) != params.get_original(c.name): </del><ins>+ v = self._getattrbycolumn(obj, c, False) + if v is NO_ATTRIBUTE: + continue + elif v != params.get_original(c.name): </ins><span class="cx"> self._setattrbycolumn(obj, c, params.get_original(c.name)) </span><span class="cx"> </span><span class="cx"> def delete_obj(self, objects, uow): </span></span></pre></div> <a id="sqlalchemytrunklibsqlalchemymappingunitofworkpy"></a> <div class="modfile"><h4>Modified: sqlalchemy/trunk/lib/sqlalchemy/mapping/unitofwork.py (1194 => 1195)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/trunk/lib/sqlalchemy/mapping/unitofwork.py 2006-03-25 01:58:27 UTC (rev 1194) +++ sqlalchemy/trunk/lib/sqlalchemy/mapping/unitofwork.py 2006-03-25 02:25:59 UTC (rev 1195) </span><span class="lines">@@ -737,7 +737,7 @@ </span><span class="cx"> def _repr_task(task): </span><span class="cx"> if task.mapper is not None: </span><span class="cx"> if task.mapper.__class__.__name__ == 'Mapper': </span><del>- name = task.mapper.class_.__name__ + "/" + task.mapper.primarytable.id + "/" + str(id(task.mapper)) </del><ins>+ name = task.mapper.class_.__name__ + "/" + str(task.mapper.primarytable) + "/" + str(id(task.mapper)) </ins><span class="cx"> else: </span><span class="cx"> name = repr(task.mapper) </span><span class="cx"> else: </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>[1194] sqlalchemy/trunk/lib/sqlalchemy/mapping/objectstore.py: added check in SessionTrans.begin() that the underlying unit of work is still the current uow</title> </head> <body> <div id="msg"> <dl> <dt>Revision</dt> <dd>1194</dd> <dt>Author</dt> <dd>zzzeek</dd> <dt>Date</dt> <dd>2006-03-24 19:58:27 -0600 (Fri, 24 Mar 2006)</dd> </dl> <h3>Log Message</h3> <pre>added check in SessionTrans.begin() that the underlying unit of work is still the current uow</pre> <h3>Modified Paths</h3> <ul> <li><a href="#sqlalchemytrunklibsqlalchemymappingobjectstorepy">sqlalchemy/trunk/lib/sqlalchemy/mapping/objectstore.py</a></li> </ul> </div> <div id="patch"> <h3>Diff</h3> <a id="sqlalchemytrunklibsqlalchemymappingobjectstorepy"></a> <div class="modfile"><h4>Modified: sqlalchemy/trunk/lib/sqlalchemy/mapping/objectstore.py (1193 => 1194)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/trunk/lib/sqlalchemy/mapping/objectstore.py 2006-03-25 00:17:51 UTC (rev 1193) +++ sqlalchemy/trunk/lib/sqlalchemy/mapping/objectstore.py 2006-03-25 01:58:27 UTC (rev 1194) </span><span class="lines">@@ -98,6 +98,8 @@ </span><span class="cx"> uow = property(lambda s:s.__uow, doc="returns the parent UnitOfWork corresponding to this transaction.") </span><span class="cx"> def begin(self): </span><span class="cx"> """calls begin() on the underlying Session object, returning a new no-op SessionTrans object.""" </span><ins>+ if self.parent.uow is not self.uow: + raise InvalidRequestError("This SessionTrans is no longer valid") </ins><span class="cx"> return self.parent.begin() </span><span class="cx"> def commit(self): </span><span class="cx"> """commits the transaction noted by this SessionTrans object.""" </span></span></pre> </div> </div> </body> </html> |
From: <co...@sq...> - 2006-03-25 00:18:05
|
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head><style type="text/css"><!-- #msg dl { border: 1px #006 solid; background: #369; padding: 6px; color: #fff; } #msg dt { float: left; width: 6em; font-weight: bold; } #msg dt:after { content:':';} #msg dl, #msg dt, #msg ul, #msg li { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; } #msg dl a { font-weight: bold} #msg dl a:link { color:#fc3; } #msg dl a:active { color:#ff0; } #msg dl a:visited { color:#cc6; } h3 { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; font-weight: bold; } #msg pre { overflow: auto; background: #ffc; border: 1px #fc0 solid; padding: 6px; } #msg ul, pre { overflow: auto; } #patch { width: 100%; } #patch h4 {font-family: verdana,arial,helvetica,sans-serif;font-size:10pt;padding:8px;background:#369;color:#fff;margin:0;} #patch .propset h4, #patch .binary h4 {margin:0;} #patch pre {padding:0;line-height:1.2em;margin:0;} #patch .diff {width:100%;background:#eee;padding: 0 0 10px 0;overflow:auto;} #patch .propset .diff, #patch .binary .diff {padding:10px 0;} #patch span {display:block;padding:0 10px;} #patch .modfile, #patch .addfile, #patch .delfile, #patch .propset, #patch .binary, #patch .copfile {border:1px solid #ccc;margin:10px 0;} #patch ins {background:#dfd;text-decoration:none;display:block;padding:0 10px;} #patch del {background:#fdd;text-decoration:none;display:block;padding:0 10px;} #patch .lines, .info {color:#888;background:#fff;} --></style> <title>[1193] sqlalchemy/trunk/test: added expunge() method to objectstore</title> </head> <body> <div id="msg"> <dl> <dt>Revision</dt> <dd>1193</dd> <dt>Author</dt> <dd>zzzeek</dd> <dt>Date</dt> <dd>2006-03-24 18:17:51 -0600 (Fri, 24 Mar 2006)</dd> </dl> <h3>Log Message</h3> <pre>added expunge() method to objectstore correction in attributes reset_history to really reset in all cases added unit tests testing refresh()/expire() bug that was fixed by reset_history 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="#sqlalchemytrunklibsqlalchemymappingunitofworkpy">sqlalchemy/trunk/lib/sqlalchemy/mapping/unitofwork.py</a></li> <li><a href="#sqlalchemytrunktestmapperpy">sqlalchemy/trunk/test/mapper.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 (1192 => 1193)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/trunk/lib/sqlalchemy/attributes.py 2006-03-24 23:06:07 UTC (rev 1192) +++ sqlalchemy/trunk/lib/sqlalchemy/attributes.py 2006-03-25 00:17:51 UTC (rev 1193) </span><span class="lines">@@ -397,7 +397,10 @@ </span><span class="cx"> x.clear() </span><span class="cx"> del self.attribute_history(obj)[key] </span><span class="cx"> except KeyError: </span><del>- pass </del><ins>+ try: + del obj.__dict__[key] + except KeyError: + pass </ins><span class="cx"> </span><span class="cx"> def class_managed(self, class_): </span><span class="cx"> """returns a dictionary of "history container definitions", which is attached to a </span></span></pre></div> <a id="sqlalchemytrunklibsqlalchemymappingobjectstorepy"></a> <div class="modfile"><h4>Modified: sqlalchemy/trunk/lib/sqlalchemy/mapping/objectstore.py (1192 => 1193)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/trunk/lib/sqlalchemy/mapping/objectstore.py 2006-03-24 23:06:07 UTC (rev 1192) +++ sqlalchemy/trunk/lib/sqlalchemy/mapping/objectstore.py 2006-03-25 00:17:51 UTC (rev 1193) </span><span class="lines">@@ -166,6 +166,10 @@ </span><span class="cx"> for o in obj: </span><span class="cx"> global_attributes.trigger_history(o, lambda: refresh(o)) </span><span class="cx"> </span><ins>+ def expunge(self, *obj): + for o in obj: + self.uow.expunge(obj) + </ins><span class="cx"> def register_clean(self, obj): </span><span class="cx"> self._bind_to(obj) </span><span class="cx"> self.uow.register_clean(obj) </span><span class="lines">@@ -252,7 +256,10 @@ </span><span class="cx"> """invalidates the data in the given objects and sets them to refresh themselves </span><span class="cx"> the next time they are requested.""" </span><span class="cx"> get_session().expire(*obj) </span><del>- </del><ins>+ +def expunge(*obj): + get_session().expunge(*obj) + </ins><span class="cx"> def delete(*obj): </span><span class="cx"> """registers the given objects as to be deleted upon the next commit""" </span><span class="cx"> s = get_session().delete(*obj) </span></span></pre></div> <a id="sqlalchemytrunklibsqlalchemymappingunitofworkpy"></a> <div class="modfile"><h4>Modified: sqlalchemy/trunk/lib/sqlalchemy/mapping/unitofwork.py (1192 => 1193)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/trunk/lib/sqlalchemy/mapping/unitofwork.py 2006-03-24 23:06:07 UTC (rev 1192) +++ sqlalchemy/trunk/lib/sqlalchemy/mapping/unitofwork.py 2006-03-25 00:17:51 UTC (rev 1193) </span><span class="lines">@@ -104,6 +104,11 @@ </span><span class="cx"> """returns True if the given key is present in this UnitOfWork's identity map.""" </span><span class="cx"> return self.identity_map.has_key(key) </span><span class="cx"> </span><ins>+ def expunge(self, obj): + """removes this object completely from the UnitOfWork, including the identity map, + and the "new", "dirty" and "deleted" lists.""" + self._remove_deleted(obj) + </ins><span class="cx"> def _remove_deleted(self, obj): </span><span class="cx"> if hasattr(obj, "_instance_key"): </span><span class="cx"> del self.identity_map[obj._instance_key] </span><span class="lines">@@ -119,7 +124,7 @@ </span><span class="cx"> del self.new[obj] </span><span class="cx"> except KeyError: </span><span class="cx"> pass </span><del>- self.attributes.commit(obj) </del><ins>+ #self.attributes.commit(obj) </ins><span class="cx"> self.attributes.remove(obj) </span><span class="cx"> </span><span class="cx"> def _validate_obj(self, obj): </span></span></pre></div> <a id="sqlalchemytrunktestmapperpy"></a> <div class="modfile"><h4>Modified: sqlalchemy/trunk/test/mapper.py (1192 => 1193)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/trunk/test/mapper.py 2006-03-24 23:06:07 UTC (rev 1192) +++ sqlalchemy/trunk/test/mapper.py 2006-03-25 00:17:51 UTC (rev 1193) </span><span class="lines">@@ -85,9 +85,14 @@ </span><span class="cx"> self.assert_(u is not u2) </span><span class="cx"> </span><span class="cx"> def testrefresh(self): </span><del>- m = mapper(User, users) </del><ins>+ m = mapper(User, users, properties={'addresses':relation(mapper(Address, addresses))}) </ins><span class="cx"> u = m.get(7) </span><span class="cx"> u.user_name = 'foo' </span><ins>+ a = Address() + u.addresses.append(a) + + self.assert_(a in u.addresses) + </ins><span class="cx"> objectstore.refresh(u) </span><span class="cx"> </span><span class="cx"> # its refreshed, so not dirty </span><span class="lines">@@ -96,16 +101,21 @@ </span><span class="cx"> # username is back to the DB </span><span class="cx"> self.assert_(u.user_name == 'jack') </span><span class="cx"> </span><ins>+ self.assert_(a not in u.addresses) + </ins><span class="cx"> u.user_name = 'foo' </span><ins>+ u.addresses.append(a) </ins><span class="cx"> # now its dirty </span><span class="cx"> self.assert_(u in objectstore.get_session().uow.dirty) </span><span class="cx"> self.assert_(u.user_name == 'foo') </span><ins>+ self.assert_(a in u.addresses) </ins><span class="cx"> objectstore.expire(u) </span><span class="cx"> </span><span class="cx"> # expired, but not refreshed yet. still dirty </span><span class="cx"> self.assert_(u in objectstore.get_session().uow.dirty) </span><span class="cx"> # get the attribute, it refreshes </span><span class="cx"> self.assert_(u.user_name == 'jack') </span><ins>+ self.assert_(a not in u.addresses) </ins><span class="cx"> # not dirty anymore </span><span class="cx"> self.assert_(u not in objectstore.get_session().uow.dirty) </span><span class="cx"> </span></span></pre> </div> </div> </body> </html> |
From: <co...@sq...> - 2006-03-24 23:06: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>[1192] sqlalchemy/trunk/lib/sqlalchemy/attributes.py: added overrideable managed_attribute_dict() function which can be changed</title> </head> <body> <div id="msg"> <dl> <dt>Revision</dt> <dd>1192</dd> <dt>Author</dt> <dd>zzzeek</dd> <dt>Date</dt> <dd>2006-03-24 17:06:07 -0600 (Fri, 24 Mar 2006)</dd> </dl> <h3>Log Message</h3> <pre>added overrideable managed_attribute_dict() function which can be changed to eliminate circular references on objects</pre> <h3>Modified Paths</h3> <ul> <li><a href="#sqlalchemytrunklibsqlalchemyattributespy">sqlalchemy/trunk/lib/sqlalchemy/attributes.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 (1191 => 1192)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/trunk/lib/sqlalchemy/attributes.py 2006-03-24 06:28:27 UTC (rev 1191) +++ sqlalchemy/trunk/lib/sqlalchemy/attributes.py 2006-03-24 23:06:07 UTC (rev 1192) </span><span class="lines">@@ -28,6 +28,7 @@ </span><span class="cx"> """ </span><span class="cx"> </span><span class="cx"> import util </span><ins>+import weakref </ins><span class="cx"> from exceptions import * </span><span class="cx"> </span><span class="cx"> class SmartProperty(object): </span><span class="lines">@@ -351,7 +352,7 @@ </span><span class="cx"> def init_attr(self, obj): </span><span class="cx"> """sets up the _managed_attributes dictionary on an object. this happens anyway regardless </span><span class="cx"> of this method being called, but saves on KeyErrors being thrown in get_history().""" </span><del>- d = {} </del><ins>+ d = managed_attribute_dict() </ins><span class="cx"> obj.__dict__['_managed_attributes'] = d </span><span class="cx"> cls_managed = self.class_managed(obj.__class__) </span><span class="cx"> for value in cls_managed.values(): </span><span class="lines">@@ -376,7 +377,7 @@ </span><span class="cx"> trigger = obj.__dict__.pop('_managed_trigger', None) </span><span class="cx"> if trigger: </span><span class="cx"> trigger() </span><del>- attr = {} </del><ins>+ attr = managed_attribute_dict() </ins><span class="cx"> obj.__dict__['_managed_attributes'] = attr </span><span class="cx"> return attr </span><span class="cx"> </span><span class="lines">@@ -458,3 +459,9 @@ </span><span class="cx"> self.class_managed(class_)[key] = createprop </span><span class="cx"> setattr(class_, key, self.create_prop(class_, key, uselist)) </span><span class="cx"> </span><ins>+# make this function return a weakref.WeakValueDictionary to avoid +# creating circular references in objects +def managed_attribute_dict(): + return {} +# return weakref.WeakValueDictionary() + </ins></span></pre> </div> </div> </body> </html> |
From: <co...@sq...> - 2006-03-24 06:28:40
|
<!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>[1191] sqlalchemy/trunk/lib/sqlalchemy: some more tweaks to get more advanced polymorphic stuff to work</title> </head> <body> <div id="msg"> <dl> <dt>Revision</dt> <dd>1191</dd> <dt>Author</dt> <dd>zzzeek</dd> <dt>Date</dt> <dd>2006-03-24 00:28:27 -0600 (Fri, 24 Mar 2006)</dd> </dl> <h3>Log Message</h3> <pre>some more tweaks to get more advanced polymorphic stuff to work</pre> <h3>Modified Paths</h3> <ul> <li><a href="#sqlalchemytrunkexamplespolymorphpolymorphpy">sqlalchemy/trunk/examples/polymorph/polymorph.py</a></li> <li><a href="#sqlalchemytrunklibsqlalchemymappingmapperpy">sqlalchemy/trunk/lib/sqlalchemy/mapping/mapper.py</a></li> <li><a href="#sqlalchemytrunklibsqlalchemymappingpropertiespy">sqlalchemy/trunk/lib/sqlalchemy/mapping/properties.py</a></li> <li><a href="#sqlalchemytrunklibsqlalchemysqlpy">sqlalchemy/trunk/lib/sqlalchemy/sql.py</a></li> </ul> <h3>Added Paths</h3> <ul> <li><a href="#sqlalchemytrunkexamplespolymorphpolymorph2py">sqlalchemy/trunk/examples/polymorph/polymorph2.py</a></li> </ul> </div> <div id="patch"> <h3>Diff</h3> <a id="sqlalchemytrunkexamplespolymorphpolymorphpy"></a> <div class="modfile"><h4>Modified: sqlalchemy/trunk/examples/polymorph/polymorph.py (1190 => 1191)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/trunk/examples/polymorph/polymorph.py 2006-03-23 07:38:28 UTC (rev 1190) +++ sqlalchemy/trunk/examples/polymorph/polymorph.py 2006-03-24 06:28:27 UTC (rev 1191) </span><span class="lines">@@ -23,7 +23,7 @@ </span><span class="cx"> </span><span class="cx"> engineers = Table('engineers', db, </span><span class="cx"> Column('person_id', Integer, ForeignKey('people.person_id'), primary_key=True), </span><del>- Column('description', String(50))).create() </del><ins>+ Column('special_description', String(50))).create() </ins><span class="cx"> </span><span class="cx"> managers = Table('managers', db, </span><span class="cx"> Column('person_id', Integer, ForeignKey('people.person_id'), primary_key=True), </span><span class="lines">@@ -36,7 +36,7 @@ </span><span class="cx"> return "Ordinary person %s" % self.name </span><span class="cx"> class Engineer(Person): </span><span class="cx"> def __repr__(self): </span><del>- return "Engineer %s, description %s" % (self.name, self.description) </del><ins>+ return "Engineer %s, description %s" % (self.name, self.special_description) </ins><span class="cx"> class Manager(Person): </span><span class="cx"> def __repr__(self): </span><span class="cx"> return "Manager %s, description %s" % (self.name, self.description) </span><span class="lines">@@ -69,7 +69,7 @@ </span><span class="cx"> [people, managers.c.description,column("'manager'").label('type')], </span><span class="cx"> people.c.person_id==managers.c.person_id).union_all( </span><span class="cx"> select( </span><del>- [people, engineers.c.description, column("'engineer'").label('type')], </del><ins>+ [people, engineers.c.special_description.label('description'), column("'engineer'").label('type')], </ins><span class="cx"> people.c.person_id==engineers.c.person_id)).alias('pjoin') </span><span class="cx"> </span><span class="cx"> </span><span class="lines">@@ -83,7 +83,9 @@ </span><span class="cx"> class PersonLoader(MapperExtension): </span><span class="cx"> def create_instance(self, mapper, row, imap, class_): </span><span class="cx"> if row['pjoin_type'] =='engineer': </span><del>- return Engineer() </del><ins>+ e = Engineer() + e.special_description = row['pjoin_description'] + return e </ins><span class="cx"> elif row['pjoin_type'] =='manager': </span><span class="cx"> return Manager() </span><span class="cx"> else: </span><span class="lines">@@ -111,8 +113,8 @@ </span><span class="cx"> </span><span class="cx"> c = Company(name='company1') </span><span class="cx"> c.employees.append(Manager(name='pointy haired boss', description='manager1')) </span><del>-c.employees.append(Engineer(name='dilbert', description='engineer1')) -c.employees.append(Engineer(name='wally', description='engineer2')) </del><ins>+c.employees.append(Engineer(name='dilbert', special_description='engineer1')) +c.employees.append(Engineer(name='wally', special_description='engineer2')) </ins><span class="cx"> c.employees.append(Manager(name='jsmith', description='manager2')) </span><span class="cx"> objectstore.commit() </span><span class="cx"> </span><span class="lines">@@ -125,7 +127,7 @@ </span><span class="cx"> print "\n" </span><span class="cx"> </span><span class="cx"> dilbert = Engineer.mapper.get_by(name='dilbert') </span><del>-dilbert.description = 'hes dibert!' </del><ins>+dilbert.special_description = 'hes dibert!' </ins><span class="cx"> objectstore.commit() </span><span class="cx"> </span><span class="cx"> objectstore.clear() </span></span></pre></div> <a id="sqlalchemytrunkexamplespolymorphpolymorph2py"></a> <div class="addfile"><h4>Added: sqlalchemy/trunk/examples/polymorph/polymorph2.py (1190 => 1191)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/trunk/examples/polymorph/polymorph2.py 2006-03-23 07:38:28 UTC (rev 1190) +++ sqlalchemy/trunk/examples/polymorph/polymorph2.py 2006-03-24 06:28:27 UTC (rev 1191) </span><span class="lines">@@ -0,0 +1,137 @@ </span><ins>+from sqlalchemy import * +import sys + +# this example illustrates a polymorphic load of two classes, where each class has a very +# different set of properties + +db = create_engine('sqlite://', echo=True, echo_uow=False) + +# a table to store companies +companies = Table('companies', db, + Column('company_id', Integer, primary_key=True), + Column('name', String(50))).create() + +# we will define an inheritance relationship between the table "people" and "engineers", +# and a second inheritance relationship between the table "people" and "managers" +people = Table('people', db, + Column('person_id', Integer, primary_key=True), + Column('company_id', Integer, ForeignKey('companies.company_id')), + Column('name', String(50))).create() + +engineers = Table('engineers', db, + Column('person_id', Integer, ForeignKey('people.person_id'), primary_key=True), + Column('status', String(30)), + Column('engineer_name', String(50)), + Column('primary_language', String(50)), + ).create() + +managers = Table('managers', db, + Column('person_id', Integer, ForeignKey('people.person_id'), primary_key=True), + Column('status', String(30)), + Column('manager_name', String(50)) + ).create() + + +# create our classes. The Engineer and Manager classes extend from Person. +class Person(object): + def __repr__(self): + return "Ordinary person %s" % self.name +class Engineer(Person): + def __repr__(self): + return "Engineer %s, status %s, engineer_name %s, primary_language %s" % (self.name, self.status, self.engineer_name, self.primary_language) +class Manager(Person): + def __repr__(self): + return "Manager %s, status %s, manager_name %s" % (self.name, self.status, self.manager_name) +class Company(object): + def __repr__(self): + return "Company %s" % self.name + +# assign plain vanilla mappers +assign_mapper(Person, people) +assign_mapper(Engineer, engineers, inherits=Person.mapper) +assign_mapper(Manager, managers, inherits=Person.mapper) + +# create a union that represents both types of joins. we have to use +# nulls to pad out the disparate columns. +person_join = select( + [ + people, + managers.c.status, + managers.c.manager_name, + null().label('engineer_name'), + null().label('primary_language'), + column("'manager'").label('type') + ], + people.c.person_id==managers.c.person_id).union_all( + select( + [ + people, + engineers.c.status, + null().label('').label('manager_name'), + engineers.c.engineer_name, + engineers.c.primary_language, + column("'engineer'").label('type') + ], + people.c.person_id==engineers.c.person_id)).alias('pjoin') + + +# MapperExtension object. +class PersonLoader(MapperExtension): + def create_instance(self, mapper, row, imap, class_): + if row[person_join.c.type] =='engineer': + return Engineer() + elif row[person_join.c.type] =='manager': + return Manager() + else: + return Person() + + def populate_instance(self, mapper, instance, row, identitykey, imap, isnew): + if row[person_join.c.type] =='engineer': + Engineer.mapper.populate_instance(instance, row, identitykey, imap, isnew) + return False + elif row[person_join.c.type] =='manager': + Manager.mapper.populate_instance(instance, row, identitykey, imap, isnew) + return False + else: + return True + + + +people_mapper = mapper(Person, person_join, extension=PersonLoader()) + +assign_mapper(Company, companies, properties={ + 'employees': relation(people_mapper, lazy=False, private=True) +}) + +c = Company(name='company1') +c.employees.append(Manager(name='pointy haired boss', status='AAB', manager_name='manager1')) +c.employees.append(Engineer(name='dilbert', status='BBA', engineer_name='engineer1', primary_language='java')) +c.employees.append(Engineer(name='wally', status='CGG', engineer_name='engineer2', primary_language='python')) +c.employees.append(Manager(name='jsmith', status='ABA', manager_name='manager2')) +objectstore.commit() + +objectstore.clear() + +c = Company.get(1) +for e in c.employees: + print e, e._instance_key + +print "\n" + +dilbert = Engineer.mapper.get_by(name='dilbert') +dilbert.engineer_name = 'hes dibert!' +objectstore.commit() + +objectstore.clear() +c = Company.get(1) +for e in c.employees: + print e, e._instance_key + +objectstore.delete(c) +objectstore.commit() + + +managers.drop() +engineers.drop() +people.drop() +companies.drop() </ins></span></pre></div> <a id="sqlalchemytrunklibsqlalchemymappingmapperpy"></a> <div class="modfile"><h4>Modified: sqlalchemy/trunk/lib/sqlalchemy/mapping/mapper.py (1190 => 1191)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/trunk/lib/sqlalchemy/mapping/mapper.py 2006-03-23 07:38:28 UTC (rev 1190) +++ sqlalchemy/trunk/lib/sqlalchemy/mapping/mapper.py 2006-03-24 06:28:27 UTC (rev 1191) </span><span class="lines">@@ -291,7 +291,7 @@ </span><span class="cx"> objectstore.get_session().register_clean(value) </span><span class="cx"> </span><span class="cx"> if mappers: </span><del>- result.extend(otherresults) </del><ins>+ result = [result] + otherresults </ins><span class="cx"> return result </span><span class="cx"> </span><span class="cx"> def get(self, *ident): </span><span class="lines">@@ -837,8 +837,8 @@ </span><span class="cx"> </span><span class="cx"> # call further mapper properties on the row, to pull further </span><span class="cx"> # instances from the row and possibly populate this item. </span><del>- for prop in self.props.values(): - prop.execute(instance, row, identitykey, imap, isnew) </del><ins>+ if self.extension.populate_instance(self, instance, row, identitykey, imap, isnew): + self.populate_instance(instance, row, identitykey, imap, isnew, translate=False) </ins><span class="cx"> </span><span class="cx"> if self.extension.append_result(self, row, imap, result, instance, isnew, populate_existing=populate_existing): </span><span class="cx"> if result is not None: </span><span class="lines">@@ -846,6 +846,17 @@ </span><span class="cx"> </span><span class="cx"> return instance </span><span class="cx"> </span><ins>+ def populate_instance(self, instance, row, identitykey, imap, isnew, translate=True): + if translate: + newrow = {} + for table in self.tables: + for c in table.c: + newrow[c] = row[c.key] + row = newrow + + for prop in self.props.values(): + prop.execute(instance, row, identitykey, imap, isnew) + </ins><span class="cx"> class MapperProperty(object): </span><span class="cx"> """an element attached to a Mapper that describes and assists in the loading and saving </span><span class="cx"> of an attribute on an object instance.""" </span><span class="lines">@@ -930,7 +941,8 @@ </span><span class="cx"> def append_result(self, mapper, row, imap, result, instance, isnew, populate_existing=False): </span><span class="cx"> """called when an object instance is being appended to a result list. </span><span class="cx"> </span><del>- If it returns True, it is assumed that this method handled the appending itself. </del><ins>+ If this method returns True, it is assumed that the mapper should do the appending, else + if this method returns False, it is assumed that the append was handled by this method. </ins><span class="cx"> </span><span class="cx"> mapper - the mapper doing the operation </span><span class="cx"> </span><span class="lines">@@ -956,6 +968,22 @@ </span><span class="cx"> return True </span><span class="cx"> else: </span><span class="cx"> return self.next.append_result(mapper, row, imap, result, instance, isnew, populate_existing) </span><ins>+ def populate_instance(self, mapper, instance, row, identitykey, imap, isnew): + """called right before the mapper, after creating an instance from a row, passes the row + to its MapperProperty objects which are responsible for populating the object's attributes. + If this method returns True, it is assumed that the mapper should do the appending, else + if this method returns False, it is assumed that the append was handled by this method. + + Essentially, this method is used to have a different mapper populate the object: + + def populate_instance(self, mapper, *args): + othermapper.populate_instance(*args) + return False + """ + if self.next is None: + return True + else: + return self.next.populate_instance(row, imap, result, instance, isnew) </ins><span class="cx"> def before_insert(self, mapper, instance): </span><span class="cx"> """called before an object instance is INSERTed into its table. </span><span class="cx"> </span></span></pre></div> <a id="sqlalchemytrunklibsqlalchemymappingpropertiespy"></a> <div class="modfile"><h4>Modified: sqlalchemy/trunk/lib/sqlalchemy/mapping/properties.py (1190 => 1191)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/trunk/lib/sqlalchemy/mapping/properties.py 2006-03-23 07:38:28 UTC (rev 1190) +++ sqlalchemy/trunk/lib/sqlalchemy/mapping/properties.py 2006-03-24 06:28:27 UTC (rev 1191) </span><span class="lines">@@ -807,11 +807,14 @@ </span><span class="cx"> if map.has_key(key): </span><span class="cx"> key = map[key] </span><span class="cx"> return self.row[key] </span><ins>+ def keys(self): + return map.keys() </ins><span class="cx"> map = {} </span><span class="cx"> for c in self.eagertarget.c: </span><span class="cx"> parent = self.target._get_col_by_original(c.original) </span><span class="cx"> map[parent] = c </span><span class="cx"> map[parent._label] = c </span><ins>+ map[parent.name] = c </ins><span class="cx"> return DecoratorDict </span><span class="cx"> </span><span class="cx"> def _instance(self, row, imap, result_list=None): </span></span></pre></div> <a id="sqlalchemytrunklibsqlalchemysqlpy"></a> <div class="modfile"><h4>Modified: sqlalchemy/trunk/lib/sqlalchemy/sql.py (1190 => 1191)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/trunk/lib/sqlalchemy/sql.py 2006-03-23 07:38:28 UTC (rev 1190) +++ sqlalchemy/trunk/lib/sqlalchemy/sql.py 2006-03-24 06:28:27 UTC (rev 1191) </span><span class="lines">@@ -13,7 +13,7 @@ </span><span class="cx"> import string, re, random </span><span class="cx"> types = __import__('types') </span><span class="cx"> </span><del>-__all__ = ['text', 'table', 'column', 'func', 'select', 'update', 'insert', 'delete', 'join', 'and_', 'or_', 'not_', 'union', 'union_all', 'desc', 'asc', 'outerjoin', 'alias', 'subquery', 'literal', 'bindparam', 'exists'] </del><ins>+__all__ = ['text', 'table', 'column', 'func', 'select', 'update', 'insert', 'delete', 'join', 'and_', 'or_', 'not_', 'union', 'union_all', 'null', 'desc', 'asc', 'outerjoin', 'alias', 'subquery', 'literal', 'bindparam', 'exists'] </ins><span class="cx"> </span><span class="cx"> def desc(column): </span><span class="cx"> """returns a descending ORDER BY clause element, e.g.: </span><span class="lines">@@ -705,9 +705,11 @@ </span><span class="cx"> def _get_from_objects(self): </span><span class="cx"> return [] </span><span class="cx"> </span><del>-class Null(ClauseElement): </del><ins>+class Null(ColumnElement): </ins><span class="cx"> """represents the NULL keyword in a SQL statement. public contstructor is the </span><span class="cx"> null() function.""" </span><ins>+ def __init__(self): + self.type = sqltypes.NULLTYPE </ins><span class="cx"> def accept_visitor(self, visitor): </span><span class="cx"> visitor.visit_null(self) </span><span class="cx"> def _get_from_objects(self): </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>[1190] sqlalchemy/trunk/lib/sqlalchemy/databases/sqlite.py: added explicit "convert date types to a string in bind params", since pysqlite1 doesnet seem to do it, operation is synymous with what pysqlite2 does</title> </head> <body> <div id="msg"> <dl> <dt>Revision</dt> <dd>1190</dd> <dt>Author</dt> <dd>zzzeek</dd> <dt>Date</dt> <dd>2006-03-23 01:38:28 -0600 (Thu, 23 Mar 2006)</dd> </dl> <h3>Log Message</h3> <pre>added explicit "convert date types to a string in bind params", since pysqlite1 doesnet seem to do it, operation is synymous with what pysqlite2 does</pre> <h3>Modified Paths</h3> <ul> <li><a href="#sqlalchemytrunklibsqlalchemydatabasessqlitepy">sqlalchemy/trunk/lib/sqlalchemy/databases/sqlite.py</a></li> </ul> </div> <div id="patch"> <h3>Diff</h3> <a id="sqlalchemytrunklibsqlalchemydatabasessqlitepy"></a> <div class="modfile"><h4>Modified: sqlalchemy/trunk/lib/sqlalchemy/databases/sqlite.py (1189 => 1190)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/trunk/lib/sqlalchemy/databases/sqlite.py 2006-03-23 06:35:27 UTC (rev 1189) +++ sqlalchemy/trunk/lib/sqlalchemy/databases/sqlite.py 2006-03-23 07:38:28 UTC (rev 1190) </span><span class="lines">@@ -38,6 +38,11 @@ </span><span class="cx"> class SLDateTime(sqltypes.DateTime): </span><span class="cx"> def get_col_spec(self): </span><span class="cx"> return "TIMESTAMP" </span><ins>+ def convert_bind_param(self, value, engine): + if value is not None: + return str(value) + else: + return None </ins><span class="cx"> def _cvt(self, value, engine, fmt): </span><span class="cx"> if value is None: </span><span class="cx"> return None </span></span></pre> </div> </div> </body> </html> |
From: <co...@sq...> - 2006-03-23 06:35: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>[1189] sqlalchemy/trunk/test: added oracle8 test target, sets use_ansi to false</title> </head> <body> <div id="msg"> <dl> <dt>Revision</dt> <dd>1189</dd> <dt>Author</dt> <dd>zzzeek</dd> <dt>Date</dt> <dd>2006-03-23 00:35:27 -0600 (Thu, 23 Mar 2006)</dd> </dl> <h3>Log Message</h3> <pre>added oracle8 test target, sets use_ansi to false got mapper, objectstore, inheritance unittest working with oracle8, tweaks to join syntax</pre> <h3>Modified Paths</h3> <ul> <li><a href="#sqlalchemytrunklibsqlalchemydatabasesoraclepy">sqlalchemy/trunk/lib/sqlalchemy/databases/oracle.py</a></li> <li><a href="#sqlalchemytrunktestinheritancepy">sqlalchemy/trunk/test/inheritance.py</a></li> <li><a href="#sqlalchemytrunktesttestbasepy">sqlalchemy/trunk/test/testbase.py</a></li> </ul> </div> <div id="patch"> <h3>Diff</h3> <a id="sqlalchemytrunklibsqlalchemydatabasesoraclepy"></a> <div class="modfile"><h4>Modified: sqlalchemy/trunk/lib/sqlalchemy/databases/oracle.py (1188 => 1189)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/trunk/lib/sqlalchemy/databases/oracle.py 2006-03-23 06:19:26 UTC (rev 1188) +++ sqlalchemy/trunk/lib/sqlalchemy/databases/oracle.py 2006-03-23 06:35:27 UTC (rev 1189) </span><span class="lines">@@ -233,14 +233,13 @@ </span><span class="cx"> </span><span class="cx"> if join.isouter: </span><span class="cx"> # if outer join, push on the right side table as the current "outertable" </span><del>- outertable = self._outertable </del><span class="cx"> self._outertable = join.right </span><span class="cx"> </span><span class="cx"> # now re-visit the onclause, which will be used as a where clause </span><span class="cx"> # (the first visit occured via the Join object itself right before it called visit_join()) </span><span class="cx"> join.onclause.accept_visitor(self) </span><span class="cx"> </span><del>- self._outertable = outertable </del><ins>+ self._outertable = None </ins><span class="cx"> </span><span class="cx"> self.visit_compound(self.wheres[join]) </span><span class="cx"> </span><span class="lines">@@ -250,13 +249,9 @@ </span><span class="cx"> self.strings[alias] = self.get_str(alias.original) </span><span class="cx"> </span><span class="cx"> def visit_column(self, column): </span><del>- if self._use_ansi: - return ansisql.ANSICompiler.visit_column(self, column) - - if column.table is self._outertable: - self.strings[column] = "%s.%s(+)" % (column.table.name, column.name) - else: - self.strings[column] = "%s.%s" % (column.table.name, column.name) </del><ins>+ ansisql.ANSICompiler.visit_column(self, column) + if not self._use_ansi and self._outertable is not None and column.table is self._outertable: + self.strings[column] = self.strings[column] + "(+)" </ins><span class="cx"> </span><span class="cx"> def visit_insert(self, insert): </span><span class="cx"> """inserts are required to have the primary keys be explicitly present. </span></span></pre></div> <a id="sqlalchemytrunktestinheritancepy"></a> <div class="modfile"><h4>Modified: sqlalchemy/trunk/test/inheritance.py (1188 => 1189)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/trunk/test/inheritance.py 2006-03-23 06:19:26 UTC (rev 1188) +++ sqlalchemy/trunk/test/inheritance.py 2006-03-23 06:35:27 UTC (rev 1189) </span><span class="lines">@@ -95,7 +95,7 @@ </span><span class="cx"> engine = testbase.db </span><span class="cx"> global foo, bar, foo_bar </span><span class="cx"> foo = Table('foo', engine, </span><del>- Column('id', Integer, primary_key=True), </del><ins>+ Column('id', Integer, Sequence('foo_id_seq'), primary_key=True), </ins><span class="cx"> Column('data', String(20)), </span><span class="cx"> ).create() </span><span class="cx"> </span></span></pre></div> <a id="sqlalchemytrunktesttestbasepy"></a> <div class="modfile"><h4>Modified: sqlalchemy/trunk/test/testbase.py (1188 => 1189)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/trunk/test/testbase.py 2006-03-23 06:19:26 UTC (rev 1188) +++ sqlalchemy/trunk/test/testbase.py 2006-03-23 06:35:27 UTC (rev 1189) </span><span class="lines">@@ -25,7 +25,7 @@ </span><span class="cx"> elif sys.argv[1] == '--db': </span><span class="cx"> (param, DBTYPE) = (sys.argv.pop(1), sys.argv.pop(1)) </span><span class="cx"> </span><del>- </del><ins>+ opts = {} </ins><span class="cx"> if (None == db_uri): </span><span class="cx"> p = DBTYPE.split('.') </span><span class="cx"> if len(p) > 1: </span><span class="lines">@@ -43,15 +43,18 @@ </span><span class="cx"> db_uri = 'mysql://database=test&host=127.0.0.1&user=scott&password=tiger' </span><span class="cx"> elif DBTYPE == 'oracle': </span><span class="cx"> db_uri = 'oracle://user=scott&password=tiger' </span><ins>+ elif DBTYPE == 'oracle8': + db_uri = 'oracle://user=scott&password=tiger' + opts = {'use_ansi':False} </ins><span class="cx"> </span><span class="cx"> if not db_uri: </span><span class="cx"> raise "Could not create engine. specify --db <sqlite|sqlite_file|postgres|mysql|oracle> to test runner." </span><span class="cx"> </span><span class="cx"> if PROXY: </span><del>- db = proxy.ProxyEngine(echo=echo, default_ordering=True) </del><ins>+ db = proxy.ProxyEngine(echo=echo, default_ordering=True, **opts) </ins><span class="cx"> db.connect(db_uri) </span><span class="cx"> else: </span><del>- db = engine.create_engine(db_uri, echo=echo, default_ordering=True) </del><ins>+ db = engine.create_engine(db_uri, echo=echo, default_ordering=True, **opts) </ins><span class="cx"> db = EngineAssert(db) </span><span class="cx"> </span><span class="cx"> class PersistTest(unittest.TestCase): </span></span></pre> </div> </div> </body> </html> |
From: <co...@sq...> - 2006-03-23 06:19:36
|
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head><style type="text/css"><!-- #msg dl { border: 1px #006 solid; background: #369; padding: 6px; color: #fff; } #msg dt { float: left; width: 6em; font-weight: bold; } #msg dt:after { content:':';} #msg dl, #msg dt, #msg ul, #msg li { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; } #msg dl a { font-weight: bold} #msg dl a:link { color:#fc3; } #msg dl a:active { color:#ff0; } #msg dl a:visited { color:#cc6; } h3 { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; font-weight: bold; } #msg pre { overflow: auto; background: #ffc; border: 1px #fc0 solid; padding: 6px; } #msg ul, pre { overflow: auto; } #patch { width: 100%; } #patch h4 {font-family: verdana,arial,helvetica,sans-serif;font-size:10pt;padding:8px;background:#369;color:#fff;margin:0;} #patch .propset h4, #patch .binary h4 {margin:0;} #patch pre {padding:0;line-height:1.2em;margin:0;} #patch .diff {width:100%;background:#eee;padding: 0 0 10px 0;overflow:auto;} #patch .propset .diff, #patch .binary .diff {padding:10px 0;} #patch span {display:block;padding:0 10px;} #patch .modfile, #patch .addfile, #patch .delfile, #patch .propset, #patch .binary, #patch .copfile {border:1px solid #ccc;margin:10px 0;} #patch ins {background:#dfd;text-decoration:none;display:block;padding:0 10px;} #patch del {background:#fdd;text-decoration:none;display:block;padding:0 10px;} #patch .lines, .info {color:#888;background:#fff;} --></style> <title>[1188] sqlalchemy/trunk/lib/sqlalchemy/databases/oracle.py: another adjustment...</title> </head> <body> <div id="msg"> <dl> <dt>Revision</dt> <dd>1188</dd> <dt>Author</dt> <dd>zzzeek</dd> <dt>Date</dt> <dd>2006-03-23 00:19:26 -0600 (Thu, 23 Mar 2006)</dd> </dl> <h3>Log Message</h3> <pre>another adjustment...</pre> <h3>Modified Paths</h3> <ul> <li><a href="#sqlalchemytrunklibsqlalchemydatabasesoraclepy">sqlalchemy/trunk/lib/sqlalchemy/databases/oracle.py</a></li> </ul> </div> <div id="patch"> <h3>Diff</h3> <a id="sqlalchemytrunklibsqlalchemydatabasesoraclepy"></a> <div class="modfile"><h4>Modified: sqlalchemy/trunk/lib/sqlalchemy/databases/oracle.py (1187 => 1188)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/trunk/lib/sqlalchemy/databases/oracle.py 2006-03-23 06:17:29 UTC (rev 1187) +++ sqlalchemy/trunk/lib/sqlalchemy/databases/oracle.py 2006-03-23 06:19:26 UTC (rev 1188) </span><span class="lines">@@ -290,7 +290,7 @@ </span><span class="cx"> if len(select.primary_key): </span><span class="cx"> col = select.primary_key[0].original.table.name </span><span class="cx"> else: </span><del>- col = select.froms[0].name </del><ins>+ col = [c for c in select.c][0].original.table.name </ins><span class="cx"> orderby = "%s.rowid ASC" % col </span><span class="cx"> select.append_column(sql.ColumnClause("ROW_NUMBER() OVER (ORDER BY %s)" % orderby).label("ora_rn")) </span><span class="cx"> limitselect = sql.select([c for c in select.c if c.key!='ora_rn']) </span></span></pre> </div> </div> </body> </html> |
From: <co...@sq...> - 2006-03-23 06:17:49
|
<!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>[1187] sqlalchemy/trunk/test: some adjustments to oracle non-ansi join concatenation, 'row number over' syntax</title> </head> <body> <div id="msg"> <dl> <dt>Revision</dt> <dd>1187</dd> <dt>Author</dt> <dd>zzzeek</dd> <dt>Date</dt> <dd>2006-03-23 00:17:29 -0600 (Thu, 23 Mar 2006)</dd> </dl> <h3>Log Message</h3> <pre>some adjustments to oracle non-ansi join concatenation, 'row number over' syntax</pre> <h3>Modified Paths</h3> <ul> <li><a href="#sqlalchemytrunklibsqlalchemydatabasesoraclepy">sqlalchemy/trunk/lib/sqlalchemy/databases/oracle.py</a></li> <li><a href="#sqlalchemytrunktestselectpy">sqlalchemy/trunk/test/select.py</a></li> </ul> </div> <div id="patch"> <h3>Diff</h3> <a id="sqlalchemytrunklibsqlalchemydatabasesoraclepy"></a> <div class="modfile"><h4>Modified: sqlalchemy/trunk/lib/sqlalchemy/databases/oracle.py (1186 => 1187)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/trunk/lib/sqlalchemy/databases/oracle.py 2006-03-22 21:25:10 UTC (rev 1186) +++ sqlalchemy/trunk/lib/sqlalchemy/databases/oracle.py 2006-03-23 06:17:29 UTC (rev 1187) </span><span class="lines">@@ -227,9 +227,9 @@ </span><span class="cx"> def visit_join(self, join): </span><span class="cx"> if self._use_ansi: </span><span class="cx"> return ansisql.ANSICompiler.visit_join(self, join) </span><del>- </del><ins>+ </ins><span class="cx"> self.froms[join] = self.get_from_text(join.left) + ", " + self.get_from_text(join.right) </span><del>- self.wheres[join] = join.onclause </del><ins>+ self.wheres[join] = sql.and_(self.wheres.get(join.left, None), join.onclause) </ins><span class="cx"> </span><span class="cx"> if join.isouter: </span><span class="cx"> # if outer join, push on the right side table as the current "outertable" </span><span class="lines">@@ -241,6 +241,8 @@ </span><span class="cx"> join.onclause.accept_visitor(self) </span><span class="cx"> </span><span class="cx"> self._outertable = outertable </span><ins>+ + self.visit_compound(self.wheres[join]) </ins><span class="cx"> </span><span class="cx"> def visit_alias(self, alias): </span><span class="cx"> """oracle doesnt like 'FROM table AS alias'. is the AS standard SQL??""" </span><span class="lines">@@ -250,7 +252,7 @@ </span><span class="cx"> def visit_column(self, column): </span><span class="cx"> if self._use_ansi: </span><span class="cx"> return ansisql.ANSICompiler.visit_column(self, column) </span><del>- </del><ins>+ </ins><span class="cx"> if column.table is self._outertable: </span><span class="cx"> self.strings[column] = "%s.%s(+)" % (column.table.name, column.name) </span><span class="cx"> else: </span><span class="lines">@@ -285,7 +287,11 @@ </span><span class="cx"> # to use ROW_NUMBER(), an ORDER BY is required. so here we dig in </span><span class="cx"> # as best we can to find some column we can order by </span><span class="cx"> # TODO: try to get "oid_column" to be used here </span><del>- orderby = "%s.rowid ASC" % select.primary_key[0].original.table.name </del><ins>+ if len(select.primary_key): + col = select.primary_key[0].original.table.name + else: + col = select.froms[0].name + orderby = "%s.rowid ASC" % col </ins><span class="cx"> select.append_column(sql.ColumnClause("ROW_NUMBER() OVER (ORDER BY %s)" % orderby).label("ora_rn")) </span><span class="cx"> limitselect = sql.select([c for c in select.c if c.key!='ora_rn']) </span><span class="cx"> if select.offset is not None: </span></span></pre></div> <a id="sqlalchemytrunktestselectpy"></a> <div class="modfile"><h4>Modified: sqlalchemy/trunk/test/select.py (1186 => 1187)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/trunk/test/select.py 2006-03-22 21:25:10 UTC (rev 1186) +++ sqlalchemy/trunk/test/select.py 2006-03-23 06:17:29 UTC (rev 1187) </span><span class="lines">@@ -463,6 +463,10 @@ </span><span class="cx"> myothertable.othername != :myothertable_othername AND EXISTS (select yay from foo where boo = lar)", </span><span class="cx"> engine = oracle.engine({}, use_ansi = False)) </span><span class="cx"> </span><ins>+ query = table1.outerjoin(table2, table1.c.myid==table2.c.otherid).outerjoin(table3, table3.c.userid==table2.c.otherid) + self.runtest(query.select(), "SELECT mytable.myid, mytable.name, mytable.description, myothertable.otherid, myothertable.othername, thirdtable.userid, thirdtable.otherstuff FROM mytable LEFT OUTER JOIN myothertable ON mytable.myid = myothertable.otherid LEFT OUTER JOIN thirdtable ON thirdtable.userid = myothertable.otherid") + self.runtest(query.select(), "SELECT mytable.myid, mytable.name, mytable.description, myothertable.otherid, myothertable.othername, thirdtable.userid, thirdtable.otherstuff FROM mytable, myothertable, thirdtable WHERE mytable.myid = myothertable.otherid(+) AND thirdtable.userid(+) = myothertable.otherid", engine=oracle.engine({}, use_ansi=False)) + </ins><span class="cx"> def testbindparam(self): </span><span class="cx"> self.runtest(select( </span><span class="cx"> [table1, table2], </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>[1186] sqlalchemy/trunk/lib/sqlalchemy/mapping/properties.py: added some extra traversal for one-to-many/many-to-one "private" relations to allow single-object commits to cascade into private child objects</title> </head> <body> <div id="msg"> <dl> <dt>Revision</dt> <dd>1186</dd> <dt>Author</dt> <dd>zzzeek</dd> <dt>Date</dt> <dd>2006-03-22 15:25:10 -0600 (Wed, 22 Mar 2006)</dd> </dl> <h3>Log Message</h3> <pre>added some extra traversal for one-to-many/many-to-one "private" relations to allow single-object commits to cascade into private child objects</pre> <h3>Modified Paths</h3> <ul> <li><a href="#sqlalchemytrunklibsqlalchemymappingpropertiespy">sqlalchemy/trunk/lib/sqlalchemy/mapping/properties.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 (1185 => 1186)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/trunk/lib/sqlalchemy/mapping/properties.py 2006-03-22 17:06:49 UTC (rev 1185) +++ sqlalchemy/trunk/lib/sqlalchemy/mapping/properties.py 2006-03-22 21:25:10 UTC (rev 1186) </span><span class="lines">@@ -382,6 +382,7 @@ </span><span class="cx"> else: </span><span class="cx"> uowcommit.register_dependency(self.mapper, self.parent) </span><span class="cx"> uowcommit.register_processor(self.mapper, self, self.parent, False) </span><ins>+ uowcommit.register_processor(self.mapper, self, self.parent, True) </ins><span class="cx"> else: </span><span class="cx"> raise AssertionError(" no foreign key ?") </span><span class="cx"> </span><span class="lines">@@ -444,7 +445,12 @@ </span><span class="cx"> statement = self.secondary.insert() </span><span class="cx"> statement.execute(*secondary_insert) </span><span class="cx"> elif self.direction == PropertyLoader.MANYTOONE and delete: </span><del>- if self.post_update: </del><ins>+ if self.private: + for obj in deplist: + childlist = getlist(obj, False) + for child in childlist.deleted_items() + childlist.unchanged_items(): + uowcommit.register_object(child, isdelete=True) + elif self.post_update: </ins><span class="cx"> # post_update means we have to update our row to not reference the child object </span><span class="cx"> # before we can DELETE the row </span><span class="cx"> for obj in deplist: </span><span class="lines">@@ -455,14 +461,20 @@ </span><span class="cx"> # the child objects have to have their foreign key to the parent set to NULL </span><span class="cx"> if self.private and not self.post_update: </span><span class="cx"> # if we are privately managed, then all our objects should </span><del>- # have been marked as "todelete" already and no attribute adjustment is needed - return - for obj in deplist: - childlist = getlist(obj, False) - for child in childlist.deleted_items() + childlist.unchanged_items(): - if child is not None: - self._synchronize(obj, child, None, True) - uowcommit.register_object(child, postupdate=self.post_update) </del><ins>+ # have been marked as "todelete" already and no attribute adjustment is needed. + # however, if they say objectstore.commit(x), i.e. on an individual object, + # then this extra step is more important. + for obj in deplist: + childlist = getlist(obj, False) + for child in childlist.deleted_items() + childlist.unchanged_items(): + uowcommit.register_object(child, isdelete=True) + else: + for obj in deplist: + childlist = getlist(obj, False) + for child in childlist.deleted_items() + childlist.unchanged_items(): + if child is not None: + self._synchronize(obj, child, None, True) + uowcommit.register_object(child, postupdate=self.post_update) </ins><span class="cx"> elif self.association is not None: </span><span class="cx"> # manage association objects. </span><span class="cx"> for obj in deplist: </span></span></pre> </div> </div> </body> </html> |
From: <co...@sq...> - 2006-03-22 17:07:01
|
<!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>[1185] sqlalchemy/trunk/lib/sqlalchemy/mapping/properties.py: removed print</title> </head> <body> <div id="msg"> <dl> <dt>Revision</dt> <dd>1185</dd> <dt>Author</dt> <dd>zzzeek</dd> <dt>Date</dt> <dd>2006-03-22 11:06:49 -0600 (Wed, 22 Mar 2006)</dd> </dl> <h3>Log Message</h3> <pre>removed print</pre> <h3>Modified Paths</h3> <ul> <li><a href="#sqlalchemytrunklibsqlalchemymappingpropertiespy">sqlalchemy/trunk/lib/sqlalchemy/mapping/properties.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 (1184 => 1185)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/trunk/lib/sqlalchemy/mapping/properties.py 2006-03-22 16:57:46 UTC (rev 1184) +++ sqlalchemy/trunk/lib/sqlalchemy/mapping/properties.py 2006-03-22 17:06:49 UTC (rev 1185) </span><span class="lines">@@ -645,7 +645,6 @@ </span><span class="cx"> lazywhere = lazywhere.copy_container() </span><span class="cx"> li = BinaryVisitor(visit_binary) </span><span class="cx"> lazywhere.accept_visitor(li) </span><del>- print "OK LAZYWHERE, START WITH TAB", table, "PJ", primaryjoin, "SEC", secondaryjoin, "GOT", lazywhere </del><span class="cx"> return (lazywhere, binds) </span><span class="cx"> </span><span class="cx"> </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>[1184] sqlalchemy/trunk/lib/sqlalchemy/databases/oracle.py: fixed oracle's efforts to get an ORDER BY for its ROW NUMBER OVER clause, fixed support for multi-leveled Alias objects to render correctly</title> </head> <body> <div id="msg"> <dl> <dt>Revision</dt> <dd>1184</dd> <dt>Author</dt> <dd>zzzeek</dd> <dt>Date</dt> <dd>2006-03-22 10:57:46 -0600 (Wed, 22 Mar 2006)</dd> </dl> <h3>Log Message</h3> <pre>fixed oracle's efforts to get an ORDER BY for its ROW NUMBER OVER clause, fixed support for multi-leveled Alias objects to render correctly</pre> <h3>Modified Paths</h3> <ul> <li><a href="#sqlalchemytrunklibsqlalchemydatabasesoraclepy">sqlalchemy/trunk/lib/sqlalchemy/databases/oracle.py</a></li> </ul> </div> <div id="patch"> <h3>Diff</h3> <a id="sqlalchemytrunklibsqlalchemydatabasesoraclepy"></a> <div class="modfile"><h4>Modified: sqlalchemy/trunk/lib/sqlalchemy/databases/oracle.py (1183 => 1184)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/trunk/lib/sqlalchemy/databases/oracle.py 2006-03-22 15:39:57 UTC (rev 1183) +++ sqlalchemy/trunk/lib/sqlalchemy/databases/oracle.py 2006-03-22 16:57:46 UTC (rev 1184) </span><span class="lines">@@ -244,8 +244,8 @@ </span><span class="cx"> </span><span class="cx"> def visit_alias(self, alias): </span><span class="cx"> """oracle doesnt like 'FROM table AS alias'. is the AS standard SQL??""" </span><del>- self.froms[alias] = self.get_from_text(alias.selectable) + " " + alias.name - self.strings[alias] = self.get_str(alias.selectable) </del><ins>+ self.froms[alias] = self.get_from_text(alias.original) + " " + alias.name + self.strings[alias] = self.get_str(alias.original) </ins><span class="cx"> </span><span class="cx"> def visit_column(self, column): </span><span class="cx"> if self._use_ansi: </span><span class="lines">@@ -282,8 +282,10 @@ </span><span class="cx"> if hasattr(select, "order_by_clause"): </span><span class="cx"> orderby = self.strings[select.order_by_clause] </span><span class="cx"> else: </span><ins>+ # to use ROW_NUMBER(), an ORDER BY is required. so here we dig in + # as best we can to find some column we can order by </ins><span class="cx"> # TODO: try to get "oid_column" to be used here </span><del>- orderby = "%s.rowid ASC" % select.froms[0].name </del><ins>+ orderby = "%s.rowid ASC" % select.primary_key[0].original.table.name </ins><span class="cx"> select.append_column(sql.ColumnClause("ROW_NUMBER() OVER (ORDER BY %s)" % orderby).label("ora_rn")) </span><span class="cx"> limitselect = sql.select([c for c in select.c if c.key!='ora_rn']) </span><span class="cx"> if select.offset is not None: </span></span></pre> </div> </div> </body> </html> |
From: <co...@sq...> - 2006-03-22 15:40:10
|
<!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>[1183] sqlalchemy/trunk/doc/build/content/docstrings.myt: added Mapper class to the docstrings</title> </head> <body> <div id="msg"> <dl> <dt>Revision</dt> <dd>1183</dd> <dt>Author</dt> <dd>zzzeek</dd> <dt>Date</dt> <dd>2006-03-22 09:39:57 -0600 (Wed, 22 Mar 2006)</dd> </dl> <h3>Log Message</h3> <pre>added Mapper class to the docstrings</pre> <h3>Modified Paths</h3> <ul> <li><a href="#sqlalchemytrunkdocbuildcontentdocstringsmyt">sqlalchemy/trunk/doc/build/content/docstrings.myt</a></li> </ul> </div> <div id="patch"> <h3>Diff</h3> <a id="sqlalchemytrunkdocbuildcontentdocstringsmyt"></a> <div class="modfile"><h4>Modified: sqlalchemy/trunk/doc/build/content/docstrings.myt (1182 => 1183)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/trunk/doc/build/content/docstrings.myt 2006-03-22 15:33:26 UTC (rev 1182) +++ sqlalchemy/trunk/doc/build/content/docstrings.myt 2006-03-22 15:39:57 UTC (rev 1183) </span><span class="lines">@@ -16,7 +16,7 @@ </span><span class="cx"> <& pydoc.myt:obj_doc, obj=engine, classes=[engine.SQLSession, engine.SQLEngine, engine.ResultProxy, engine.RowProxy] &> </span><span class="cx"> <& pydoc.myt:obj_doc, obj=sql, classes=[sql.ClauseParameters, sql.Compiled, sql.ClauseElement, sql.TableClause, sql.ColumnClause] &> </span><span class="cx"> <& pydoc.myt:obj_doc, obj=pool, classes=[pool.DBProxy, pool.Pool, pool.QueuePool, pool.SingletonThreadPool] &> </span><del>-<& pydoc.myt:obj_doc, obj=mapping &> </del><ins>+<& pydoc.myt:obj_doc, obj=mapping, classes=[mapping.Mapper, mapping.MapperExtension] &> </ins><span class="cx"> <& pydoc.myt:obj_doc, obj=mapping.objectstore, classes=[mapping.objectstore.Session, mapping.objectstore.Session.SessionTrans] &> </span><span class="cx"> <& pydoc.myt:obj_doc, obj=exceptions &> </span><span class="cx"> <& pydoc.myt:obj_doc, obj=proxy &> </span></span></pre> </div> </div> </body> </html> |