sqlalchemy-commits Mailing List for SQLAlchemy (Page 368)
Brought to you by:
zzzeek
You can subscribe to this list here.
2006 |
Jan
|
Feb
(74) |
Mar
(167) |
Apr
(127) |
May
(190) |
Jun
(119) |
Jul
(77) |
Aug
(82) |
Sep
(84) |
Oct
(153) |
Nov
(45) |
Dec
(54) |
---|---|---|---|---|---|---|---|---|---|---|---|---|
2007 |
Jan
(109) |
Feb
(80) |
Mar
(110) |
Apr
(106) |
May
(92) |
Jun
(147) |
Jul
(288) |
Aug
(307) |
Sep
(108) |
Oct
(156) |
Nov
(147) |
Dec
(134) |
2008 |
Jan
(126) |
Feb
(91) |
Mar
(184) |
Apr
(208) |
May
(212) |
Jun
(54) |
Jul
(106) |
Aug
(80) |
Sep
(58) |
Oct
(80) |
Nov
(119) |
Dec
(220) |
2009 |
Jan
(202) |
Feb
(50) |
Mar
(70) |
Apr
(46) |
May
(80) |
Jun
(61) |
Jul
(146) |
Aug
(81) |
Sep
(71) |
Oct
(74) |
Nov
(66) |
Dec
(82) |
2010 |
Jan
(112) |
Feb
(169) |
Mar
(235) |
Apr
(77) |
May
(22) |
Jun
(31) |
Jul
(46) |
Aug
(46) |
Sep
(70) |
Oct
(36) |
Nov
(37) |
Dec
(79) |
2011 |
Jan
(46) |
Feb
(54) |
Mar
(65) |
Apr
(73) |
May
(31) |
Jun
(46) |
Jul
(40) |
Aug
(36) |
Sep
(44) |
Oct
(33) |
Nov
(19) |
Dec
(10) |
2012 |
Jan
(60) |
Feb
(37) |
Mar
(35) |
Apr
(28) |
May
(27) |
Jun
(50) |
Jul
(33) |
Aug
(88) |
Sep
(64) |
Oct
(74) |
Nov
(62) |
Dec
(41) |
2013 |
Jan
(30) |
Feb
(37) |
Mar
(39) |
Apr
(52) |
May
(40) |
Jun
(85) |
Jul
(74) |
Aug
(76) |
Sep
(26) |
Oct
(76) |
Nov
(63) |
Dec
(65) |
2014 |
Jan
(68) |
Feb
(82) |
Mar
(87) |
Apr
(24) |
May
(66) |
Jun
(34) |
Jul
(86) |
Aug
(75) |
Sep
(70) |
Oct
(41) |
Nov
(23) |
Dec
(53) |
2015 |
Jan
(40) |
Feb
(39) |
Mar
(69) |
Apr
(64) |
May
(40) |
Jun
(43) |
Jul
(20) |
Aug
(48) |
Sep
(38) |
Oct
(28) |
Nov
(34) |
Dec
(44) |
2016 |
Jan
(82) |
Feb
(49) |
Mar
(25) |
Apr
(21) |
May
(19) |
Jun
(46) |
Jul
(38) |
Aug
(21) |
Sep
(33) |
Oct
(44) |
Nov
(26) |
Dec
(10) |
2017 |
Jan
(52) |
Feb
(18) |
Mar
(61) |
Apr
(43) |
May
(57) |
Jun
(36) |
Jul
(37) |
Aug
|
Sep
|
Oct
|
Nov
|
Dec
|
From: <co...@sq...> - 2006-04-04 21:40:27
|
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head><style type="text/css"><!-- #msg dl { border: 1px #006 solid; background: #369; padding: 6px; color: #fff; } #msg dt { float: left; width: 6em; font-weight: bold; } #msg dt:after { content:':';} #msg dl, #msg dt, #msg ul, #msg li { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; } #msg dl a { font-weight: bold} #msg dl a:link { color:#fc3; } #msg dl a:active { color:#ff0; } #msg dl a:visited { color:#cc6; } h3 { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; font-weight: bold; } #msg pre { overflow: auto; background: #ffc; border: 1px #fc0 solid; padding: 6px; } #msg ul, pre { overflow: auto; } #patch { width: 100%; } #patch h4 {font-family: verdana,arial,helvetica,sans-serif;font-size:10pt;padding:8px;background:#369;color:#fff;margin:0;} #patch .propset h4, #patch .binary h4 {margin:0;} #patch pre {padding:0;line-height:1.2em;margin:0;} #patch .diff {width:100%;background:#eee;padding: 0 0 10px 0;overflow:auto;} #patch .propset .diff, #patch .binary .diff {padding:10px 0;} #patch span {display:block;padding:0 10px;} #patch .modfile, #patch .addfile, #patch .delfile, #patch .propset, #patch .binary, #patch .copfile {border:1px solid #ccc;margin:10px 0;} #patch ins {background:#dfd;text-decoration:none;display:block;padding:0 10px;} #patch del {background:#fdd;text-decoration:none;display:block;padding:0 10px;} #patch .lines, .info {color:#888;background:#fff;} --></style> <title>[1257] sqlalchemy/trunk/lib/sqlalchemy/sql.py: factored oid column into a consistent late-bound pattern, fixing [ticket:146]</title> </head> <body> <div id="msg"> <dl> <dt>Revision</dt> <dd>1257</dd> <dt>Author</dt> <dd>zzzeek</dd> <dt>Date</dt> <dd>2006-04-04 16:40:18 -0500 (Tue, 04 Apr 2006)</dd> </dl> <h3>Log Message</h3> <pre>factored oid column into a consistent late-bound pattern, fixing [ticket:146]</pre> <h3>Modified Paths</h3> <ul> <li><a href="#sqlalchemytrunklibsqlalchemysqlpy">sqlalchemy/trunk/lib/sqlalchemy/sql.py</a></li> </ul> </div> <div id="patch"> <h3>Diff</h3> <a id="sqlalchemytrunklibsqlalchemysqlpy"></a> <div class="modfile"><h4>Modified: sqlalchemy/trunk/lib/sqlalchemy/sql.py (1256 => 1257)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/trunk/lib/sqlalchemy/sql.py 2006-04-04 00:28:33 UTC (rev 1256) +++ sqlalchemy/trunk/lib/sqlalchemy/sql.py 2006-04-04 21:40:18 UTC (rev 1257) </span><span class="lines">@@ -611,6 +611,13 @@ </span><span class="cx"> return Join(self, right, isouter = True, *args, **kwargs) </span><span class="cx"> def alias(self, name=None): </span><span class="cx"> return Alias(self, name) </span><ins>+ def _locate_oid_column(self): + """subclasses override this to return an appropriate OID column""" + return None + def _get_oid_column(self): + if not hasattr(self, '_oid_column'): + self._oid_column = self._locate_oid_column() + return self._oid_column </ins><span class="cx"> def _get_col_by_original(self, column, raiseerr=True): </span><span class="cx"> """given a column which is a schema.Column object attached to a schema.Table object </span><span class="cx"> (i.e. an "original" column), return the Column object from this </span><span class="lines">@@ -635,6 +642,7 @@ </span><span class="cx"> primary_key = property(lambda s:s._get_exported_attribute('_primary_key')) </span><span class="cx"> foreign_keys = property(lambda s:s._get_exported_attribute('_foreign_keys')) </span><span class="cx"> original_columns = property(lambda s:s._get_exported_attribute('_orig_cols')) </span><ins>+ oid_column = property(_get_oid_column) </ins><span class="cx"> </span><span class="cx"> def _export_columns(self): </span><span class="cx"> if hasattr(self, '_columns'): </span><span class="lines">@@ -912,7 +920,8 @@ </span><span class="cx"> self.onclause = onclause </span><span class="cx"> self.isouter = isouter </span><span class="cx"> </span><del>- oid_column = property(lambda s:s.left.oid_column) </del><ins>+ def _locate_oid_column(self): + return self.left.oid_column </ins><span class="cx"> </span><span class="cx"> def _exportable_columns(self): </span><span class="cx"> return [c for c in self.left.columns] + [c for c in self.right.columns] </span><span class="lines">@@ -990,11 +999,13 @@ </span><span class="cx"> n = n[0:15] </span><span class="cx"> alias = n + "_" + hex(random.randint(0, 65535))[2:] </span><span class="cx"> self.name = alias </span><ins>+ + def _locate_oid_column(self): </ins><span class="cx"> if self.selectable.oid_column is not None: </span><del>- self.oid_column = self.selectable.oid_column._make_proxy(self) </del><ins>+ return self.selectable.oid_column._make_proxy(self) </ins><span class="cx"> else: </span><del>- self.oid_column = None - </del><ins>+ return None + </ins><span class="cx"> def _exportable_columns(self): </span><span class="cx"> return self.selectable.columns </span><span class="cx"> </span><span class="lines">@@ -1094,18 +1105,16 @@ </span><span class="cx"> def append_column(self, c): </span><span class="cx"> self._columns[c.text] = c </span><span class="cx"> c.table = self </span><del>- def _oid_col(self): </del><ins>+ def _locate_oid_column(self): </ins><span class="cx"> if self.engine is None: </span><span class="cx"> return None </span><del>- # OID remains a little hackish so far - if not hasattr(self, '_oid_column'): - if self.engine.oid_column_name() is not None: - self._oid_column = schema.Column(self.engine.oid_column_name(), sqltypes.Integer, hidden=True) - self._oid_column._set_parent(self) - self._orig_columns()[self._oid_column.original] = self._oid_column - else: - self._oid_column = None - return self._oid_column </del><ins>+ if self.engine.oid_column_name() is not None: + _oid_column = schema.Column(self.engine.oid_column_name(), sqltypes.Integer, hidden=True) + _oid_column._set_parent(self) + self._orig_columns()[_oid_column.original] = _oid_column + return _oid_column + else: + return None </ins><span class="cx"> def _orig_columns(self): </span><span class="cx"> try: </span><span class="cx"> return self._orig_cols </span><span class="lines">@@ -1119,7 +1128,6 @@ </span><span class="cx"> primary_key = property(lambda s:s._primary_key) </span><span class="cx"> foreign_keys = property(lambda s:s._foreign_keys) </span><span class="cx"> original_columns = property(_orig_columns) </span><del>- oid_column = property(_oid_col) </del><span class="cx"> </span><span class="cx"> def _clear(self): </span><span class="cx"> """clears all attributes on this TableClause so that new items can be added again""" </span><span class="lines">@@ -1193,13 +1201,15 @@ </span><span class="cx"> self.parens = kwargs.pop('parens', False) </span><span class="cx"> self.correlate = kwargs.pop('correlate', False) </span><span class="cx"> self.for_update = kwargs.pop('for_update', False) </span><del>- self.oid_column = selects[0].oid_column </del><span class="cx"> for s in self.selects: </span><span class="cx"> s.group_by(None) </span><span class="cx"> s.order_by(None) </span><span class="cx"> self.group_by(*kwargs.get('group_by', [None])) </span><span class="cx"> self.order_by(*kwargs.get('order_by', [None])) </span><span class="cx"> </span><ins>+ def _locate_oid_column(self): + return self.selects[0].oid_column + </ins><span class="cx"> def _exportable_columns(self): </span><span class="cx"> for s in self.selects: </span><span class="cx"> for c in s.c: </span><span class="lines">@@ -1235,7 +1245,6 @@ </span><span class="cx"> self.whereclause = None </span><span class="cx"> self.having = None </span><span class="cx"> self._engine = engine </span><del>- self.oid_column = None </del><span class="cx"> self.limit = limit </span><span class="cx"> self.offset = offset </span><span class="cx"> self.for_update = for_update </span><span class="lines">@@ -1345,11 +1354,15 @@ </span><span class="cx"> def append_from(self, fromclause): </span><span class="cx"> if type(fromclause) == str: </span><span class="cx"> fromclause = FromClause(from_name = fromclause) </span><del>- if self.oid_column is None and hasattr(fromclause, 'oid_column'): - self.oid_column = fromclause.oid_column </del><span class="cx"> fromclause.accept_visitor(self._correlator) </span><span class="cx"> fromclause._process_from_dict(self._froms, True) </span><del>- </del><ins>+ def _locate_oid_column(self): + for f in self._froms.values(): + oid = f.oid_column + if oid is not None: + return oid + else: + return None </ins><span class="cx"> def _get_froms(self): </span><span class="cx"> return [f for f in self._froms.values() if f is not self and (self._correlated is None or not self._correlated.has_key(f))] </span><span class="cx"> froms = property(lambda s: s._get_froms()) </span></span></pre> </div> </div> </body> </html> |
From: <co...@sq...> - 2006-04-04 00:29:18
|
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head><style type="text/css"><!-- #msg dl { border: 1px #006 solid; background: #369; padding: 6px; color: #fff; } #msg dt { float: left; width: 6em; font-weight: bold; } #msg dt:after { content:':';} #msg dl, #msg dt, #msg ul, #msg li { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; } #msg dl a { font-weight: bold} #msg dl a:link { color:#fc3; } #msg dl a:active { color:#ff0; } #msg dl a:visited { color:#cc6; } h3 { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; font-weight: bold; } #msg pre { overflow: auto; background: #ffc; border: 1px #fc0 solid; padding: 6px; } #msg ul, pre { overflow: auto; } #patch { width: 100%; } #patch h4 {font-family: verdana,arial,helvetica,sans-serif;font-size:10pt;padding:8px;background:#369;color:#fff;margin:0;} #patch .propset h4, #patch .binary h4 {margin:0;} #patch pre {padding:0;line-height:1.2em;margin:0;} #patch .diff {width:100%;background:#eee;padding: 0 0 10px 0;overflow:auto;} #patch .propset .diff, #patch .binary .diff {padding:10px 0;} #patch span {display:block;padding:0 10px;} #patch .modfile, #patch .addfile, #patch .delfile, #patch .propset, #patch .binary, #patch .copfile {border:1px solid #ccc;margin:10px 0;} #patch ins {background:#dfd;text-decoration:none;display:block;padding:0 10px;} #patch del {background:#fdd;text-decoration:none;display:block;padding:0 10px;} #patch .lines, .info {color:#888;background:#fff;} --></style> <title>[1256] sqlalchemy/trunk: Added cast() to allow use of cast(tbl.c.col as Numeric(4,2)) in select and where clauses.</title> </head> <body> <div id="msg"> <dl> <dt>Revision</dt> <dd>1256</dd> <dt>Author</dt> <dd>rtl</dd> <dt>Date</dt> <dd>2006-04-03 19:28:33 -0500 (Mon, 03 Apr 2006)</dd> </dl> <h3>Log Message</h3> <pre>Added cast() to allow use of cast(tbl.c.col as Numeric(4,2)) in select and where clauses. Unit tests for same.</pre> <h3>Modified Paths</h3> <ul> <li><a href="#sqlalchemytrunklibsqlalchemysqlpy">sqlalchemy/trunk/lib/sqlalchemy/sql.py</a></li> <li><a href="#sqlalchemytrunktestselectpy">sqlalchemy/trunk/test/select.py</a></li> </ul> <h3>Property Changed</h3> <ul> <li>sqlalchemy/trunk/</li> </ul> </div> <div id="patch"> <h3>Diff</h3> <a id="sqlalchemytrunk"></a> <div class="propset"><h4>Property changes: sqlalchemy/trunk</h4> <pre class="diff"><span> <span class="cx">Name: svk:merge </span><span class="cx"> - 8b31e7cb-c107-0410-ae80-e86de58984b7:/local/sqlalchemy:2426 </span><span class="cx"> + 8b31e7cb-c107-0410-ae80-e86de58984b7:/local/sqlalchemy:2709 </span></span></pre></div> <a id="sqlalchemytrunklibsqlalchemysqlpy"></a> <div class="modfile"><h4>Modified: sqlalchemy/trunk/lib/sqlalchemy/sql.py (1255 => 1256)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/trunk/lib/sqlalchemy/sql.py 2006-04-03 22:48:28 UTC (rev 1255) +++ sqlalchemy/trunk/lib/sqlalchemy/sql.py 2006-04-04 00:28:33 UTC (rev 1256) </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', 'null', 'desc', 'asc', 'outerjoin', 'alias', 'subquery', 'literal', 'bindparam', 'exists'] </del><ins>+__all__ = ['text', 'table', 'column', 'func', 'select', 'update', 'insert', 'delete', 'join', 'and_', 'or_', 'not_', 'between_', 'cast', '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">@@ -132,6 +132,25 @@ </span><span class="cx"> """ returns BETWEEN predicate clause (clausetest BETWEEN clauseleft AND clauseright) """ </span><span class="cx"> return BooleanExpression(ctest, and_(cleft, cright), 'BETWEEN') </span><span class="cx"> </span><ins>+def cast(clause, totype, **kwargs): + """ returns CAST function CAST(clause AS totype) + Use with a sqlalchemy.types.TypeEngine object, i.e + cast(table.c.unit_price * table.c.qty, Numeric(10,4)) + or + cast(table.c.timestamp, DATE) + """ + engine = kwargs.get('engine', None) + if engine is None: + engine = getattr(clause, 'engine', None) + if engine is not None: + totype_desc = engine.type_descriptor(totype) + # handle non-column clauses (e.g. cast(1234, TEXT) + if not hasattr(clause, 'label'): + clause = literal(clause) + return Function('CAST', clause.label(totype_desc.get_col_spec()), type=totype, **kwargs) + else: + raise InvalidRequestError("No engine available, cannot generate cast for " + str(clause) + " to type " + str(totype)) + </ins><span class="cx"> def exists(*args, **params): </span><span class="cx"> params['correlate'] = True </span><span class="cx"> s = select(*args, **params) </span></span></pre></div> <a id="sqlalchemytrunktestselectpy"></a> <div class="modfile"><h4>Modified: sqlalchemy/trunk/test/select.py (1255 => 1256)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/trunk/test/select.py 2006-04-03 22:48:28 UTC (rev 1255) +++ sqlalchemy/trunk/test/select.py 2006-04-04 00:28:33 UTC (rev 1256) </span><span class="lines">@@ -4,6 +4,7 @@ </span><span class="cx"> import sqlalchemy.databases.postgres as postgres </span><span class="cx"> import sqlalchemy.databases.oracle as oracle </span><span class="cx"> import sqlalchemy.databases.sqlite as sqlite </span><ins>+import sqlalchemy.databases.mysql as mysql </ins><span class="cx"> </span><span class="cx"> db = ansisql.engine() </span><span class="cx"> #db = create_engine('mssql') </span><span class="lines">@@ -532,6 +533,34 @@ </span><span class="cx"> </span><span class="cx"> self.runtest(table1.select(table1.c.name=='jack'), "SELECT mytable.myid, mytable.name, mytable.description FROM mytable WHERE mytable.myid = :mytable_myid AND mytable.name = :mytable_name", params={'myid':'3', 'name':'fred'}) </span><span class="cx"> </span><ins>+ def testcast(self): + tbl = table('casttest', + Column('id', Integer), + Column('v1', Float), + Column('v2', Float), + Column('ts', TIMESTAMP), + ) + + def check_results(engine, expected_results, literal): + self.assertEqual(len(expected_results), 5, 'Incorrect number of expected results') + self.assertEqual(str(cast(tbl.c.v1, Numeric, engine=engine)), 'CAST(casttest.v1 AS %s)' %expected_results[0]) + self.assertEqual(str(cast(tbl.c.v1, Numeric(12, 9), engine=engine)), 'CAST(casttest.v1 AS %s)' %expected_results[1]) + self.assertEqual(str(cast(tbl.c.ts, Date, engine=engine)), 'CAST(casttest.ts AS %s)' %expected_results[2]) + self.assertEqual(str(cast(1234, TEXT, engine=engine)), 'CAST(%s AS %s)' %(literal, expected_results[3])) + self.assertEqual(str(cast('test', String(20), engine=engine)), 'CAST(%s AS %s)' %(literal, expected_results[4])) + + # first test with Postgres engine + check_results(postgres.engine({}), ['NUMERIC(10, 2)', 'NUMERIC(12, 9)', 'DATE', 'TEXT', 'VARCHAR(20)'], '%(literal)s') + + # then the Oracle engine + check_results(oracle.engine({}, use_ansi = False), ['NUMERIC(10, 2)', 'NUMERIC(12, 9)', 'DATE', 'CLOB', 'VARCHAR(20)'], ':literal') + + # then the sqlite engine + check_results(sqlite.engine({}), ['NUMERIC(10, 2)', 'NUMERIC(12, 9)', 'DATE', 'TEXT', 'VARCHAR(20)'], '?') + + # and the MySQL engine + check_results(mysql.engine({}), ['NUMERIC(10, 2)', 'NUMERIC(12, 9)', 'DATE', 'TEXT', 'VARCHAR(20)'], '%s') + </ins><span class="cx"> class CRUDTest(SQLTest): </span><span class="cx"> def testinsert(self): </span><span class="cx"> # generic insert, will create bind params for all columns </span></span></pre> </div> </div> </body> </html> |
From: <co...@sq...> - 2006-04-03 22:48:37
|
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head><style type="text/css"><!-- #msg dl { border: 1px #006 solid; background: #369; padding: 6px; color: #fff; } #msg dt { float: left; width: 6em; font-weight: bold; } #msg dt:after { content:':';} #msg dl, #msg dt, #msg ul, #msg li { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; } #msg dl a { font-weight: bold} #msg dl a:link { color:#fc3; } #msg dl a:active { color:#ff0; } #msg dl a:visited { color:#cc6; } h3 { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; font-weight: bold; } #msg pre { overflow: auto; background: #ffc; border: 1px #fc0 solid; padding: 6px; } #msg ul, pre { overflow: auto; } #patch { width: 100%; } #patch h4 {font-family: verdana,arial,helvetica,sans-serif;font-size:10pt;padding:8px;background:#369;color:#fff;margin:0;} #patch .propset h4, #patch .binary h4 {margin:0;} #patch pre {padding:0;line-height:1.2em;margin:0;} #patch .diff {width:100%;background:#eee;padding: 0 0 10px 0;overflow:auto;} #patch .propset .diff, #patch .binary .diff {padding:10px 0;} #patch span {display:block;padding:0 10px;} #patch .modfile, #patch .addfile, #patch .delfile, #patch .propset, #patch .binary, #patch .copfile {border:1px solid #ccc;margin:10px 0;} #patch ins {background:#dfd;text-decoration:none;display:block;padding:0 10px;} #patch del {background:#fdd;text-decoration:none;display:block;padding:0 10px;} #patch .lines, .info {color:#888;background:#fff;} --></style> <title>[1255] sqlalchemy/trunk/examples/polymorph/polymorph2.py: put proper return type</title> </head> <body> <div id="msg"> <dl> <dt>Revision</dt> <dd>1255</dd> <dt>Author</dt> <dd>zzzeek</dd> <dt>Date</dt> <dd>2006-04-03 17:48:28 -0500 (Mon, 03 Apr 2006)</dd> </dl> <h3>Log Message</h3> <pre>put proper return type</pre> <h3>Modified 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="sqlalchemytrunkexamplespolymorphpolymorph2py"></a> <div class="modfile"><h4>Modified: sqlalchemy/trunk/examples/polymorph/polymorph2.py (1254 => 1255)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/trunk/examples/polymorph/polymorph2.py 2006-04-03 22:04:08 UTC (rev 1254) +++ sqlalchemy/trunk/examples/polymorph/polymorph2.py 2006-04-03 22:48:28 UTC (rev 1255) </span><span class="lines">@@ -94,7 +94,7 @@ </span><span class="cx"> Manager.mapper.populate_instance(instance, row, identitykey, imap, isnew, frommapper=mapper) </span><span class="cx"> return False </span><span class="cx"> else: </span><del>- return True </del><ins>+ return sqlalchemy.mapping.EXT_PASS </ins><span class="cx"> </span><span class="cx"> people_mapper = mapper(Person, person_join, extension=PersonLoader()) </span><span class="cx"> </span></span></pre> </div> </div> </body> </html> |
From: <co...@sq...> - 2006-04-03 22:04:21
|
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head><style type="text/css"><!-- #msg dl { border: 1px #006 solid; background: #369; padding: 6px; color: #fff; } #msg dt { float: left; width: 6em; font-weight: bold; } #msg dt:after { content:':';} #msg dl, #msg dt, #msg ul, #msg li { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; } #msg dl a { font-weight: bold} #msg dl a:link { color:#fc3; } #msg dl a:active { color:#ff0; } #msg dl a:visited { color:#cc6; } h3 { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; font-weight: bold; } #msg pre { overflow: auto; background: #ffc; border: 1px #fc0 solid; padding: 6px; } #msg ul, pre { overflow: auto; } #patch { width: 100%; } #patch h4 {font-family: verdana,arial,helvetica,sans-serif;font-size:10pt;padding:8px;background:#369;color:#fff;margin:0;} #patch .propset h4, #patch .binary h4 {margin:0;} #patch pre {padding:0;line-height:1.2em;margin:0;} #patch .diff {width:100%;background:#eee;padding: 0 0 10px 0;overflow:auto;} #patch .propset .diff, #patch .binary .diff {padding:10px 0;} #patch span {display:block;padding:0 10px;} #patch .modfile, #patch .addfile, #patch .delfile, #patch .propset, #patch .binary, #patch .copfile {border:1px solid #ccc;margin:10px 0;} #patch ins {background:#dfd;text-decoration:none;display:block;padding:0 10px;} #patch del {background:#fdd;text-decoration:none;display:block;padding:0 10px;} #patch .lines, .info {color:#888;background:#fff;} --></style> <title>[1254] sqlalchemy/trunk/lib/sqlalchemy/databases/mssql.py: adjustments to auto-table-aliasing logic (R.</title> </head> <body> <div id="msg"> <dl> <dt>Revision</dt> <dd>1254</dd> <dt>Author</dt> <dd>zzzeek</dd> <dt>Date</dt> <dd>2006-04-03 17:04:08 -0500 (Mon, 03 Apr 2006)</dd> </dl> <h3>Log Message</h3> <pre>adjustments to auto-table-aliasing logic (R. Morrison)</pre> <h3>Modified Paths</h3> <ul> <li><a href="#sqlalchemytrunklibsqlalchemydatabasesmssqlpy">sqlalchemy/trunk/lib/sqlalchemy/databases/mssql.py</a></li> </ul> </div> <div id="patch"> <h3>Diff</h3> <a id="sqlalchemytrunklibsqlalchemydatabasesmssqlpy"></a> <div class="modfile"><h4>Modified: sqlalchemy/trunk/lib/sqlalchemy/databases/mssql.py (1253 => 1254)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/trunk/lib/sqlalchemy/databases/mssql.py 2006-04-03 22:01:35 UTC (rev 1253) +++ sqlalchemy/trunk/lib/sqlalchemy/databases/mssql.py 2006-04-03 22:04:08 UTC (rev 1254) </span><span class="lines">@@ -430,9 +430,7 @@ </span><span class="cx"> </span><span class="cx"> def visit_table(self, table): </span><span class="cx"> # alias schema-qualified tables </span><del>- if self.tablealiases.has_key(table): - super(MSSQLCompiler, self).visit_table(table) - elif getattr(table, 'schema', None) is not None: </del><ins>+ if getattr(table, 'schema', None) is not None and not self.tablealiases.has_key(table): </ins><span class="cx"> alias = table.alias() </span><span class="cx"> self.tablealiases[table] = alias </span><span class="cx"> alias.accept_visitor(self) </span><span class="lines">@@ -441,7 +439,9 @@ </span><span class="cx"> c.accept_visitor(self) </span><span class="cx"> self.tablealiases[alias] = self.froms[table] </span><span class="cx"> self.froms[table] = self.froms[alias] </span><del>- </del><ins>+ else: + super(MSSQLCompiler, self).visit_table(table) + </ins><span class="cx"> def visit_alias(self, alias): </span><span class="cx"> # translate for schema-qualified table aliases </span><span class="cx"> if self.froms.has_key(('alias', alias.original)): </span></span></pre> </div> </div> </body> </html> |
From: <co...@sq...> - 2006-04-03 22:01:48
|
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head><style type="text/css"><!-- #msg dl { border: 1px #006 solid; background: #369; padding: 6px; color: #fff; } #msg dt { float: left; width: 6em; font-weight: bold; } #msg dt:after { content:':';} #msg dl, #msg dt, #msg ul, #msg li { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; } #msg dl a { font-weight: bold} #msg dl a:link { color:#fc3; } #msg dl a:active { color:#ff0; } #msg dl a:visited { color:#cc6; } h3 { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; font-weight: bold; } #msg pre { overflow: auto; background: #ffc; border: 1px #fc0 solid; padding: 6px; } #msg ul, pre { overflow: auto; } #patch { width: 100%; } #patch h4 {font-family: verdana,arial,helvetica,sans-serif;font-size:10pt;padding:8px;background:#369;color:#fff;margin:0;} #patch .propset h4, #patch .binary h4 {margin:0;} #patch pre {padding:0;line-height:1.2em;margin:0;} #patch .diff {width:100%;background:#eee;padding: 0 0 10px 0;overflow:auto;} #patch .propset .diff, #patch .binary .diff {padding:10px 0;} #patch span {display:block;padding:0 10px;} #patch .modfile, #patch .addfile, #patch .delfile, #patch .propset, #patch .binary, #patch .copfile {border:1px solid #ccc;margin:10px 0;} #patch ins {background:#dfd;text-decoration:none;display:block;padding:0 10px;} #patch del {background:#fdd;text-decoration:none;display:block;padding:0 10px;} #patch .lines, .info {color:#888;background:#fff;} --></style> <title>[1253] sqlalchemy/trunk: added a unit test for the "version_id" keyword argument, which passes based on previous changes to Mapper</title> </head> <body> <div id="msg"> <dl> <dt>Revision</dt> <dd>1253</dd> <dt>Author</dt> <dd>zzzeek</dd> <dt>Date</dt> <dd>2006-04-03 17:01:35 -0500 (Mon, 03 Apr 2006)</dd> </dl> <h3>Log Message</h3> <pre>added a unit test for the "version_id" keyword argument, which passes based on previous changes to Mapper</pre> <h3>Modified Paths</h3> <ul> <li><a href="#sqlalchemytrunkCHANGES">sqlalchemy/trunk/CHANGES</a></li> <li><a href="#sqlalchemytrunktestobjectstorepy">sqlalchemy/trunk/test/objectstore.py</a></li> </ul> </div> <div id="patch"> <h3>Diff</h3> <a id="sqlalchemytrunkCHANGES"></a> <div class="modfile"><h4>Modified: sqlalchemy/trunk/CHANGES (1252 => 1253)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/trunk/CHANGES 2006-04-03 21:46:00 UTC (rev 1252) +++ sqlalchemy/trunk/CHANGES 2006-04-03 22:01:35 UTC (rev 1253) </span><span class="lines">@@ -1,4 +1,10 @@ </span><span class="cx"> 0.1.6 </span><ins>+- added 'version_id' keyword argument to mapper. this keyword should reference a +Column object with type Integer, preferably non-nullable, which will be used on +the mapped table to track version numbers. this number is incremented on each +save operation and is specifed in the UPDATE/DELETE conditions so that it +factors into the returned row count, which results in a ConcurrencyError if the +value received is not the expected count. </ins><span class="cx"> - added 'entity_name' keyword argument to mapper. a mapper is now associated </span><span class="cx"> with a class via the class object as well as an optional entity_name parameter, </span><span class="cx"> which is a string defaulting to None. any number of primary mappers can be </span></span></pre></div> <a id="sqlalchemytrunktestobjectstorepy"></a> <div class="modfile"><h4>Modified: sqlalchemy/trunk/test/objectstore.py (1252 => 1253)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/trunk/test/objectstore.py 2006-04-03 21:46:00 UTC (rev 1252) +++ sqlalchemy/trunk/test/objectstore.py 2006-04-03 22:01:35 UTC (rev 1253) </span><span class="lines">@@ -169,7 +169,61 @@ </span><span class="cx"> objectstore.clear() </span><span class="cx"> self.assert_(m.get(8).user_name == name2) </span><span class="cx"> self.assert_(m.get(7).user_name != name1) </span><ins>+ +class VersioningTest(AssertMixin): + def setUpAll(self): + global version_table + version_table = Table('version_test', db, + Column('id', Integer, primary_key=True), + Column('version_id', Integer, nullable=False), + Column('value', String(40), nullable=False) + ).create() + def tearDownAll(self): + version_table.drop() + def tearDown(self): + version_table.delete().execute() + objectstore.clear() + clear_mappers() </ins><span class="cx"> </span><ins>+ def testbasic(self): + class Foo(object):pass + assign_mapper(Foo, version_table, version_id_col=version_table.c.version_id) + f1 =Foo(value='f1') + f2 = Foo(value='f2') + objectstore.commit() + + f1.value='f1rev2' + objectstore.commit() + s = objectstore.Session() + f1_s = Foo.mapper.using(s).get(f1.id) + f1_s.value='f1rev3' + s.commit() + + f1.value='f1rev3mine' + success = False + try: + # a concurrent session has modified this, should throw + # an exception + objectstore.commit() + except SQLAlchemyError: + success = True + assert success + + objectstore.clear() + f1 = Foo.mapper.get(f1.id) + f2 = Foo.mapper.get(f2.id) + + f1_s.value='f1rev4' + s.commit() + + objectstore.delete(f1, f2) + success = False + try: + objectstore.commit() + except SQLAlchemyError: + success = True + assert success + </ins><span class="cx"> class UnicodeTest(AssertMixin): </span><span class="cx"> def setUpAll(self): </span><span class="cx"> global uni_table </span></span></pre> </div> </div> </body> </html> |
From: <co...@sq...> - 2006-04-03 21:46:16
|
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head><style type="text/css"><!-- #msg dl { border: 1px #006 solid; background: #369; padding: 6px; color: #fff; } #msg dt { float: left; width: 6em; font-weight: bold; } #msg dt:after { content:':';} #msg dl, #msg dt, #msg ul, #msg li { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; } #msg dl a { font-weight: bold} #msg dl a:link { color:#fc3; } #msg dl a:active { color:#ff0; } #msg dl a:visited { color:#cc6; } h3 { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; font-weight: bold; } #msg pre { overflow: auto; background: #ffc; border: 1px #fc0 solid; padding: 6px; } #msg ul, pre { overflow: auto; } #patch { width: 100%; } #patch h4 {font-family: verdana,arial,helvetica,sans-serif;font-size:10pt;padding:8px;background:#369;color:#fff;margin:0;} #patch .propset h4, #patch .binary h4 {margin:0;} #patch pre {padding:0;line-height:1.2em;margin:0;} #patch .diff {width:100%;background:#eee;padding: 0 0 10px 0;overflow:auto;} #patch .propset .diff, #patch .binary .diff {padding:10px 0;} #patch span {display:block;padding:0 10px;} #patch .modfile, #patch .addfile, #patch .delfile, #patch .propset, #patch .binary, #patch .copfile {border:1px solid #ccc;margin:10px 0;} #patch ins {background:#dfd;text-decoration:none;display:block;padding:0 10px;} #patch del {background:#fdd;text-decoration:none;display:block;padding:0 10px;} #patch .lines, .info {color:#888;background:#fff;} --></style> <title>[1252] sqlalchemy/trunk/CHANGES: update</title> </head> <body> <div id="msg"> <dl> <dt>Revision</dt> <dd>1252</dd> <dt>Author</dt> <dd>zzzeek</dd> <dt>Date</dt> <dd>2006-04-03 16:46:00 -0500 (Mon, 03 Apr 2006)</dd> </dl> <h3>Log Message</h3> <pre>update</pre> <h3>Modified Paths</h3> <ul> <li><a href="#sqlalchemytrunkCHANGES">sqlalchemy/trunk/CHANGES</a></li> </ul> </div> <div id="patch"> <h3>Diff</h3> <a id="sqlalchemytrunkCHANGES"></a> <div class="modfile"><h4>Modified: sqlalchemy/trunk/CHANGES (1251 => 1252)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/trunk/CHANGES 2006-04-03 21:43:22 UTC (rev 1251) +++ sqlalchemy/trunk/CHANGES 2006-04-03 21:46:00 UTC (rev 1252) </span><span class="lines">@@ -1,3 +1,14 @@ </span><ins>+0.1.6 +- added 'entity_name' keyword argument to mapper. a mapper is now associated +with a class via the class object as well as an optional entity_name parameter, +which is a string defaulting to None. any number of primary mappers can be +created for a class, qualified by the entity name. instances of those classes +will issue all of their load and save operations through their +entity_name-qualified mapper, and maintain separate a identity in the identity +map for an otherwise equilvalent object. +- overhaul to the attributes system. code has been clarified, and also fixed to +support proper polymorphic behavior on object attributes. + </ins><span class="cx"> 0.1.5 </span><span class="cx"> - added SQLSession concept to SQLEngine. this object keeps track of retrieving a </span><span class="cx"> connection from the connection pool as well as an in-progress transaction. </span></span></pre> </div> </div> </body> </html> |
From: <co...@sq...> - 2006-04-03 21:43: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>[1251] sqlalchemy/trunk/test: added 'entity_name' keyword argument to mapper.</title> </head> <body> <div id="msg"> <dl> <dt>Revision</dt> <dd>1251</dd> <dt>Author</dt> <dd>zzzeek</dd> <dt>Date</dt> <dd>2006-04-03 16:43:22 -0500 (Mon, 03 Apr 2006)</dd> </dl> <h3>Log Message</h3> <pre>added 'entity_name' keyword argument to mapper. a mapper is now associated with a class via the class object as well as the optional entity_name parameter, which is a string defaulting to None. any number of primary mappers can be created for a class, qualified by the entity name. instances of those classes will issue all of their load and save operations through their entity_name-qualified mapper, and maintain separate identity from an otherwise equilvalent object.</pre> <h3>Modified Paths</h3> <ul> <li><a href="#sqlalchemytrunklibsqlalchemyattributespy">sqlalchemy/trunk/lib/sqlalchemy/attributes.py</a></li> <li><a href="#sqlalchemytrunklibsqlalchemymappingmapperpy">sqlalchemy/trunk/lib/sqlalchemy/mapping/mapper.py</a></li> <li><a href="#sqlalchemytrunklibsqlalchemymappingobjectstorepy">sqlalchemy/trunk/lib/sqlalchemy/mapping/objectstore.py</a></li> <li><a href="#sqlalchemytrunklibsqlalchemymappingpropertiespy">sqlalchemy/trunk/lib/sqlalchemy/mapping/properties.py</a></li> <li><a href="#sqlalchemytrunktestalltestspy">sqlalchemy/trunk/test/alltests.py</a></li> </ul> <h3>Added Paths</h3> <ul> <li><a href="#sqlalchemytrunktestentitypy">sqlalchemy/trunk/test/entity.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 (1250 => 1251)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/trunk/lib/sqlalchemy/attributes.py 2006-04-03 21:04:16 UTC (rev 1250) +++ sqlalchemy/trunk/lib/sqlalchemy/attributes.py 2006-04-03 21:43:22 UTC (rev 1251) </span><span class="lines">@@ -72,6 +72,11 @@ </span><span class="cx"> h.append_nohistory(value) </span><span class="cx"> </span><span class="cx"> class ManagedAttribute(object): </span><ins>+ """base class for a "managed attribute", which is attached to individual instances + of a class mapped to the keyname of the property, inside of a dictionary which is + attached to the object via the propertyname "_managed_attributes". Attribute access + which occurs through the SmartProperty property object ultimately calls upon + ManagedAttribute objects associated with the instance via this dictionary.""" </ins><span class="cx"> def __init__(self, obj, key): </span><span class="cx"> self.__obj = weakref.ref(obj) </span><span class="cx"> self.key = key </span></span></pre></div> <a id="sqlalchemytrunklibsqlalchemymappingmapperpy"></a> <div class="modfile"><h4>Modified: sqlalchemy/trunk/lib/sqlalchemy/mapping/mapper.py (1250 => 1251)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/trunk/lib/sqlalchemy/mapping/mapper.py 2006-04-03 21:04:16 UTC (rev 1250) +++ sqlalchemy/trunk/lib/sqlalchemy/mapping/mapper.py 2006-04-03 21:43:22 UTC (rev 1251) </span><span class="lines">@@ -66,11 +66,12 @@ </span><span class="cx"> self.extension = ext </span><span class="cx"> </span><span class="cx"> self.class_ = class_ </span><ins>+ self.entity_name = entity_name + self.class_key = ClassKey(class_, entity_name) </ins><span class="cx"> self.is_primary = is_primary </span><span class="cx"> self.order_by = order_by </span><span class="cx"> self._options = {} </span><span class="cx"> self.always_refresh = always_refresh </span><del>- self.entity_name = entity_name </del><span class="cx"> self.version_id_col = version_id_col </span><span class="cx"> </span><span class="cx"> if not issubclass(class_, object): </span><span class="lines">@@ -208,12 +209,9 @@ </span><span class="cx"> for primary_key in self.pks_by_table[self.table]: </span><span class="cx"> self._get_clause.clauses.append(primary_key == sql.bindparam("pk_"+primary_key.key)) </span><span class="cx"> </span><del>- if not mapper_registry.has_key(self.class_) or self.is_primary or (inherits is not None and inherits._is_primary_mapper()): </del><ins>+ if not mapper_registry.has_key(self.class_key) or self.is_primary or (inherits is not None and inherits._is_primary_mapper()): </ins><span class="cx"> objectstore.global_attributes.reset_class_managed(self.class_) </span><span class="cx"> self._init_class() </span><del>- self.identitytable = self.primarytable - else: - self.identitytable = mapper_registry[self.class_].primarytable </del><span class="cx"> </span><span class="cx"> if inherits is not None: </span><span class="cx"> for key, prop in inherits.props.iteritems(): </span><span class="lines">@@ -251,41 +249,50 @@ </span><span class="cx"> prop.init(key, self) </span><span class="cx"> </span><span class="cx"> def __str__(self): </span><del>- return "Mapper|" + self.class_.__name__ + "|" + self.primarytable.name </del><ins>+ return "Mapper|" + self.class_.__name__ + "|" + (self.entity_name is not None and "/%s" % self.entity_name or "") + self.primarytable.name </ins><span class="cx"> </span><span class="cx"> def _is_primary_mapper(self): </span><del>- return mapper_registry.get(self.class_, None) is self </del><ins>+ """returns True if this mapper is the primary mapper for its class key (class + entity_name)""" + return mapper_registry.get(self.class_key, None) is self </ins><span class="cx"> </span><span class="cx"> def _primary_mapper(self): </span><del>- return mapper_registry[self.class_] - </del><ins>+ """returns the primary mapper corresponding to this mapper's class key (class + entity_name)""" + return mapper_registry[self.class_key] + + def is_assigned(self, instance): + """returns True if this mapper is the primary mapper for the given instance. this is dependent + not only on class assignment but the optional "entity_name" parameter as well.""" + return instance.__class__ is self.class_ and getattr(instance, '_entity_name', None) == self.entity_name + </ins><span class="cx"> def _init_class(self): </span><span class="cx"> """sets up our classes' overridden __init__ method, this mappers hash key as its </span><span class="cx"> '_mapper' property, and our columns as its 'c' property. if the class already had a </span><span class="cx"> mapper, the old __init__ method is kept the same.""" </span><del>- if not self.class_.__dict__.has_key('_mapper'): - oldinit = self.class_.__init__ - def init(self, *args, **kwargs): - # this gets the AttributeManager to do some pre-initialization, - # in order to save on KeyErrors later on - objectstore.global_attributes.init_attr(self) - - nohist = kwargs.pop('_mapper_nohistory', False) - session = kwargs.pop('_sa_session', objectstore.get_session()) - if not nohist: - # register new with the correct session, before the object's - # constructor is called, since further assignments within the - # constructor would otherwise bind it to whatever get_session() is. - session.register_new(self) - if oldinit is not None: - oldinit(self, *args, **kwargs) - # override oldinit, insuring that its not already one of our - # own modified inits - if oldinit is None or not hasattr(oldinit, '_sa_mapper_init'): - init._sa_mapper_init = True - self.class_.__init__ = init - mapper_registry[self.class_] = self - self.class_.c = self.c </del><ins>+ oldinit = self.class_.__init__ + def init(self, *args, **kwargs): + self._entity_name = kwargs.pop('_sa_entity_name', None) + + # this gets the AttributeManager to do some pre-initialization, + # in order to save on KeyErrors later on + objectstore.global_attributes.init_attr(self) + + nohist = kwargs.pop('_mapper_nohistory', False) + session = kwargs.pop('_sa_session', objectstore.get_session()) + if not nohist: + # register new with the correct session, before the object's + # constructor is called, since further assignments within the + # constructor would otherwise bind it to whatever get_session() is. + session.register_new(self) + if oldinit is not None: + oldinit(self, *args, **kwargs) + # override oldinit, insuring that its not already one of our + # own modified inits + if oldinit is None or not hasattr(oldinit, '_sa_mapper_init'): + init._sa_mapper_init = True + self.class_.__init__ = init + mapper_registry[self.class_key] = self + if self.entity_name is None: + self.class_.c = self.c </ins><span class="cx"> </span><span class="cx"> def set_property(self, key, prop): </span><span class="cx"> self.props[key] = prop </span><span class="lines">@@ -325,7 +332,7 @@ </span><span class="cx"> """returns an instance of the object based on the given identifier, or None </span><span class="cx"> if not found. The *ident argument is a </span><span class="cx"> list of primary key columns in the order of the table def's primary key columns.""" </span><del>- key = objectstore.get_id_key(ident, self.class_) </del><ins>+ key = objectstore.get_id_key(ident, self.class_, self.entity_name) </ins><span class="cx"> #print "key: " + repr(key) + " ident: " + repr(ident) </span><span class="cx"> return self._get(key, ident) </span><span class="cx"> </span><span class="lines">@@ -352,7 +359,7 @@ </span><span class="cx"> </span><span class="cx"> def identity_key(self, *primary_key): </span><span class="cx"> """returns the instance key for the given identity value. this is a global tracking object used by the objectstore, and is usually available off a mapped object as instance._instance_key.""" </span><del>- return objectstore.get_id_key(tuple(primary_key), self.class_) </del><ins>+ return objectstore.get_id_key(tuple(primary_key), self.class_, self.entity_name) </ins><span class="cx"> </span><span class="cx"> def instance_key(self, instance): </span><span class="cx"> """returns the instance key for the given instance. this is a global tracking object used by the objectstore, and is usually available off a mapped object as instance._instance_key.""" </span><span class="lines">@@ -847,7 +854,7 @@ </span><span class="cx"> return statement </span><span class="cx"> </span><span class="cx"> def _identity_key(self, row): </span><del>- return objectstore.get_row_key(row, self.class_, self.pks_by_table[self.table]) </del><ins>+ return objectstore.get_row_key(row, self.class_, self.pks_by_table[self.table], self.entity_name) </ins><span class="cx"> </span><span class="cx"> def _instance(self, row, imap, result = None, populate_existing = False): </span><span class="cx"> """pulls an object instance from the given row and appends it to the given result </span><span class="lines">@@ -886,7 +893,7 @@ </span><span class="cx"> # plugin point </span><span class="cx"> instance = self.extension.create_instance(self, row, imap, self.class_) </span><span class="cx"> if instance is EXT_PASS: </span><del>- instance = self.class_(_mapper_nohistory=True) </del><ins>+ instance = self.class_(_mapper_nohistory=True, _sa_entity_name=self.entity_name) </ins><span class="cx"> imap[identitykey] = instance </span><span class="cx"> isnew = True </span><span class="cx"> else: </span><span class="lines">@@ -1086,7 +1093,16 @@ </span><span class="cx"> if self.next is not None: </span><span class="cx"> self.next.before_delete(mapper, instance) </span><span class="cx"> </span><del>- </del><ins>+class ClassKey(object): + """keys a class and an entity name to a mapper, via the mapper_registry""" + def __init__(self, class_, entity_name): + self.class_ = class_ + self.entity_name = entity_name + def __hash__(self): + return hash((self.class_, self.entity_name)) + def __eq__(self, other): + return self.class_ is other.class_ and self.entity_name == other.entity_name + </ins><span class="cx"> def hash_key(obj): </span><span class="cx"> if obj is None: </span><span class="cx"> return 'None' </span><span class="lines">@@ -1100,11 +1116,14 @@ </span><span class="cx"> def object_mapper(object): </span><span class="cx"> """given an object, returns the primary Mapper associated with the object </span><span class="cx"> or the object's class.""" </span><del>- return class_mapper(object.__class__) </del><ins>+ try: + return mapper_registry[ClassKey(object.__class__, getattr(object, '_entity_name', None))] + except KeyError: + raise InvalidRequestError("Class '%s' entity name '%s' has no mapper associated with it" % (object.__class__.__name__, getattr(object, '_entity_name', None))) </ins><span class="cx"> </span><del>-def class_mapper(class_): - """given a class, returns the primary Mapper associated with the class.""" </del><ins>+def class_mapper(class_, entity_name=None): + """given a ClassKey, returns the primary Mapper associated with the key.""" </ins><span class="cx"> try: </span><del>- return mapper_registry[class_] </del><ins>+ return mapper_registry[ClassKey(class_, entity_name)] </ins><span class="cx"> except (KeyError, AttributeError): </span><del>- raise InvalidRequestError("Class '%s' has no mapper associated with it" % class_.__name__) </del><ins>+ raise InvalidRequestError("Class '%s' entity name '%s' has no mapper associated with it" % (class_.__name__, entity_name)) </ins></span></pre></div> <a id="sqlalchemytrunklibsqlalchemymappingobjectstorepy"></a> <div class="modfile"><h4>Modified: sqlalchemy/trunk/lib/sqlalchemy/mapping/objectstore.py (1250 => 1251)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/trunk/lib/sqlalchemy/mapping/objectstore.py 2006-04-03 21:04:16 UTC (rev 1250) +++ sqlalchemy/trunk/lib/sqlalchemy/mapping/objectstore.py 2006-04-03 21:43:22 UTC (rev 1251) </span><span class="lines">@@ -51,7 +51,7 @@ </span><span class="cx"> if self.__pushed_count == 0: </span><span class="cx"> for n in self.nest_on: </span><span class="cx"> n.pop_session() </span><del>- def get_id_key(ident, class_): </del><ins>+ def get_id_key(ident, class_, entity_name=None): </ins><span class="cx"> """returns an identity-map key for use in storing/retrieving an item from the identity </span><span class="cx"> map, given a tuple of the object's primary key values. </span><span class="cx"> </span><span class="lines">@@ -60,15 +60,12 @@ </span><span class="cx"> </span><span class="cx"> class_ - a reference to the object's class </span><span class="cx"> </span><del>- table - a Table object where the object's primary fields are stored. - - selectable - a Selectable object which represents all the object's column-based fields. - this Selectable may be synonymous with the table argument or can be a larger construct - containing that table. return value: a tuple object which is used as an identity key. """ - return (class_, tuple(ident)) </del><ins>+ entity_name - optional string name to further qualify the class + """ + return (class_, tuple(ident), entity_name) </ins><span class="cx"> get_id_key = staticmethod(get_id_key) </span><span class="cx"> </span><del>- def get_row_key(row, class_, primary_key): </del><ins>+ def get_row_key(row, class_, primary_key, entity_name=None): </ins><span class="cx"> """returns an identity-map key for use in storing/retrieving an item from the identity </span><span class="cx"> map, given a result set row. </span><span class="cx"> </span><span class="lines">@@ -77,13 +74,12 @@ </span><span class="cx"> </span><span class="cx"> class_ - a reference to the object's class </span><span class="cx"> </span><del>- table - a Table object where the object's primary fields are stored. - - selectable - a Selectable object which represents all the object's column-based fields. - this Selectable may be synonymous with the table argument or can be a larger construct - containing that table. return value: a tuple object which is used as an identity key. </del><ins>+ primary_key - a list of column objects that will target the primary key values + in the given row. + + entity_name - optional string name to further qualify the class </ins><span class="cx"> """ </span><del>- return (class_, tuple([row[column] for column in primary_key])) </del><ins>+ return (class_, tuple([row[column] for column in primary_key]), entity_name) </ins><span class="cx"> get_row_key = staticmethod(get_row_key) </span><span class="cx"> </span><span class="cx"> class SessionTrans(object): </span><span class="lines">@@ -222,11 +218,11 @@ </span><span class="cx"> u.register_new(instance) </span><span class="cx"> return instance </span><span class="cx"> </span><del>-def get_id_key(ident, class_): - return Session.get_id_key(ident, class_) </del><ins>+def get_id_key(ident, class_, entity_name=None): + return Session.get_id_key(ident, class_, entity_name) </ins><span class="cx"> </span><del>-def get_row_key(row, class_, primary_key): - return Session.get_row_key(row, class_, primary_key) </del><ins>+def get_row_key(row, class_, primary_key, entity_name=None): + return Session.get_row_key(row, class_, primary_key, entity_name) </ins><span class="cx"> </span><span class="cx"> def begin(): </span><span class="cx"> """begins a new UnitOfWork transaction. the next commit will affect only </span></span></pre></div> <a id="sqlalchemytrunklibsqlalchemymappingpropertiespy"></a> <div class="modfile"><h4>Modified: sqlalchemy/trunk/lib/sqlalchemy/mapping/properties.py (1250 => 1251)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/trunk/lib/sqlalchemy/mapping/properties.py 2006-04-03 21:04:16 UTC (rev 1250) +++ sqlalchemy/trunk/lib/sqlalchemy/mapping/properties.py 2006-04-03 21:43:22 UTC (rev 1251) </span><span class="lines">@@ -590,6 +590,8 @@ </span><span class="cx"> objectstore.global_attributes.register_attribute(class_, key, uselist = self.uselist, deleteremoved = self.private, callable_=lambda i: self.setup_loader(i), extension=self.attributeext) </span><span class="cx"> </span><span class="cx"> def setup_loader(self, instance): </span><ins>+ if not self.parent.is_assigned(instance): + return object_mapper(instance).props[self.key].setup_loader(instance) </ins><span class="cx"> def lazyload(): </span><span class="cx"> params = {} </span><span class="cx"> allparams = True </span></span></pre></div> <a id="sqlalchemytrunktestalltestspy"></a> <div class="modfile"><h4>Modified: sqlalchemy/trunk/test/alltests.py (1250 => 1251)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/trunk/test/alltests.py 2006-04-03 21:04:16 UTC (rev 1250) +++ sqlalchemy/trunk/test/alltests.py 2006-04-03 21:43:22 UTC (rev 1251) </span><span class="lines">@@ -44,6 +44,7 @@ </span><span class="cx"> 'cycles', </span><span class="cx"> </span><span class="cx"> # more select/persistence, backrefs </span><ins>+ 'entity', </ins><span class="cx"> 'manytomany', </span><span class="cx"> 'onetoone', </span><span class="cx"> 'inheritance', </span></span></pre></div> <a id="sqlalchemytrunktestentitypy"></a> <div class="addfile"><h4>Added: sqlalchemy/trunk/test/entity.py (1250 => 1251)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/trunk/test/entity.py 2006-04-03 21:04:16 UTC (rev 1250) +++ sqlalchemy/trunk/test/entity.py 2006-04-03 21:43:22 UTC (rev 1251) </span><span class="lines">@@ -0,0 +1,130 @@ </span><ins>+from testbase import PersistTest, AssertMixin +import unittest +from sqlalchemy import * +import testbase + +from tables import * +import tables + +class EntityTest(AssertMixin): + """tests mappers that are constructed based on "entity names", which allows the same class + to have multiple primary mappers """ + def setUpAll(self): + global user1, user2, address1, address2 + db = testbase.db + user1 = Table('user1', db, + Column('user_id', Integer, Sequence('user1_id_seq'), primary_key=True), + Column('name', String(60), nullable=False) + ).create() + user2 = Table('user2', db, + Column('user_id', Integer, Sequence('user2_id_seq'), primary_key=True), + Column('name', String(60), nullable=False) + ).create() + address1 = Table('address1', db, + Column('address_id', Integer, Sequence('address1_id_seq'), primary_key=True), + Column('user_id', Integer, ForeignKey(user1.c.user_id), nullable=False), + Column('email', String(100), nullable=False) + ).create() + address2 = Table('address2', db, + Column('address_id', Integer, Sequence('address2_id_seq'), primary_key=True), + Column('user_id', Integer, ForeignKey(user2.c.user_id), nullable=False), + Column('email', String(100), nullable=False) + ).create() + def tearDownAll(self): + address1.drop() + address2.drop() + user1.drop() + user2.drop() + def tearDown(self): + address1.delete().execute() + address2.delete().execute() + user1.delete().execute() + user2.delete().execute() + objectstore.clear() + clear_mappers() + + def testbasic(self): + """tests a pair of one-to-many mapper structures, establishing that both + parent and child objects honor the "entity_name" attribute attached to the object + instances.""" + class User(object):pass + class Address(object):pass + + a1mapper = mapper(Address, address1, entity_name='address1') + a2mapper = mapper(Address, address2, entity_name='address2') + u1mapper = mapper(User, user1, entity_name='user1', properties ={ + 'addresses':relation(a1mapper) + }) + u2mapper =mapper(User, user2, entity_name='user2', properties={ + 'addresses':relation(a2mapper) + }) + + u1 = User(_sa_entity_name='user1') + u1.name = 'this is user 1' + a1 = Address(_sa_entity_name='address1') + a1.email='a1...@fo...' + u1.addresses.append(a1) + + u2 = User(_sa_entity_name='user2') + u2.name='this is user 2' + a2 = Address(_sa_entity_name='address2') + a2.email='a2...@fo...' + u2.addresses.append(a2) + + objectstore.commit() + assert user1.select().execute().fetchall() == [(u1.user_id, u1.name)] + assert user2.select().execute().fetchall() == [(u2.user_id, u2.name)] + assert address1.select().execute().fetchall() == [(u1.user_id, a1.user_id, 'a1...@fo...')] + assert address2.select().execute().fetchall() == [(u2.user_id, a2.user_id, 'a2...@fo...')] + + objectstore.clear() + u1list = u1mapper.select() + u2list = u2mapper.select() + assert len(u1list) == len(u2list) == 1 + assert u1list[0] is not u2list[0] + assert len(u1list[0].addresses) == len(u2list[0].addresses) == 1 + + def testpolymorphic(self): + """tests that entity_name can be used to have two kinds of relations on the same class.""" + class User(object):pass + class Address1(object):pass + class Address2(object):pass + + a1mapper = mapper(Address1, address1) + a2mapper = mapper(Address2, address2) + u1mapper = mapper(User, user1, entity_name='user1', properties ={ + 'addresses':relation(a1mapper) + }) + u2mapper =mapper(User, user2, entity_name='user2', properties={ + 'addresses':relation(a2mapper) + }) + + u1 = User(_sa_entity_name='user1') + u1.name = 'this is user 1' + a1 = Address1() + a1.email='a1...@fo...' + u1.addresses.append(a1) + + u2 = User(_sa_entity_name='user2') + u2.name='this is user 2' + a2 = Address2() + a2.email='a2...@fo...' + u2.addresses.append(a2) + + objectstore.commit() + assert user1.select().execute().fetchall() == [(u1.user_id, u1.name)] + assert user2.select().execute().fetchall() == [(u2.user_id, u2.name)] + assert address1.select().execute().fetchall() == [(u1.user_id, a1.user_id, 'a1...@fo...')] + assert address2.select().execute().fetchall() == [(u2.user_id, a2.user_id, 'a2...@fo...')] + + objectstore.clear() + u1list = u1mapper.select() + u2list = u2mapper.select() + assert len(u1list) == len(u2list) == 1 + assert u1list[0] is not u2list[0] + assert len(u1list[0].addresses) == len(u2list[0].addresses) == 1 + assert isinstance(u1list[0].addresses[0], Address1) + assert isinstance(u2list[0].addresses[0], Address2) + +if __name__ == "__main__": + testbase.main() </ins></span></pre> </div> </div> </body> </html> |
From: <co...@sq...> - 2006-04-03 21:04: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>[1250] sqlalchemy/trunk/test: attributes overhaul #2 - attribute manager now tracks class-level initializers strictly through the SmartPropery instances attached to the class, so that attributes retain their natural polymorphic behavior.</title> </head> <body> <div id="msg"> <dl> <dt>Revision</dt> <dd>1250</dd> <dt>Author</dt> <dd>zzzeek</dd> <dt>Date</dt> <dd>2006-04-03 16:04:16 -0500 (Mon, 03 Apr 2006)</dd> </dl> <h3>Log Message</h3> <pre>attributes overhaul #2 - attribute manager now tracks class-level initializers strictly through the SmartPropery instances attached to the class, so that attributes retain their natural polymorphic behavior. naming conventions migrating to "managed_attribute", simplifying codepaths.</pre> <h3>Modified Paths</h3> <ul> <li><a href="#sqlalchemytrunklibsqlalchemyattributespy">sqlalchemy/trunk/lib/sqlalchemy/attributes.py</a></li> <li><a href="#sqlalchemytrunklibsqlalchemymappingunitofworkpy">sqlalchemy/trunk/lib/sqlalchemy/mapping/unitofwork.py</a></li> <li><a href="#sqlalchemytrunktestattributespy">sqlalchemy/trunk/test/attributes.py</a></li> <li><a href="#sqlalchemytrunktestmapperpy">sqlalchemy/trunk/test/mapper.py</a></li> <li><a href="#sqlalchemytrunktestmasscreatepy">sqlalchemy/trunk/test/masscreate.py</a></li> <li><a href="#sqlalchemytrunktestobjectstorepy">sqlalchemy/trunk/test/objectstore.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 (1249 => 1250)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/trunk/lib/sqlalchemy/attributes.py 2006-04-03 17:49:41 UTC (rev 1249) +++ sqlalchemy/trunk/lib/sqlalchemy/attributes.py 2006-04-03 21:04:16 UTC (rev 1250) </span><span class="lines">@@ -37,10 +37,20 @@ </span><span class="cx"> create_prop method on AttributeManger, which can be overridden to provide </span><span class="cx"> subclasses of SmartProperty. </span><span class="cx"> """ </span><del>- def __init__(self, manager, key, uselist): </del><ins>+ def __init__(self, manager, key, uselist, callable_, **kwargs): </ins><span class="cx"> self.manager = manager </span><span class="cx"> self.key = key </span><span class="cx"> self.uselist = uselist </span><ins>+ self.callable_ = callable_ + self.kwargs = kwargs + def init(self, obj, attrhist=None): + """creates an appropriate ManagedAttribute for the given object and establishes + it with the object's list of managed attributes.""" + if self.callable_ is not None: + func = self.callable_(obj) + else: + func = None + return self.manager.create_managed_attribute(obj, self.key, self.uselist, callable_=func, attrdict=attrhist, **self.kwargs) </ins><span class="cx"> def __set__(self, obj, value): </span><span class="cx"> self.manager.set_attribute(obj, self.key, value) </span><span class="cx"> def __delete__(self, obj): </span><span class="lines">@@ -300,10 +310,11 @@ </span><span class="cx"> upon an attribute change of value.""" </span><span class="cx"> pass </span><span class="cx"> </span><del>- def create_prop(self, class_, key, uselist, **kwargs): </del><ins>+ def create_prop(self, class_, key, uselist, callable_, **kwargs): </ins><span class="cx"> """creates a scalar property object, defaulting to SmartProperty, which </span><span class="cx"> will communicate change events back to this AttributeManager.""" </span><del>- return SmartProperty(self, key, uselist) </del><ins>+ return SmartProperty(self, key, uselist, callable_, **kwargs) + </ins><span class="cx"> def create_list(self, obj, key, list_, **kwargs): </span><span class="cx"> """creates a history-aware list property, defaulting to a ListAttribute which </span><span class="cx"> is a subclass of HistoryArrayList.""" </span><span class="lines">@@ -365,22 +376,19 @@ </span><span class="cx"> # currently a no-op since the state of the object is attached to the object itself </span><span class="cx"> pass </span><span class="cx"> </span><del>- def create_history(self, obj, key, uselist, callable_=None, **kwargs): - """creates a new "history" container for a specific attribute on the given object. - this can be used to override a class-level attribute with something different, - such as a callable. """ - p = self.create_history_container(obj, key, uselist, callable_=callable_, **kwargs) - self.attribute_history(obj)[key] = p - return p </del><span class="cx"> </span><span class="cx"> def init_attr(self, obj): </span><del>- """sets up the _managed_attributes dictionary on an object. this happens anyway regardless - of this method being called, but saves on KeyErrors being thrown in get_history().""" </del><ins>+ """sets up the _managed_attributes dictionary on an object. this happens anyway + when a particular attribute is first accessed on the object regardless + of this method being called, however calling this first will result in an elimination of + AttributeError/KeyErrors that are thrown when get_unexec_history is called for the first + time for a particular key.""" </ins><span class="cx"> d = {} </span><span class="cx"> obj._managed_attributes = d </span><del>- cls_managed = self.class_managed(obj.__class__) - for value in cls_managed.values(): - value(obj, d).plain_init(d) </del><ins>+ for value in obj.__class__.__dict__.values(): + if not isinstance(value, SmartProperty): + continue + value.init(obj, attrhist=d).plain_init(d) </ins><span class="cx"> </span><span class="cx"> def get_unexec_history(self, obj, key): </span><span class="cx"> """returns the "history" container for the given attribute on the given object. </span><span class="lines">@@ -389,31 +397,35 @@ </span><span class="cx"> try: </span><span class="cx"> return obj._managed_attributes[key] </span><span class="cx"> except AttributeError, ae: </span><del>- return self.class_managed(obj.__class__)[key](obj) </del><ins>+ return getattr(obj.__class__, key).init(obj) </ins><span class="cx"> except KeyError, e: </span><del>- return self.class_managed(obj.__class__)[key](obj) </del><ins>+ return getattr(obj.__class__, key).init(obj) </ins><span class="cx"> </span><span class="cx"> def get_history(self, obj, key, **kwargs): </span><del>- """returns the "history" container, and calls its history() method, - which for a TriggeredAttribute will execute the underlying callable and return the - resulting ScalarAttribute or ListHistory object.""" </del><ins>+ """accesses the appropriate ManagedAttribute container and calls its history() method. + For a TriggeredAttribute this will execute the underlying callable and return the + resulting ScalarAttribute or ListAttribute object. For an existing ScalarAttribute + or ListAttribute, just returns the container.""" </ins><span class="cx"> return self.get_unexec_history(obj, key).history(**kwargs) </span><span class="cx"> </span><span class="cx"> def attribute_history(self, obj): </span><del>- """returns a dictionary of "history" containers corresponding to the given object. </del><ins>+ """returns a dictionary of ManagedAttribute containers corresponding to the given object. </ins><span class="cx"> this dictionary is attached to the object via the attribute '_managed_attributes'. </span><del>- If the dictionary does not exist, it will be created.""" </del><ins>+ If the dictionary does not exist, it will be created. If a 'trigger' has been placed on + this object via the trigger_history() method, it will first be executed.""" </ins><span class="cx"> try: </span><span class="cx"> return obj._managed_attributes </span><span class="cx"> except AttributeError: </span><ins>+ obj._managed_attributes = {} </ins><span class="cx"> trigger = obj.__dict__.pop('_managed_trigger', None) </span><span class="cx"> if trigger: </span><span class="cx"> trigger() </span><del>- attr = {} - obj._managed_attributes = attr - return attr </del><ins>+ return obj._managed_attributes </ins><span class="cx"> </span><span class="cx"> def trigger_history(self, obj, callable): </span><ins>+ """removes all ManagedAttribute instances from the given object and places the given callable + as an attribute-wide "trigger", which will execute upon the next attribute access, after + which the trigger is removed and the object re-initialized to receive new ManagedAttributes. """ </ins><span class="cx"> try: </span><span class="cx"> del obj._managed_attributes </span><span class="cx"> except KeyError: </span><span class="lines">@@ -440,62 +452,42 @@ </span><span class="cx"> except KeyError: </span><span class="cx"> pass </span><span class="cx"> </span><del>- def class_managed(self, class_): - """returns a dictionary of "history container definitions", which is attached to a - class. creates the dictionary if it doesnt exist.""" - try: - attr = class_._class_managed_attributes - except AttributeError: - attr = {} - class_._class_managed_attributes = attr - class_._attribute_manager = self - return attr - </del><span class="cx"> def reset_class_managed(self, class_): </span><del>- try: - attr = class_._class_managed_attributes - for key in attr.keys(): - delattr(class_, key) - del class_._class_managed_attributes - except AttributeError: - pass </del><ins>+ for value in class_.__dict__.values(): + if not isinstance(value, SmartProperty): + continue + delattr(class_, value.key) </ins><span class="cx"> </span><span class="cx"> def is_class_managed(self, class_, key): </span><del>- try: - return class_._class_managed_attributes.has_key(key) - except AttributeError: - return False - - def create_history_container(self, obj, key, uselist, callable_ = None, **kwargs): - """creates a new history container for the given attribute on the given object.""" </del><ins>+ return hasattr(class_, key) and isinstance(getattr(class_, key), SmartProperty) + + def create_managed_attribute(self, obj, key, uselist, callable_=None, attrdict=None, **kwargs): + """creates a new ManagedAttribute corresponding to the given attribute key on the + given object instance, and installs it in the attribute dictionary attached to the object.""" </ins><span class="cx"> if callable_ is not None: </span><del>- return self.create_callable(obj, key, callable_, uselist=uselist, **kwargs) </del><ins>+ prop = self.create_callable(obj, key, callable_, uselist=uselist, **kwargs) </ins><span class="cx"> elif not uselist: </span><del>- return ScalarAttribute(obj, key, **kwargs) </del><ins>+ prop = ScalarAttribute(obj, key, **kwargs) </ins><span class="cx"> else: </span><del>- return self.create_list(obj, key, None, **kwargs) - </del><ins>+ prop = self.create_list(obj, key, None, **kwargs) + if attrdict is None: + attrdict = self.attribute_history(obj) + attrdict[key] = prop + return prop + + # deprecated + create_history=create_managed_attribute + </ins><span class="cx"> def register_attribute(self, class_, key, uselist, callable_=None, **kwargs): </span><span class="cx"> """registers an attribute's behavior at the class level. This attribute </span><span class="cx"> can be scalar or list based, and also may have a callable unit that will be </span><del>- used to create the initial value. The definition for this attribute is - wrapped up into a callable which is then stored in the classes' - dictionary of "class managed" attributes. When instances of the class </del><ins>+ used to create the initial value (i.e. a lazy loader). The definition for this attribute is + wrapped up into a callable which is then stored in the corresponding + SmartProperty object attached to the class. When instances of the class </ins><span class="cx"> are created and the attribute first referenced, the callable is invoked with </span><del>- the new object instance as an argument to create the new history container. </del><ins>+ the new object instance as an argument to create the new ManagedAttribute. </ins><span class="cx"> Extra keyword arguments can be sent which </span><del>- will be passed along to newly created history containers.""" - def createprop(obj, attrhist=None): - if callable_ is not None: - func = callable_(obj) - else: - func = None - p = self.create_history_container(obj, key, uselist, callable_=func, **kwargs) - if attrhist is None: - attrhist = self.attribute_history(obj) - attrhist[key] = p - return p - - self.class_managed(class_)[key] = createprop - setattr(class_, key, self.create_prop(class_, key, uselist)) </del><ins>+ will be passed along to newly created ManagedAttribute.""" + class_._attribute_manager = self + setattr(class_, key, self.create_prop(class_, key, uselist, callable_, **kwargs)) </ins><span class="cx"> </span></span></pre></div> <a id="sqlalchemytrunklibsqlalchemymappingunitofworkpy"></a> <div class="modfile"><h4>Modified: sqlalchemy/trunk/lib/sqlalchemy/mapping/unitofwork.py (1249 => 1250)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/trunk/lib/sqlalchemy/mapping/unitofwork.py 2006-04-03 17:49:41 UTC (rev 1249) +++ sqlalchemy/trunk/lib/sqlalchemy/mapping/unitofwork.py 2006-04-03 21:04:16 UTC (rev 1250) </span><span class="lines">@@ -66,8 +66,8 @@ </span><span class="cx"> else: </span><span class="cx"> get_session(obj).register_new(obj) </span><span class="cx"> </span><del>- def create_prop(self, class_, key, uselist, **kwargs): - return UOWProperty(class_, self, key, uselist) </del><ins>+ def create_prop(self, class_, key, uselist, callable_, **kwargs): + return UOWProperty(class_, self, key, uselist, callable_, **kwargs) </ins><span class="cx"> </span><span class="cx"> def create_list(self, obj, key, list_, **kwargs): </span><span class="cx"> return UOWListElement(obj, key, list_, **kwargs) </span></span></pre></div> <a id="sqlalchemytrunktestattributespy"></a> <div class="modfile"><h4>Modified: sqlalchemy/trunk/test/attributes.py (1249 => 1250)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/trunk/test/attributes.py 2006-04-03 17:49:41 UTC (rev 1249) +++ sqlalchemy/trunk/test/attributes.py 2006-04-03 21:04:16 UTC (rev 1250) </span><span class="lines">@@ -131,6 +131,30 @@ </span><span class="cx"> </span><span class="cx"> j.port = None </span><span class="cx"> self.assert_(p.jack is None) </span><ins>+ + def testinheritance(self): + """tests that attributes are polymorphic""" + class Foo(object):pass + class Bar(Foo):pass </ins><span class="cx"> </span><ins>+ manager = attributes.AttributeManager() + + def func1(): + return "this is the foo attr" + def func2(): + return "this is the bar attr" + def func3(): + return "this is the shared attr" + manager.register_attribute(Foo, 'element', uselist=False, callable_=lambda o:func1) + manager.register_attribute(Foo, 'element2', uselist=False, callable_=lambda o:func3) + manager.register_attribute(Bar, 'element', uselist=False, callable_=lambda o:func2) + + x = Foo() + y = Bar() + assert x.element == 'this is the foo attr' + assert y.element == 'this is the bar attr' + assert x.element2 == 'this is the shared attr' + assert y.element2 == 'this is the shared attr' + </ins><span class="cx"> if __name__ == "__main__": </span><span class="cx"> unittest.main() </span></span></pre></div> <a id="sqlalchemytrunktestmapperpy"></a> <div class="modfile"><h4>Modified: sqlalchemy/trunk/test/mapper.py (1249 => 1250)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/trunk/test/mapper.py 2006-04-03 17:49:41 UTC (rev 1249) +++ sqlalchemy/trunk/test/mapper.py 2006-04-03 21:04:16 UTC (rev 1250) </span><span class="lines">@@ -125,12 +125,15 @@ </span><span class="cx"> self.assert_sql_count(db, go, 1) </span><span class="cx"> </span><span class="cx"> def testexpire(self): </span><del>- m = mapper(User, users, properties={'addresses':relation(mapper(Address, addresses))}) </del><ins>+ m = mapper(User, users, properties={'addresses':relation(mapper(Address, addresses), lazy=False)}) </ins><span class="cx"> u = m.get(7) </span><ins>+ assert(len(u.addresses) == 1) </ins><span class="cx"> u.user_name = 'foo' </span><ins>+ del u.addresses[0] </ins><span class="cx"> objectstore.expire(u) </span><span class="cx"> # test plain expire </span><span class="cx"> self.assert_(u.user_name =='jack') </span><ins>+ self.assert_(len(u.addresses) == 1) </ins><span class="cx"> </span><span class="cx"> # we're changing the database here, so if this test fails in the middle, </span><span class="cx"> # it'll screw up the other tests which are hardcoded to 7/'jack' </span></span></pre></div> <a id="sqlalchemytrunktestmasscreatepy"></a> <div class="modfile"><h4>Modified: sqlalchemy/trunk/test/masscreate.py (1249 => 1250)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/trunk/test/masscreate.py 2006-04-03 17:49:41 UTC (rev 1249) +++ sqlalchemy/trunk/test/masscreate.py 2006-04-03 21:04:16 UTC (rev 1250) </span><span class="lines">@@ -35,4 +35,4 @@ </span><span class="cx"> u.addresses.append(u) </span><span class="cx"> </span><span class="cx"> total = time.time() - now </span><del>-print "Total time", total </del><span class="cx">\ No newline at end of file </span><ins>+print "Total time", total </ins></span></pre></div> <a id="sqlalchemytrunktestobjectstorepy"></a> <div class="modfile"><h4>Modified: sqlalchemy/trunk/test/objectstore.py (1249 => 1250)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/trunk/test/objectstore.py 2006-04-03 17:49:41 UTC (rev 1249) +++ sqlalchemy/trunk/test/objectstore.py 2006-04-03 21:04:16 UTC (rev 1250) </span><span class="lines">@@ -1078,7 +1078,6 @@ </span><span class="cx"> a.user = u </span><span class="cx"> objectstore.commit() </span><span class="cx"> print repr(u.addresses) </span><del>- print repr(u.addresses) </del><span class="cx"> x = False </span><span class="cx"> try: </span><span class="cx"> u.addresses.append('hi') </span><span class="lines">@@ -1087,7 +1086,7 @@ </span><span class="cx"> pass </span><span class="cx"> </span><span class="cx"> if x: </span><del>- self.assert_(False, "User addresses element should be read-only") </del><ins>+ self.assert_(False, "User addresses element should be scalar based") </ins><span class="cx"> </span><span class="cx"> objectstore.delete(u) </span><span class="cx"> objectstore.commit() </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>[1249] sqlalchemy/trunk/lib/sqlalchemy/sql.py: further order_by: order_by() with no arguments should not affect the current order_by clause (same with group_by)</title> </head> <body> <div id="msg"> <dl> <dt>Revision</dt> <dd>1249</dd> <dt>Author</dt> <dd>zzzeek</dd> <dt>Date</dt> <dd>2006-04-03 12:49:41 -0500 (Mon, 03 Apr 2006)</dd> </dl> <h3>Log Message</h3> <pre>further order_by: order_by() with no arguments should not affect the current order_by clause (same with group_by)</pre> <h3>Modified Paths</h3> <ul> <li><a href="#sqlalchemytrunklibsqlalchemysqlpy">sqlalchemy/trunk/lib/sqlalchemy/sql.py</a></li> </ul> </div> <div id="patch"> <h3>Diff</h3> <a id="sqlalchemytrunklibsqlalchemysqlpy"></a> <div class="modfile"><h4>Modified: sqlalchemy/trunk/lib/sqlalchemy/sql.py (1248 => 1249)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/trunk/lib/sqlalchemy/sql.py 2006-04-03 17:46:08 UTC (rev 1248) +++ sqlalchemy/trunk/lib/sqlalchemy/sql.py 2006-04-03 17:49:41 UTC (rev 1249) </span><span class="lines">@@ -1145,14 +1145,14 @@ </span><span class="cx"> class SelectBaseMixin(object): </span><span class="cx"> """base class for Select and CompoundSelects""" </span><span class="cx"> def order_by(self, *clauses): </span><del>- if len(clauses) == 0 or clauses[0] is None: </del><ins>+ if len(clauses) == 1 and clauses[0] is None: </ins><span class="cx"> self.order_by_clause = ClauseList() </span><span class="cx"> elif getattr(self, 'order_by_clause', None): </span><span class="cx"> self.order_by_clause = ClauseList(*(list(self.order_by_clause.clauses) + list(clauses))) </span><span class="cx"> else: </span><span class="cx"> self.order_by_clause = ClauseList(*clauses) </span><span class="cx"> def group_by(self, *clauses): </span><del>- if len(clauses) == 0 or clauses[0] is None: </del><ins>+ if len(clauses) == 1 and clauses[0] is None: </ins><span class="cx"> self.group_by_clause = ClauseList() </span><span class="cx"> elif getattr(self, 'group_by_clause', None): </span><span class="cx"> self.group_by_clause = ClauseList(*(list(clauses)+list(self.group_by_clause.clauses))) </span></span></pre> </div> </div> </body> </html> |
From: <co...@sq...> - 2006-04-03 17:46:28
|
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head><style type="text/css"><!-- #msg dl { border: 1px #006 solid; background: #369; padding: 6px; color: #fff; } #msg dt { float: left; width: 6em; font-weight: bold; } #msg dt:after { content:':';} #msg dl, #msg dt, #msg ul, #msg li { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; } #msg dl a { font-weight: bold} #msg dl a:link { color:#fc3; } #msg dl a:active { color:#ff0; } #msg dl a:visited { color:#cc6; } h3 { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; font-weight: bold; } #msg pre { overflow: auto; background: #ffc; border: 1px #fc0 solid; padding: 6px; } #msg ul, pre { overflow: auto; } #patch { width: 100%; } #patch h4 {font-family: verdana,arial,helvetica,sans-serif;font-size:10pt;padding:8px;background:#369;color:#fff;margin:0;} #patch .propset h4, #patch .binary h4 {margin:0;} #patch pre {padding:0;line-height:1.2em;margin:0;} #patch .diff {width:100%;background:#eee;padding: 0 0 10px 0;overflow:auto;} #patch .propset .diff, #patch .binary .diff {padding:10px 0;} #patch span {display:block;padding:0 10px;} #patch .modfile, #patch .addfile, #patch .delfile, #patch .propset, #patch .binary, #patch .copfile {border:1px solid #ccc;margin:10px 0;} #patch ins {background:#dfd;text-decoration:none;display:block;padding:0 10px;} #patch del {background:#fdd;text-decoration:none;display:block;padding:0 10px;} #patch .lines, .info {color:#888;background:#fff;} --></style> <title>[1248] sqlalchemy/trunk/lib/sqlalchemy/sql.py: ack, fixes to the order by stuff from last night</title> </head> <body> <div id="msg"> <dl> <dt>Revision</dt> <dd>1248</dd> <dt>Author</dt> <dd>zzzeek</dd> <dt>Date</dt> <dd>2006-04-03 12:46:08 -0500 (Mon, 03 Apr 2006)</dd> </dl> <h3>Log Message</h3> <pre>ack, fixes to the order by stuff from last night</pre> <h3>Modified Paths</h3> <ul> <li><a href="#sqlalchemytrunklibsqlalchemysqlpy">sqlalchemy/trunk/lib/sqlalchemy/sql.py</a></li> </ul> </div> <div id="patch"> <h3>Diff</h3> <a id="sqlalchemytrunklibsqlalchemysqlpy"></a> <div class="modfile"><h4>Modified: sqlalchemy/trunk/lib/sqlalchemy/sql.py (1247 => 1248)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/trunk/lib/sqlalchemy/sql.py 2006-04-03 17:13:42 UTC (rev 1247) +++ sqlalchemy/trunk/lib/sqlalchemy/sql.py 2006-04-03 17:46:08 UTC (rev 1248) </span><span class="lines">@@ -1145,14 +1145,14 @@ </span><span class="cx"> class SelectBaseMixin(object): </span><span class="cx"> """base class for Select and CompoundSelects""" </span><span class="cx"> def order_by(self, *clauses): </span><del>- if clauses[0] is None: </del><ins>+ if len(clauses) == 0 or clauses[0] is None: </ins><span class="cx"> self.order_by_clause = ClauseList() </span><span class="cx"> elif getattr(self, 'order_by_clause', None): </span><del>- self.order_by_clause = ClauseList(*(list(clauses)+list(self.order_by_clause.clauses))) </del><ins>+ self.order_by_clause = ClauseList(*(list(self.order_by_clause.clauses) + list(clauses))) </ins><span class="cx"> else: </span><span class="cx"> self.order_by_clause = ClauseList(*clauses) </span><span class="cx"> def group_by(self, *clauses): </span><del>- if clauses[0] is None: </del><ins>+ if len(clauses) == 0 or clauses[0] is None: </ins><span class="cx"> self.group_by_clause = ClauseList() </span><span class="cx"> elif getattr(self, 'group_by_clause', None): </span><span class="cx"> self.group_by_clause = ClauseList(*(list(clauses)+list(self.group_by_clause.clauses))) </span></span></pre> </div> </div> </body> </html> |
From: <co...@sq...> - 2006-04-03 17:13:56
|
<!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>[1247] sqlalchemy/trunk/lib/sqlalchemy/databases/mssql.py: DOH !</title> </head> <body> <div id="msg"> <dl> <dt>Revision</dt> <dd>1247</dd> <dt>Author</dt> <dd>zzzeek</dd> <dt>Date</dt> <dd>2006-04-03 12:13:42 -0500 (Mon, 03 Apr 2006)</dd> </dl> <h3>Log Message</h3> <pre>DOH !</pre> <h3>Added Paths</h3> <ul> <li><a href="#sqlalchemytrunklibsqlalchemydatabasesmssqlpy">sqlalchemy/trunk/lib/sqlalchemy/databases/mssql.py</a></li> </ul> </div> <div id="patch"> <h3>Diff</h3> <a id="sqlalchemytrunklibsqlalchemydatabasesmssqlpy"></a> <div class="addfile"><h4>Added: sqlalchemy/trunk/lib/sqlalchemy/databases/mssql.py (1246 => 1247)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/trunk/lib/sqlalchemy/databases/mssql.py 2006-04-03 05:50:34 UTC (rev 1246) +++ sqlalchemy/trunk/lib/sqlalchemy/databases/mssql.py 2006-04-03 17:13:42 UTC (rev 1247) </span><span class="lines">@@ -0,0 +1,492 @@ </span><ins>+# mssql.py + +""" +notes: + supports both pymssql and adodbapi interfaces + + IDENTITY columns are supported by using SA schema.Sequence() objects. In other words: + Table('test', mss_engine, + Column('id', Integer, Sequence('blah',100,10), primary_key=True), + Column('name', String(20)) + ).create() + + would yield: + CREATE TABLE test ( + id INTEGER NOT NULL IDENTITY(100,10) PRIMARY KEY, + name VARCHAR(20) + ) + note that the start & increment values for sequences are optional and will default to 1,1 + + support for SET IDENTITY_INSERT ON mode (automagic on / off for INSERTs) + + support for auto-fetching of @@IDENTITY on insert + + select.limit implemented as SELECT TOP n + + +Known issues / TODO: + no support for more than one IDENTITY column per table + no support for table reflection of IDENTITY columns with (seed,increment) values other than (1,1) + no support for GUID type columns (yet) + pymssql has problems with transaction control that this module attempts to work around + pymssql has problems with binary and unicode data that this module does NOT work around + adodbapi fails testtypes.py unit test on unicode data too -- issue with the test? + +""" + +import sys, StringIO, string, types, re, datetime + +import sqlalchemy.sql as sql +import sqlalchemy.engine as engine +import sqlalchemy.schema as schema +import sqlalchemy.ansisql as ansisql +import sqlalchemy.types as sqltypes +from sqlalchemy import * + +try: + import adodbapi as dbmodule + # ADODBAPI has a non-standard Connection method + connect = dbmodule.Connection + make_connect_string = lambda keys: \ + [["Provider=SQLOLEDB;Data Source=%s;User Id=%s;Password=%s;Initial Catalog=%s" % ( + keys["host"], keys["user"], keys["password"], keys["database"])], {}] + do_commit = False +except: + try: + import pymssql as dbmodule + connect = dbmodule.connect + # pymmsql doesn't have a Binary method. we use string + dbmodule.Binary = lambda st: str(st) + make_connect_string = lambda keys: \ + [[], keys] + do_commit = True + except: + dbmodule = None + make_connect_string = lambda keys: [[],{}] + +class MSNumeric(sqltypes.Numeric): + def convert_result_value(self, value, engine): + return value + + def convert_bind_param(self, value, engine): + if value is None: + # Not sure that this exception is needed + return value + else: + return str(value) + + def get_col_spec(self): + return "NUMERIC(%(precision)s, %(length)s)" % {'precision': self.precision, 'length' : self.length} + +class MSFloat(sqltypes.Float): + def get_col_spec(self): + return "FLOAT(%(precision)s)" % {'precision': self.precision} + def convert_bind_param(self, value, engine): + """By converting to string, we can use Decimal types round-trip.""" + return str(value) + +class MSInteger(sqltypes.Integer): + def get_col_spec(self): + return "INTEGER" + +class MSSmallInteger(sqltypes.Smallinteger): + def get_col_spec(self): + return "SMALLINT" + +class MSDateTime(sqltypes.DateTime): + def get_col_spec(self): + return "DATETIME" + + def convert_bind_param(self, value, engine): + if hasattr(value, "isoformat"): + return value.isoformat(' ') + else: + return value + + def convert_result_value(self, value, engine): + # adodbapi will return datetimes with empty time values as datetime.date() objects. Promote them back to full datetime.datetime() + if value and not hasattr(value, 'second'): + return datetime.datetime(value.year, value.month, value.day) + return value + +class MSDate(sqltypes.Date): + def get_col_spec(self): + return "SMALLDATETIME" + + def convert_bind_param(self, value, engine): + if value and hasattr(value, "isoformat"): + return value.isoformat() + return value + + def convert_result_value(self, value, engine): + # pymssql will return SMALLDATETIME values as datetime.datetime(), truncate it back to datetime.date() + if value and hasattr(value, 'second'): + return value.date() + return value + +class MSText(sqltypes.TEXT): + def get_col_spec(self): + return "TEXT" +class MSString(sqltypes.String): + def get_col_spec(self): + return "VARCHAR(%(length)s)" % {'length' : self.length} +class MSChar(sqltypes.CHAR): + def get_col_spec(self): + return "CHAR(%(length)s)" % {'length' : self.length} +class MSBinary(sqltypes.Binary): + def get_col_spec(self): + return "IMAGE" +class MSBoolean(sqltypes.Boolean): + def get_col_spec(self): + return "BIT" + +colspecs = { + sqltypes.Integer : MSInteger, + sqltypes.Smallinteger: MSSmallInteger, + sqltypes.Numeric : MSNumeric, + sqltypes.Float : MSFloat, + sqltypes.DateTime : MSDateTime, + sqltypes.Date : MSDate, + sqltypes.String : MSString, + sqltypes.Binary : MSBinary, + sqltypes.Boolean : MSBoolean, + sqltypes.TEXT : MSText, + sqltypes.CHAR: MSChar, +} + +ischema_names = { + 'int' : MSInteger, + 'smallint' : MSSmallInteger, + 'varchar' : MSString, + 'char' : MSChar, + 'text' : MSText, + 'decimal' : MSNumeric, + 'numeric' : MSNumeric, + 'float' : MSFloat, + 'datetime' : MSDateTime, + 'smalldatetime' : MSDate, + 'binary' : MSBinary, + 'bit': MSBoolean, + 'real' : MSFloat, + 'image' : MSBinary +} + +def engine(opts, **params): + return MSSQLEngine(opts, **params) + +def descriptor(): + return {'name':'mssql', + 'description':'MSSQL', + 'arguments':[ + ('user',"Database Username",None), + ('password',"Database Password",None), + ('db',"Database Name",None), + ('host',"Hostname", None), + ]} + +class MSSQLEngine(ansisql.ANSISQLEngine): + def __init__(self, opts, module = None, **params): + if module is None: + self.module = dbmodule + self.opts = opts or {} + ansisql.ANSISQLEngine.__init__(self, **params) + + def connect_args(self): + return make_connect_string(self.opts) + + def type_descriptor(self, typeobj): + return sqltypes.adapt_type(typeobj, colspecs) + + def last_inserted_ids(self): + return self.context.last_inserted_ids + + def supports_sane_rowcount(self): + return True + + def compiler(self, statement, bindparams, **kwargs): + return MSSQLCompiler(statement, bindparams, engine=self, **kwargs) + + def schemagenerator(self, **params): + return MSSQLSchemaGenerator(self, **params) + + def schemadropper(self, **params): + return MSSQLSchemaDropper(self, **params) + + def get_default_schema_name(self): + return "dbo" + + def last_inserted_ids(self): + return self.context.last_inserted_ids + + def do_begin(self, connection): + """implementations might want to put logic here for turning autocommit on/off, etc.""" + if do_commit: + pass + + def _execute(self, c, statement, parameters): + try: + c.execute(statement, parameters) + self.context.rowcount = c.rowcount + c.DBPROP_COMMITPRESERVE = "Y" + except Exception, e: + # del c.parent # Close the Parent Connection, delete it from the pool + raise exceptions.SQLError(statement, parameters, e) + + + def do_rollback(self, connection): + """implementations might want to put logic here for turning autocommit on/off, etc.""" + if do_commit: + try: + # connection.rollback() for pymmsql failed sometimes--the begin tran doesn't show up + # this is a workaround that seems to be handle it. + r = self.raw_connection(connection) + r.query("if @@trancount > 0 rollback tran") + r.fetch_array() + r.query("begin tran") + r.fetch_array() + except: + pass + try: + del connection + except: + raise + + def raw_connection(self, connection): + """Pull the raw pymmsql connection out--sensative to "pool.ConnectionFairy" and pymssql.pymssqlCnx Classes""" + try: + return connection.connection.__dict__['_pymssqlCnx__cnx'] + except: + return connection.connection.adoConn + + def do_commit(self, connection): + """implementations might want to put logic here for turning autocommit on/off, etc. + do_commit is set for pymmsql connections--ADO seems to handle transactions without any issue + """ + # ADO Uses Implicit Transactions. + if do_commit: + # This is very pymssql specific. We use this instead of its commit, because it hangs on failed rollbacks. + # By using the "if" we don't assume an open transaction--much better. + r = self.raw_connection(connection) + r.query("if @@trancount > 0 commit tran") + r.fetch_array() + r.query("begin tran") + r.fetch_array() + else: + pass + #connection.supportsTransactions = 1 + try: + pass + #connection.adoConn.CommitTrans() + except: + pass + #connection.adoConn.execute("begin trans", {}) + #connection.adoConn.BeginTrans() + + def connection(self): + """returns a managed DBAPI connection from this SQLEngine's connection pool.""" + c = self._pool.connect() + c.supportsTransactions = 0 + return c + + def pre_exec(self, proxy, compiled, parameters, **kwargs): + """ MS-SQL has a special mode for inserting non-NULL values into IDENTITY columns. Activate it if needed. """ + if getattr(compiled, "isinsert", False): + self.context.IINSERT = False + self.context.HASIDENT = False + for c in compiled.statement.table.c: + if hasattr(c,'sequence'): + self.context.HASIDENT = True + if parameters.has_key(c.name): + self.context.IINSERT = True + break + if self.context.IINSERT: + proxy("SET IDENTITY_INSERT %s ON" % compiled.statement.table.name) + + def post_exec(self, proxy, compiled, parameters, **kwargs): + """ Turn off the INDENTITY_INSERT mode if it's been activated, and fetch recently inserted IDENTIFY values (works only for one column) """ + if getattr(compiled, "isinsert", False): + if self.context.IINSERT: + proxy("SET IDENTITY_INSERT %s OFF" % compiled.statement.table.name) + self.context.IINSERT = False + elif self.context.HASIDENT: + cursor = proxy("SELECT @@IDENTITY AS lastrowid") + row = cursor.fetchone() + self.context.last_inserted_ids = [row[0]] + self.context.HASIDENT = False + + def dbapi(self): + return self.module + + def reflecttable(self, table): + import sqlalchemy.databases.information_schema as ischema + + # Get base columns + if table.schema is not None: + current_schema = table.schema + else: + current_schema = self.get_default_schema_name() + + columns = ischema.gen_columns.toengine(self) + s = select([columns], + current_schema and sql.and_(columns.c.table_name==table.name, columns.c.table_schema==current_schema) or columns.c.table_name==table.name, + order_by=[columns.c.ordinal_position]) + + c = s.execute() + while True: + row = c.fetchone() + if row is None: + break + + (name, type, nullable, charlen, numericprec, numericscale, default) = ( + row[columns.c.column_name], + row[columns.c.data_type], + row[columns.c.is_nullable] == 'YES', + row[columns.c.character_maximum_length], + row[columns.c.numeric_precision], + row[columns.c.numeric_scale], + row[columns.c.column_default] + ) + + args = [] + for a in (charlen, numericprec, numericscale): + if a is not None: + args.append(a) + coltype = ischema_names[type] + + coltype = coltype(*args) + colargs= [] + if default is not None: + colargs.append(PassiveDefault(sql.text(default))) + + table.append_item(schema.Column(name, coltype, nullable=nullable, *colargs)) + + + # We also run an sp_columns to check for identity columns: + # FIXME: note that this only fetches the existence of an identity column, not it's properties like (seed, increment) + cursor = table.engine.execute("sp_columns " + table.name, {}) + while True: + row = cursor.fetchone() + if row is None: + break + col_name, type_name = row[3], row[5] + if type_name.endswith("identity"): + ic = table.c[col_name] + # setup a psuedo-sequence to represent the identity attribute - we interpret this at table.create() time as the identity attribute + ic.sequence = schema.Sequence(ic.name + '_identity') + + # Add constraints + RR = ischema.gen_ref_constraints.toengine(self) #information_schema.referential_constraints + TC = ischema.gen_constraints.toengine(self) #information_schema.table_constraints + C = ischema.gen_column_constraints.toengine(self).alias('C') #information_schema.constraint_column_usage: the constrained column + R = ischema.gen_column_constraints.toengine(self).alias('R') #information_schema.constraint_column_usage: the referenced column + + fromjoin = TC.join(RR, RR.c.constraint_name == TC.c.constraint_name).join(C, C.c.constraint_name == RR.c.constraint_name) + fromjoin = fromjoin.join(R, R.c.constraint_name == RR.c.unique_constraint_name) + + s = select([TC.c.constraint_type, C.c.table_schema, C.c.table_name, C.c.column_name, + R.c.table_schema, R.c.table_name, R.c.column_name], + and_(RR.c.constraint_schema == current_schema, C.c.table_name == table.name), + from_obj = [fromjoin] + ) + + c = s.execute() + + while True: + row = c.fetchone() + if row is None: + break + (type, constrained_column, referred_schema, referred_table, referred_column) = ( + row[colmap[0]], + row[colmap[3]], + row[colmap[4]], + row[colmap[5]], + row[colmap[6]] + ) + + if type=='PRIMARY KEY': + table.c[constrained_column]._set_primary_key() + elif type=='FOREIGN KEY': + remotetable = Table(referred_table, self, autoload = True, schema=referred_schema) + table.c[constrained_column].append_item(schema.ForeignKey(remotetable.c[referred_column])) + + + +class MSSQLCompiler(ansisql.ANSICompiler): + def __init__(self, *args, **kwargs): + super(MSSQLCompiler, self).__init__(*args, **kwargs) + self.tablealiases = {} + + def visit_select_precolumns(self, select): + """ MS-SQL puts TOP, it's version of LIMIT here """ + s = select.distinct and "DISTINCT " or "" + if (select.limit): + s += "TOP %s " % (select.limit,) + return s + + def limit_clause(self, select): + # Limit in mssql is after the select keyword; MSsql has no support for offset + return "" + + + def visit_table(self, table): + # alias schema-qualified tables + if self.tablealiases.has_key(table): + super(MSSQLCompiler, self).visit_table(table) + elif getattr(table, 'schema', None) is not None: + alias = table.alias() + self.tablealiases[table] = alias + alias.accept_visitor(self) + self.froms[('alias', table)] = self.froms[table] + for c in alias.c: + c.accept_visitor(self) + self.tablealiases[alias] = self.froms[table] + self.froms[table] = self.froms[alias] + + def visit_alias(self, alias): + # translate for schema-qualified table aliases + if self.froms.has_key(('alias', alias.original)): + self.froms[alias] = self.froms[('alias', alias.original)] + " AS " + alias.name + self.strings[alias] = "" + else: + super(MSSQLCompiler, self).visit_alias(alias) + + def visit_column(self, column): + # translate for schema-qualified table aliases + super(MSSQLCompiler, self).visit_column(column) + if column.table is not None and self.tablealiases.has_key(column.table): + self.strings[column] = \ + self.strings[self.tablealiases[column.table]._get_col_by_original(column.original)] + + +class MSSQLSchemaGenerator(ansisql.ANSISchemaGenerator): + def get_column_specification(self, column, override_pk=False, first_pk=False): + colspec = column.name + " " + column.type.get_col_spec() + + # install a IDENTITY Sequence if we have an implicit IDENTITY column + if column.primary_key and isinstance(column.type, types.Integer): + if column.default is None or (isinstance(column.default, schema.Sequence) and column.default.optional): + column.sequence = schema.Sequence(column.name + '_seq') + + if not column.nullable: + colspec += " NOT NULL" + + if hasattr(column, 'sequence'): + colspec += " IDENTITY(%s,%s)" % (column.sequence.start or 1, column.sequence.increment or 1) + else: + default = self.get_column_default_string(column) + if default is not None: + colspec += " DEFAULT " + default + + if column.primary_key: + if not override_pk: + colspec += " PRIMARY KEY" + if column.foreign_key: + colspec += " REFERENCES %s(%s)" % (column.foreign_key.column.table.fullname, column.foreign_key.column.name) + + return colspec + + +class MSSQLSchemaDropper(ansisql.ANSISchemaDropper): + def visit_index(self, index): + self.append("\nDROP INDEX " + index.table.name + "." + index.name) + self.execute() </ins></span></pre> </div> </div> </body> </html> |
From: <co...@sq...> - 2006-04-03 05:50:43
|
<!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>[1246] sqlalchemy/trunk/doc/build/content/unitofwork.myt: still tryin to clarify....</title> </head> <body> <div id="msg"> <dl> <dt>Revision</dt> <dd>1246</dd> <dt>Author</dt> <dd>zzzeek</dd> <dt>Date</dt> <dd>2006-04-03 00:50:34 -0500 (Mon, 03 Apr 2006)</dd> </dl> <h3>Log Message</h3> <pre>still tryin to clarify....</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 (1245 => 1246)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/trunk/doc/build/content/unitofwork.myt 2006-04-03 05:22:42 UTC (rev 1245) +++ sqlalchemy/trunk/doc/build/content/unitofwork.myt 2006-04-03 05:50:34 UTC (rev 1246) </span><span class="lines">@@ -200,7 +200,10 @@ </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 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> </del><ins>+ <p>The purpose of the Commit operation, as defined by the <code>objectstore</code> package, is to instruct the Unit of Work to analyze its lists of modified objects, assemble them into a dependency graph, fire off the appopriate INSERT, UPDATE, and DELETE statements via the mappers related to those objects, and to synchronize column-based object attributes that correspond directly to updated/inserted database columns.</p> + <p>Its important to note that the <b>objectstore.get_session().commit() operation is not the same as the commit() operation on SQLEngine.</b> A <code>SQLEngine</code>, described in <&formatting.myt:link, path="database"&>, has its own <code>begin</code> and <code>commit</code> statements which deal directly with transactions opened on DBAPI connections. While the <code>session.commit()</code> makes use of these calls in order to issue its own SQL within a database transaction, it is only dealing with "committing" its own in-memory changes and only has an indirect relationship with database connection objects. + </p> + <p>The <code>session.commit()</code> operation also 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></pre> </div> </div> </body> </html> |
From: <co...@sq...> - 2006-04-03 05:22: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>[1245] sqlalchemy/trunk/lib/sqlalchemy/sql.py: added to_selectable() onto ColumnClause to simplify _get_col_by_original</title> </head> <body> <div id="msg"> <dl> <dt>Revision</dt> <dd>1245</dd> <dt>Author</dt> <dd>zzzeek</dd> <dt>Date</dt> <dd>2006-04-03 00:22:42 -0500 (Mon, 03 Apr 2006)</dd> </dl> <h3>Log Message</h3> <pre>added to_selectable() onto ColumnClause to simplify _get_col_by_original</pre> <h3>Modified Paths</h3> <ul> <li><a href="#sqlalchemytrunklibsqlalchemysqlpy">sqlalchemy/trunk/lib/sqlalchemy/sql.py</a></li> </ul> </div> <div id="patch"> <h3>Diff</h3> <a id="sqlalchemytrunklibsqlalchemysqlpy"></a> <div class="modfile"><h4>Modified: sqlalchemy/trunk/lib/sqlalchemy/sql.py (1244 => 1245)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/trunk/lib/sqlalchemy/sql.py 2006-04-03 05:00:53 UTC (rev 1244) +++ sqlalchemy/trunk/lib/sqlalchemy/sql.py 2006-04-03 05:22:42 UTC (rev 1245) </span><span class="lines">@@ -1033,6 +1033,12 @@ </span><span class="cx"> _label = property(_get_label) </span><span class="cx"> def accept_visitor(self, visitor): </span><span class="cx"> visitor.visit_column(self) </span><ins>+ def to_selectable(self, selectable): + """given a Selectable, returns this column's equivalent in that Selectable, if any. + + for example, this could translate the column "name" from a Table object + to an Alias of a Select off of that Table object.""" + return selectable._get_col_by_original(self.original, False) </ins><span class="cx"> def _get_from_objects(self): </span><span class="cx"> if self.table is not None: </span><span class="cx"> return [self.table] </span></span></pre> </div> </div> </body> </html> |
From: <co...@sq...> - 2006-04-03 05:01:09
|
<!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>[1244] sqlalchemy/trunk/lib/sqlalchemy: made order_by/group_by construction a little more simplisitc</title> </head> <body> <div id="msg"> <dl> <dt>Revision</dt> <dd>1244</dd> <dt>Author</dt> <dd>zzzeek</dd> <dt>Date</dt> <dd>2006-04-03 00:00:53 -0500 (Mon, 03 Apr 2006)</dd> </dl> <h3>Log Message</h3> <pre>made order_by/group_by construction a little more simplisitc fix to mapper extension CompoundSelect can export all columns now, not sure if theres any advantage there</pre> <h3>Modified Paths</h3> <ul> <li><a href="#sqlalchemytrunklibsqlalchemyansisqlpy">sqlalchemy/trunk/lib/sqlalchemy/ansisql.py</a></li> <li><a href="#sqlalchemytrunklibsqlalchemydatabasesoraclepy">sqlalchemy/trunk/lib/sqlalchemy/databases/oracle.py</a></li> <li><a href="#sqlalchemytrunklibsqlalchemymappingmapperpy">sqlalchemy/trunk/lib/sqlalchemy/mapping/mapper.py</a></li> <li><a href="#sqlalchemytrunklibsqlalchemysqlpy">sqlalchemy/trunk/lib/sqlalchemy/sql.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 (1243 => 1244)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/trunk/lib/sqlalchemy/ansisql.py 2006-04-03 03:24:06 UTC (rev 1243) +++ sqlalchemy/trunk/lib/sqlalchemy/ansisql.py 2006-04-03 05:00:53 UTC (rev 1244) </span><span class="lines">@@ -233,8 +233,12 @@ </span><span class="cx"> </span><span class="cx"> def visit_compound_select(self, cs): </span><span class="cx"> text = string.join([self.get_str(c) for c in cs.selects], " " + cs.keyword + " ") </span><del>- for tup in cs.clauses: - text += " " + tup[0] + " " + self.get_str(tup[1]) </del><ins>+ group_by = self.get_str(cs.group_by_clause) + if group_by: + text += " GROUP BY " + group_by + order_by = self.get_str(cs.order_by_clause) + if order_by: + text += " ORDER BY " + order_by </ins><span class="cx"> if cs.parens: </span><span class="cx"> self.strings[cs] = "(" + text + ")" </span><span class="cx"> else: </span><span class="lines">@@ -361,11 +365,14 @@ </span><span class="cx"> if t: </span><span class="cx"> text += " \nWHERE " + t </span><span class="cx"> </span><del>- for tup in select.clauses: - ss = self.get_str(tup[1]) - if ss: - text += " " + tup[0] + " " + ss </del><ins>+ group_by = self.get_str(select.group_by_clause) + if group_by: + text += " GROUP BY " + group_by </ins><span class="cx"> </span><ins>+ order_by = self.get_str(select.order_by_clause) + if order_by: + text += " ORDER BY " + order_by + </ins><span class="cx"> if select.having is not None: </span><span class="cx"> t = self.get_str(select.having) </span><span class="cx"> if t: </span></span></pre></div> <a id="sqlalchemytrunklibsqlalchemydatabasesoraclepy"></a> <div class="modfile"><h4>Modified: sqlalchemy/trunk/lib/sqlalchemy/databases/oracle.py (1243 => 1244)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/trunk/lib/sqlalchemy/databases/oracle.py 2006-04-03 03:24:06 UTC (rev 1243) +++ sqlalchemy/trunk/lib/sqlalchemy/databases/oracle.py 2006-04-03 05:00:53 UTC (rev 1244) </span><span class="lines">@@ -276,9 +276,8 @@ </span><span class="cx"> return </span><span class="cx"> if select.limit is not None or select.offset is not None: </span><span class="cx"> select._oracle_visit = True </span><del>- if hasattr(select, "order_by_clause"): - orderby = self.strings[select.order_by_clause] - else: </del><ins>+ orderby = self.strings[select.order_by_clause] + if not orderby: </ins><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></span></pre></div> <a id="sqlalchemytrunklibsqlalchemymappingmapperpy"></a> <div class="modfile"><h4>Modified: sqlalchemy/trunk/lib/sqlalchemy/mapping/mapper.py (1243 => 1244)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/trunk/lib/sqlalchemy/mapping/mapper.py 2006-04-03 03:24:06 UTC (rev 1243) +++ sqlalchemy/trunk/lib/sqlalchemy/mapping/mapper.py 2006-04-03 05:00:53 UTC (rev 1244) </span><span class="lines">@@ -1062,7 +1062,7 @@ </span><span class="cx"> if self.next is None: </span><span class="cx"> return EXT_PASS </span><span class="cx"> else: </span><del>- return self.next.populate_instance(row, imap, result, instance, isnew) </del><ins>+ return self.next.populate_instance(mapper, instance, row, identitykey, imap, 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="sqlalchemytrunklibsqlalchemysqlpy"></a> <div class="modfile"><h4>Modified: sqlalchemy/trunk/lib/sqlalchemy/sql.py (1243 => 1244)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/trunk/lib/sqlalchemy/sql.py 2006-04-03 03:24:06 UTC (rev 1243) +++ sqlalchemy/trunk/lib/sqlalchemy/sql.py 2006-04-03 05:00:53 UTC (rev 1244) </span><span class="lines">@@ -733,8 +733,6 @@ </span><span class="cx"> self.clauses.append(clause) </span><span class="cx"> def accept_visitor(self, visitor): </span><span class="cx"> for c in self.clauses: </span><del>- if c is None: - raise "oh weird" + repr(self.clauses) </del><span class="cx"> c.accept_visitor(visitor) </span><span class="cx"> visitor.visit_clauselist(self) </span><span class="cx"> def _get_from_objects(self): </span><span class="lines">@@ -1141,31 +1139,19 @@ </span><span class="cx"> class SelectBaseMixin(object): </span><span class="cx"> """base class for Select and CompoundSelects""" </span><span class="cx"> def order_by(self, *clauses): </span><del>- self._append_clause('order_by_clause', "ORDER BY", *clauses) </del><ins>+ if clauses[0] is None: + self.order_by_clause = ClauseList() + elif getattr(self, 'order_by_clause', None): + self.order_by_clause = ClauseList(*(list(clauses)+list(self.order_by_clause.clauses))) + else: + self.order_by_clause = ClauseList(*clauses) </ins><span class="cx"> def group_by(self, *clauses): </span><del>- self._append_clause('group_by_clause', "GROUP BY", *clauses) - def _append_clause(self, attribute, prefix, *clauses): - if len(clauses) == 1 and clauses[0] is None: - try: - delattr(self, attribute) - except AttributeError: - pass - return - if not hasattr(self, attribute): - l = ClauseList(*clauses) - setattr(self, attribute, l) </del><ins>+ if clauses[0] is None: + self.group_by_clause = ClauseList() + elif getattr(self, 'group_by_clause', None): + self.group_by_clause = ClauseList(*(list(clauses)+list(self.group_by_clause.clauses))) </ins><span class="cx"> else: </span><del>- getattr(self, attribute).clauses += clauses - def _get_clauses(self): - # TODO: this is a little stupid. make ORDER BY/GROUP BY keywords handled by - # the compiler, make group_by_clause/order_by_clause regular attributes - x =[] - if getattr(self, 'group_by_clause', None): - x.append(("GROUP BY", self.group_by_clause)) - if getattr(self, 'order_by_clause', None): - x.append(("ORDER BY", self.order_by_clause)) - return x - clauses = property(_get_clauses) </del><ins>+ self.group_by_clause = ClauseList(*clauses) </ins><span class="cx"> def select(self, whereclauses = None, **params): </span><span class="cx"> return select([self], whereclauses, **params) </span><span class="cx"> def _get_from_objects(self): </span><span class="lines">@@ -1186,23 +1172,23 @@ </span><span class="cx"> for s in self.selects: </span><span class="cx"> s.group_by(None) </span><span class="cx"> s.order_by(None) </span><del>- group_by = kwargs.get('group_by', None) - if group_by: - self.group_by(*group_by) - order_by = kwargs.get('order_by', None) - if order_by: - self.order_by(*order_by) </del><ins>+ self.group_by(*kwargs.get('group_by', [None])) + self.order_by(*kwargs.get('order_by', [None])) + </ins><span class="cx"> def _exportable_columns(self): </span><del>- return self.selects[0].columns </del><ins>+ for s in self.selects: + for c in s.c: + yield c + </ins><span class="cx"> def _proxy_column(self, column): </span><del>- self._columns[column.key] = column - if column.primary_key: - self._primary_key.append(column) - if column.foreign_key: - self._foreign_keys.append(column) </del><ins>+ if self.use_labels: + return column._make_proxy(self, name=column._label) + else: + return column._make_proxy(self, name=column.name) + </ins><span class="cx"> def accept_visitor(self, visitor): </span><del>- for tup in self.clauses: - tup[1].accept_visitor(visitor) </del><ins>+ self.order_by_clause.accept_visitor(visitor) + self.group_by_clause.accept_visitor(visitor) </ins><span class="cx"> for s in self.selects: </span><span class="cx"> s.accept_visitor(visitor) </span><span class="cx"> visitor.visit_compound_select(self) </span><span class="lines">@@ -1251,6 +1237,9 @@ </span><span class="cx"> self._correlated = None </span><span class="cx"> self._correlator = Select.CorrelatedVisitor(self, False) </span><span class="cx"> self._wherecorrelator = Select.CorrelatedVisitor(self, True) </span><ins>+ + self.group_by(*(group_by or [None])) + self.order_by(*(order_by or [None])) </ins><span class="cx"> </span><span class="cx"> if columns is not None: </span><span class="cx"> for c in columns: </span><span class="lines">@@ -1263,11 +1252,7 @@ </span><span class="cx"> </span><span class="cx"> for f in from_obj: </span><span class="cx"> self.append_from(f) </span><del>- - if group_by: - self.group_by(*group_by) - if order_by: - self.order_by(*order_by) </del><ins>+ </ins><span class="cx"> </span><span class="cx"> class CorrelatedVisitor(ClauseVisitor): </span><span class="cx"> """visits a clause, locates any Select clauses, and tells them that they should </span><span class="lines">@@ -1355,8 +1340,8 @@ </span><span class="cx"> self.whereclause.accept_visitor(visitor) </span><span class="cx"> if self.having is not None: </span><span class="cx"> self.having.accept_visitor(visitor) </span><del>- for tup in self.clauses: - tup[1].accept_visitor(visitor) </del><ins>+ self.order_by_clause.accept_visitor(visitor) + self.group_by_clause.accept_visitor(visitor) </ins><span class="cx"> visitor.visit_select(self) </span><span class="cx"> </span><span class="cx"> def union(self, other, **kwargs): </span></span></pre> </div> </div> </body> </html> |
From: <co...@sq...> - 2006-04-03 03:24:24
|
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head><style type="text/css"><!-- #msg dl { border: 1px #006 solid; background: #369; padding: 6px; color: #fff; } #msg dt { float: left; width: 6em; font-weight: bold; } #msg dt:after { content:':';} #msg dl, #msg dt, #msg ul, #msg li { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; } #msg dl a { font-weight: bold} #msg dl a:link { color:#fc3; } #msg dl a:active { color:#ff0; } #msg dl a:visited { color:#cc6; } h3 { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; font-weight: bold; } #msg pre { overflow: auto; background: #ffc; border: 1px #fc0 solid; padding: 6px; } #msg ul, pre { overflow: auto; } #patch { width: 100%; } #patch h4 {font-family: verdana,arial,helvetica,sans-serif;font-size:10pt;padding:8px;background:#369;color:#fff;margin:0;} #patch .propset h4, #patch .binary h4 {margin:0;} #patch pre {padding:0;line-height:1.2em;margin:0;} #patch .diff {width:100%;background:#eee;padding: 0 0 10px 0;overflow:auto;} #patch .propset .diff, #patch .binary .diff {padding:10px 0;} #patch span {display:block;padding:0 10px;} #patch .modfile, #patch .addfile, #patch .delfile, #patch .propset, #patch .binary, #patch .copfile {border:1px solid #ccc;margin:10px 0;} #patch ins {background:#dfd;text-decoration:none;display:block;padding:0 10px;} #patch del {background:#fdd;text-decoration:none;display:block;padding:0 10px;} #patch .lines, .info {color:#888;background:#fff;} --></style> <title>[1243] sqlalchemy/trunk/doc/build: added a type to label....</title> </head> <body> <div id="msg"> <dl> <dt>Revision</dt> <dd>1243</dd> <dt>Author</dt> <dd>zzzeek</dd> <dt>Date</dt> <dd>2006-04-02 22:24:06 -0500 (Sun, 02 Apr 2006)</dd> </dl> <h3>Log Message</h3> <pre>added a type to label....</pre> <h3>Modified Paths</h3> <ul> <li><a href="#sqlalchemytrunkdocbuildtxt2mytpy">sqlalchemy/trunk/doc/build/txt2myt.py</a></li> <li><a href="#sqlalchemytrunklibsqlalchemysqlpy">sqlalchemy/trunk/lib/sqlalchemy/sql.py</a></li> </ul> </div> <div id="patch"> <h3>Diff</h3> <a id="sqlalchemytrunkdocbuildtxt2mytpy"></a> <div class="modfile"><h4>Modified: sqlalchemy/trunk/doc/build/txt2myt.py (1242 => 1243)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/trunk/doc/build/txt2myt.py 2006-04-03 02:00:18 UTC (rev 1242) +++ sqlalchemy/trunk/doc/build/txt2myt.py 2006-04-03 03:24:06 UTC (rev 1243) </span><span class="lines">@@ -264,5 +264,5 @@ </span><span class="cx"> input = file(inname).read() </span><span class="cx"> html = markdown.markdown(input) </span><span class="cx"> myt = html2myghtydoc(html) </span><del>- file(inname[:-3] + "html", 'w').write(html) </del><ins>+ #file(inname[:-3] + "html", 'w').write(html) </ins><span class="cx"> file(outname, 'w').write(myt) </span></span></pre></div> <a id="sqlalchemytrunklibsqlalchemysqlpy"></a> <div class="modfile"><h4>Modified: sqlalchemy/trunk/lib/sqlalchemy/sql.py (1242 => 1243)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/trunk/lib/sqlalchemy/sql.py 2006-04-03 02:00:18 UTC (rev 1242) +++ sqlalchemy/trunk/lib/sqlalchemy/sql.py 2006-04-03 03:24:06 UTC (rev 1243) </span><span class="lines">@@ -489,7 +489,7 @@ </span><span class="cx"> def endswith(self, other): </span><span class="cx"> return self._compare('LIKE', "%" + str(other)) </span><span class="cx"> def label(self, name): </span><del>- return Label(name, self) </del><ins>+ return Label(name, self, self.type) </ins><span class="cx"> def distinct(self): </span><span class="cx"> return CompoundClause(None,"DISTINCT", self) </span><span class="cx"> def op(self, operator): </span><span class="lines">@@ -995,11 +995,12 @@ </span><span class="cx"> </span><span class="cx"> </span><span class="cx"> class Label(ColumnElement): </span><del>- def __init__(self, name, obj): </del><ins>+ def __init__(self, name, obj, type=None): </ins><span class="cx"> self.name = name </span><span class="cx"> while isinstance(obj, Label): </span><span class="cx"> obj = obj.obj </span><span class="cx"> self.obj = obj </span><ins>+ self.type = type or sqltypes.NullTypeEngine() </ins><span class="cx"> obj.parens=True </span><span class="cx"> key = property(lambda s: s.name) </span><span class="cx"> </span></span></pre> </div> </div> </body> </html> |
From: <co...@sq...> - 2006-04-03 02:00:29
|
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head><style type="text/css"><!-- #msg dl { border: 1px #006 solid; background: #369; padding: 6px; color: #fff; } #msg dt { float: left; width: 6em; font-weight: bold; } #msg dt:after { content:':';} #msg dl, #msg dt, #msg ul, #msg li { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; } #msg dl a { font-weight: bold} #msg dl a:link { color:#fc3; } #msg dl a:active { color:#ff0; } #msg dl a:visited { color:#cc6; } h3 { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; font-weight: bold; } #msg pre { overflow: auto; background: #ffc; border: 1px #fc0 solid; padding: 6px; } #msg ul, pre { overflow: auto; } #patch { width: 100%; } #patch h4 {font-family: verdana,arial,helvetica,sans-serif;font-size:10pt;padding:8px;background:#369;color:#fff;margin:0;} #patch .propset h4, #patch .binary h4 {margin:0;} #patch pre {padding:0;line-height:1.2em;margin:0;} #patch .diff {width:100%;background:#eee;padding: 0 0 10px 0;overflow:auto;} #patch .propset .diff, #patch .binary .diff {padding:10px 0;} #patch span {display:block;padding:0 10px;} #patch .modfile, #patch .addfile, #patch .delfile, #patch .propset, #patch .binary, #patch .copfile {border:1px solid #ccc;margin:10px 0;} #patch ins {background:#dfd;text-decoration:none;display:block;padding:0 10px;} #patch del {background:#fdd;text-decoration:none;display:block;padding:0 10px;} #patch .lines, .info {color:#888;background:#fff;} --></style> <title>[1242] sqlalchemy/trunk/doc/build/content: converted to markdown, added MS-SQL</title> </head> <body> <div id="msg"> <dl> <dt>Revision</dt> <dd>1242</dd> <dt>Author</dt> <dd>zzzeek</dd> <dt>Date</dt> <dd>2006-04-02 21:00:18 -0500 (Sun, 02 Apr 2006)</dd> </dl> <h3>Log Message</h3> <pre>converted to markdown, added MS-SQL</pre> <h3>Added Paths</h3> <ul> <li><a href="#sqlalchemytrunkdocbuildcontentdbenginetxt">sqlalchemy/trunk/doc/build/content/dbengine.txt</a></li> </ul> <h3>Removed Paths</h3> <ul> <li><a href="#sqlalchemytrunkdocbuildcontentdbenginemyt">sqlalchemy/trunk/doc/build/content/dbengine.myt</a></li> </ul> </div> <div id="patch"> <h3>Diff</h3> <a id="sqlalchemytrunkdocbuildcontentdbenginemyt"></a> <div class="delfile"><h4>Deleted: sqlalchemy/trunk/doc/build/content/dbengine.myt (1241 => 1242)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/trunk/doc/build/content/dbengine.myt 2006-04-03 01:35:48 UTC (rev 1241) +++ sqlalchemy/trunk/doc/build/content/dbengine.myt 2006-04-03 02:00:18 UTC (rev 1242) </span><span class="lines">@@ -1,267 +0,0 @@ </span><del>-<%flags>inherit='document_base.myt'</%flags> -<%attr>title='Database Engines'</%attr> - -<&|doclib.myt:item, name="dbengine", description="Database Engines" &> - <p>A database engine is a subclass of <span class="codeline">sqlalchemy.engine.SQLEngine</span>, and is the starting point for where SQLAlchemy provides a layer of abstraction on top of the various DBAPI2 database modules. It serves as an abstract factory for database-specific implementation objects as well as a layer of abstraction over the most essential tasks of a database connection, including connecting, executing queries, returning result sets, and managing transactions.</p> - - <p> - The average developer doesn't need to know anything about the interface or workings of a SQLEngine in order to use it. Simply creating one, and then specifying it when constructing tables and other SQL objects is all that's needed. </p> - - <p>A SQLEngine is also a layer of abstraction on top of the connection pooling described in the previous section. While a DBAPI connection pool can be used explicitly alongside a SQLEngine, its not really necessary. Once you have a SQLEngine, you can retrieve pooled connections directly from its underlying connection pool via its own <span class="codeline">connection()</span> method. However, if you're exclusively using SQLALchemy's SQL construction objects and/or object-relational mappers, all the details of connecting are handled by those libraries automatically. - </p> - <&|doclib.myt:item, name="establishing", description="Establishing a Database Engine" &> - <p> - Engines exist for SQLite, Postgres, MySQL, and Oracle, using the Pysqlite, Psycopg (1 or 2), MySQLDB, and cx_Oracle modules (there is also experimental support for Firebird). Each engine imports its corresponding module which is required to be installed. For Postgres and Oracle, an alternate module may be specified at construction time as well. - </p> - <p>The string based argument names for connecting are translated to the appropriate names when the connection is made; argument names include "host" or "hostname" for database host, "database", "db", or "dbname" for the database name (also is dsn for Oracle), "user" or "username" for the user, and "password", "pw", or "passwd" for the password. SQLite expects "filename" or "file" for the filename, or if None it defaults to "":memory:".</p> - <p>The connection arguments can be specified as a string + dictionary pair, or a single URL-encoded string, as follows:</p> - - <&|formatting.myt:code&> - from sqlalchemy import * - - # sqlite in memory - sqlite_engine = create_engine('sqlite', {'filename':':memory:'}, **opts) - - # via URL - sqlite_engine = create_engine('sqlite://', **opts) - - # sqlite using a file - sqlite_engine = create_engine('sqlite', {'filename':'querytest.db'}, **opts) - - # via URL - sqlite_engine = create_engine('sqlite://filename=querytest.db', **opts) - - # postgres - postgres_engine = create_engine('postgres', - {'database':'test', - 'host':'127.0.0.1', - 'user':'scott', - 'password':'tiger'}, **opts) - - # via URL - postgres_engine = create_engine('postgres://database=test&amp;host=127.0.0.1&amp;user=scott&amp;password=tiger') - - # mysql - mysql_engine = create_engine('mysql', - { - 'db':'mydb', - 'user':'scott', - 'passwd':'tiger', - 'host':'127.0.0.1' - } - **opts) - # oracle - oracle_engine = create_engine('oracle', - {'dsn':'mydsn', - 'user':'scott', - 'password':'tiger'}, **opts) - - - </&> - <p>Note that the general form of connecting to an engine is:</p> - <&|formatting.myt:code &> - # separate arguments - engine = create_engine( - <enginename>, - {<named DBAPI arguments>}, - <sqlalchemy options>; - ) - - # url - engine = create_engine('&lt;enginename&gt;://&lt;named DBAPI arguments&gt;', <sqlalchemy options>) - </&> - </&> - <&|doclib.myt:item, name="methods", description="Database Engine Methods" &> - <p>A few useful methods off the SQLEngine are described here:</p> - <&|formatting.myt:code&> - engine = create_engine('postgres://hostname=localhost&user=scott&password=tiger&database=test') - - # get a pooled DBAPI connection - conn = engine.connection() - - # create/drop tables based on table metadata objects - # (see the next section, Table Metadata, for info on table metadata) - engine.create(mytable) - engine.drop(mytable) - - # get the DBAPI module being used - dbapi = engine.dbapi() - - # get the default schema name - name = engine.get_default_schema_name() - - # execute some SQL directly, returns a ResultProxy (see the SQL Construction section for details) - result = engine.execute("select * from table where col1=:col1", {'col1':'foo'}) - - # log a message to the engine's log stream - engine.log('this is a message') - - </&> - </&> - - <&|doclib.myt:item, name="options", description="Database Engine Options" &> - <p>The remaining arguments to <span class="codeline">create_engine</span> are keyword arguments that are passed to the specific subclass of <span class="codeline">sqlalchemy.engine.SQLEngine</span> being used, as well as the underlying <span class="codeline">sqlalchemy.pool.Pool</span> instance. All of the options described in the previous section <&formatting.myt:link, path="pooling_configuration"&> can be specified, as well as engine-specific options:</p> - <ul> - <li><p>pool=None : an instance of <span class="codeline">sqlalchemy.pool.Pool</span> to be used as the underlying source for connections, overriding the engine's connect arguments (pooling is described in the previous section). If None, a default Pool (QueuePool or SingletonThreadPool as appropriate) will be created using the engine's connect arguments.</p> - <p>Example:</p> - <&|formatting.myt:code&> - from sqlalchemy import * - import sqlalchemy.pool as pool - import MySQLdb - - def getconn(): - return MySQLdb.connect(user='ed', dbname='mydb') - - engine = create_engine('mysql', pool=pool.QueuePool(getconn, pool_size=20, max_overflow=40)) - </&></li> - <li>echo=False : if True, the SQLEngine will log all statements as well as a repr() of their parameter lists to the engines logger, which defaults to sys.stdout. A SQLEngine instances' "echo" data member can be modified at any time to turn logging on and off. If set to the string 'debug', result rows will be printed to the standard output as well.</li> - <li>logger=None : a file-like object where logging output can be sent, if echo is set to True. This defaults to sys.stdout.</li> - <li>module=None : used by Oracle and Postgres, this is a reference to a DBAPI2 module to be used instead of the engine's default module. For Postgres, the default is psycopg2, or psycopg1 if 2 cannot be found. For Oracle, its cx_Oracle.</li> - <li>default_ordering=False : if True, table objects and associated joins and aliases will generate information used for ordering by primary keys (or OIDs, if the database supports OIDs). This information is used by the Mapper system to when it constructs select queries to supply a default ordering to mapped objects.</li> - <li>use_ansi=True : used only by Oracle; when False, the Oracle driver attempts to support a particular "quirk" of some Oracle databases, that the LEFT OUTER JOIN SQL syntax is not supported, and the "Oracle join" syntax of using <% "<column1>(+)=<column2>" |h%> must be used in order to achieve a LEFT OUTER JOIN. Its advised that the Oracle database be configured to have full ANSI support instead of using this feature.</li> - <li>use_oids=False : used only by Postgres, will enable the column name "oid" as the object ID column. Postgres as of 8.1 has object IDs disabled by default.</li> - <li>convert_unicode=False : if set to True, all String/character based types will convert Unicode values to raw byte values going into the database, and all raw byte values to Python Unicode coming out in result sets. This is an engine-wide method to provide unicode across the board. For unicode conversion on a column-by-column level, use the Unicode column type instead.</li> - <li>encoding='utf-8' : the encoding to use for Unicode translations - passed to all encode/decode methods.</li> - <li>echo_uow=False : when True, logs unit of work commit plans to the standard output.</li> - </ul> - </&> - <&|doclib.myt:item, name="proxy", description="Using the Proxy Engine" &> - <p>The ProxyEngine is useful for applications that need to swap engines - at runtime, or to create their tables and mappers before they know - what engine they will use. One use case is an application meant to be - pluggable into a mix of other applications, such as a WSGI - application. Well-behaved WSGI applications should be relocatable; and - since that means that two versions of the same application may be - running in the same process (or in the same thread at different - times), WSGI applications ought not to depend on module-level or - global configuration. Using the ProxyEngine allows a WSGI application - to define tables and mappers in a module, but keep the specific - database connection uri as an application instance or thread-local - value.</p> - - <p>The ProxyEngine is used in the same way as any other engine, with one - additional method:</p> - - <&|formatting.myt:code&> - # define the tables and mappers - from sqlalchemy import * - from sqlalchemy.ext.proxy import ProxyEngine - - engine = ProxyEngine() - - users = Table('users', engine, ... ) - - class Users(object): - pass - - assign_mapper(Users, users) - - def app(environ, start_response): - # later, connect the proxy engine to a real engine via the connect() method - engine.connect(environ['db_uri']) - # now you have a real db connection and can select, insert, etc. - </&> - - <&|doclib.myt:item, name="defaultproxy", description="Using the Global Proxy" &> - <p>There is an instance of ProxyEngine available within the schema package as "default_engine". You can construct Table objects and not specify the engine parameter, and they will connect to this engine by default. To connect the default_engine, use the <span class="codeline">global_connect</span> function.</p> - <&|formatting.myt:code&> - # define the tables and mappers - from sqlalchemy import * - - # specify a table with no explicit engine - users = Table('users', - Column('user_id', Integer, primary_key=True), - Column('user_name', String) - ) - - # connect the global proxy engine - global_connect('sqlite://filename=foo.db') - - # create the table in the selected database - users.create() - </&> - - </&> - </&> - <&|doclib.myt:item, name="transactions", description="Transactions" &> - <p>A SQLEngine also provides an interface to the transactional capabilities of the underlying DBAPI connection object, as well as the connection object itself. Note that when using the object-relational-mapping package, described in a later section, basic transactional operation is handled for you automatically by its "Unit of Work" system; the methods described here will usually apply just to literal SQL update/delete/insert operations or those performed via the SQL construction library.</p> - - <p>Typically, a connection is opened with "autocommit=False". So to perform SQL operations and just commit as you go, you can simply pull out a connection from the connection pool, keep it in the local scope, and call commit() on it as needed. As long as the connection remains referenced, all other SQL operations within the same thread will use this same connection, including those used by the SQL construction system as well as the object-relational mapper, both described in later sections:</p> - <&|formatting.myt:code&> - conn = engine.connection() - - # execute SQL via the engine - engine.execute("insert into mytable values ('foo', 'bar')") - conn.commit() - - # execute SQL via the SQL construction library - mytable.insert().execute(col1='bat', col2='lala') - conn.commit() - - </&> - - <p>There is a more automated way to do transactions, and that is to use the engine's begin()/commit() functionality. When the begin() method is called off the engine, a connection is checked out from the pool and stored in a thread-local context. That way, all subsequent SQL operations within the same thread will use that same connection. Subsequent commit() or rollback() operations are performed against that same connection. In effect, its a more automated way to perform the "commit as you go" example above. </p> - - <&|formatting.myt:code&> - engine.begin() - engine.execute("insert into mytable values ('foo', 'bar')") - mytable.insert().execute(col1='foo', col2='bar') - engine.commit() - </&> - - <P>A traditional "rollback on exception" pattern looks like this:</p> - - <&|formatting.myt:code&> - engine.begin() - try: - engine.execute("insert into mytable values ('foo', 'bar')") - mytable.insert().execute(col1='foo', col2='bar') - except: - engine.rollback() - raise - engine.commit() - </&> - - <p>An shortcut which is equivalent to the above is provided by the <span class="codeline">transaction</span> method:</p> - - <&|formatting.myt:code&> - def do_stuff(): - engine.execute("insert into mytable values ('foo', 'bar')") - mytable.insert().execute(col1='foo', col2='bar') - - engine.transaction(do_stuff) - </&> - <p>An added bonus to the engine's transaction methods is "reentrant" functionality; once you call begin(), subsequent calls to begin() will increment a counter that must be decremented corresponding to each commit() statement before an actual commit can happen. This way, any number of methods that want to insure a transaction can call begin/commit, and be nested arbitrarily:</p> - <&|formatting.myt:code&> - - # method_a starts a transaction and calls method_b - def method_a(): - engine.begin() - try: - method_b() - except: - engine.rollback() - raise - engine.commit() - - # method_b starts a transaction, or joins the one already in progress, - # and does some SQL - def method_b(): - engine.begin() - try: - engine.execute("insert into mytable values ('bat', 'lala')") - mytable.insert().execute(col1='bat', col2='lala') - except: - engine.rollback() - raise - engine.commit() - - # call method_a - method_a() - - </&> - <p>Above, method_a is called first, which calls engine.begin(). Then it calls method_b. When method_b calls engine.begin(), it just increments a counter that is decremented when it calls commit(). If either method_a or method_b calls rollback(), the whole transaction is rolled back. The transaction is not committed until method_a calls the commit() method.</p> - - <p>The object-relational-mapper capability of SQLAlchemy includes its own <span class="codeline">commit()</span> method that gathers SQL statements into a batch and runs them within one transaction. That transaction is also invokved within the scope of the "reentrant" methodology above; so multiple objectstore.commit() operations can also be bundled into a larger database transaction via the above methodology.</p> - </&> -</&> </del></span></pre></div> <a id="sqlalchemytrunkdocbuildcontentdbenginetxt"></a> <div class="addfile"><h4>Added: sqlalchemy/trunk/doc/build/content/dbengine.txt (1241 => 1242)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/trunk/doc/build/content/dbengine.txt 2006-04-03 01:35:48 UTC (rev 1241) +++ sqlalchemy/trunk/doc/build/content/dbengine.txt 2006-04-03 02:00:18 UTC (rev 1242) </span><span class="lines">@@ -0,0 +1,257 @@ </span><ins>+Database Engines +================ + +A database engine is a subclass of `sqlalchemy.engine.SQLEngine`, and is the starting point for where SQLAlchemy provides a layer of abstraction on top of the various DBAPI2 database modules. It serves as an abstract factory for database-specific implementation objects as well as a layer of abstraction over the most essential tasks of a database connection, including connecting, executing queries, returning result sets, and managing transactions. + +The average developer doesn't need to know anything about the interface or workings of a SQLEngine in order to use it. Simply creating one, and then specifying it when constructing tables and other SQL objects is all that's needed. + +A SQLEngine is also a layer of abstraction on top of the connection pooling described in the previous section. While a DBAPI connection pool can be used explicitly alongside a SQLEngine, its not really necessary. Once you have a SQLEngine, you can retrieve pooled connections directly from its underlying connection pool via its own `connection()` method. However, if you're exclusively using SQLALchemy's SQL construction objects and/or object-relational mappers, all the details of connecting are handled by those libraries automatically. + + +### Establishing a Database Engine {@name=establishing} + +Engines exist for SQLite, Postgres, MySQL, MS-SQL, and Oracle, using the Pysqlite, Psycopg (1 or 2), MySQLDB, adodbapi or pymssql, and cx_Oracle modules (there is also experimental support for Firebird). Each engine imports its corresponding module which is required to be installed. For Postgres and Oracle, an alternate module may be specified at construction time as well. + +The string based argument names for connecting are translated to the appropriate names when the connection is made; argument names include "host" or "hostname" for database host, "database", "db", or "dbname" for the database name (also is dsn for Oracle), "user" or "username" for the user, and "password", "pw", or "passwd" for the password. SQLite expects "filename" or "file" for the filename, or if None it defaults to "":memory:". + +The connection arguments can be specified as a string + dictionary pair, or a single URL-encoded string, as follows: + + {python}from sqlalchemy import * + + # sqlite in memory + sqlite_engine = create_engine('sqlite', {'filename':':memory:'}, **opts) + + # via URL + sqlite_engine = create_engine('sqlite://', **opts) + + # sqlite using a file + sqlite_engine = create_engine('sqlite', {'filename':'querytest.db'}, **opts) + + # via URL + sqlite_engine = create_engine('sqlite://filename=querytest.db', **opts) + + # postgres + postgres_engine = create_engine('postgres', + {'database':'test', + 'host':'127.0.0.1', + 'user':'scott', + 'password':'tiger'}, **opts) + + # via URL + postgres_engine = create_engine('postgres://database=test&amp;host=127.0.0.1&amp;user=scott&amp;password=tiger') + + # mysql + mysql_engine = create_engine('mysql', + { + 'db':'mydb', + 'user':'scott', + 'passwd':'tiger', + 'host':'127.0.0.1' + } + **opts) + # oracle + oracle_engine = create_engine('oracle', + {'dsn':'mydsn', + 'user':'scott', + 'password':'tiger'}, **opts) + + + +Note that the general form of connecting to an engine is: + + {python}# separate arguments + engine = create_engine( + <enginename>, + {<named DBAPI arguments>}, + <sqlalchemy options> + ) + + # url + engine = create_engine('&lt;enginename&gt;://&lt;named DBAPI arguments&gt;', <sqlalchemy options>) + +### Database Engine Methods {@name=methods} + +A few useful methods off the SQLEngine are described here: + + {python}engine = create_engine('postgres://hostname=localhost&amp;user=scott&amp;password=tiger&amp;database=test') + + # get a pooled DBAPI connection + conn = engine.connection() + + # create/drop tables based on table metadata objects + # (see the next section, Table Metadata, for info on table metadata) + engine.create(mytable) + engine.drop(mytable) + + # get the DBAPI module being used + dbapi = engine.dbapi() + + # get the default schema name + name = engine.get_default_schema_name() + + # execute some SQL directly, returns a ResultProxy (see the SQL Construction section for details) + result = engine.execute("select * from table where col1=:col1", {'col1':'foo'}) + + # log a message to the engine's log stream + engine.log('this is a message') + +### Database Engine Options {@name=options} + +The remaining arguments to `create_engine` are keyword arguments that are passed to the specific subclass of `sqlalchemy.engine.SQLEngine` being used, as well as the underlying `sqlalchemy.pool.Pool` instance. All of the options described in the previous section [pooling_configuration][pooling_configuration] can be specified, as well as engine-specific options: + +* pool=None : an instance of `sqlalchemy.pool.Pool` to be used as the underlying source for connections, overriding the engine's connect arguments (pooling is described in the previous section). If None, a default Pool (QueuePool or SingletonThreadPool as appropriate) will be created using the engine's connect arguments. + +Example: + + {python}from sqlalchemy import * + import sqlalchemy.pool as pool + import MySQLdb + + def getconn(): + return MySQLdb.connect(user='ed', dbname='mydb') + + engine = create_engine('mysql', pool=pool.QueuePool(getconn, pool_size=20, max_overflow=40)) + +* echo=False : if True, the SQLEngine will log all statements as well as a repr() of their parameter lists to the engines logger, which defaults to sys.stdout. A SQLEngine instances' "echo" data member can be modified at any time to turn logging on and off. If set to the string 'debug', result rows will be printed to the standard output as well. +* logger=None : a file-like object where logging output can be sent, if echo is set to True. This defaults to sys.stdout. +* module=None : used by Oracle and Postgres, this is a reference to a DBAPI2 module to be used instead of the engine's default module. For Postgres, the default is psycopg2, or psycopg1 if 2 cannot be found. For Oracle, its cx_Oracle. +* default_ordering=False : if True, table objects and associated joins and aliases will generate information used for ordering by primary keys (or OIDs, if the database supports OIDs). This information is used by the Mapper system to when it constructs select queries to supply a default ordering to mapped objects. +* use_ansi=True : used only by Oracle; when False, the Oracle driver attempts to support a particular "quirk" of some Oracle databases, that the LEFT OUTER JOIN SQL syntax is not supported, and the "Oracle join" syntax of using &lt;column1&gt;(+)=&lt;column2&gt; must be used in order to achieve a LEFT OUTER JOIN. Its advised that the Oracle database be configured to have full ANSI support instead of using this feature. +* use_oids=False : used only by Postgres, will enable the column name "oid" as the object ID column. Postgres as of 8.1 has object IDs disabled by default. +* convert_unicode=False : if set to True, all String/character based types will convert Unicode values to raw byte values going into the database, and all raw byte values to Python Unicode coming out in result sets. This is an engine-wide method to provide unicode across the board. For unicode conversion on a column-by-column level, use the Unicode column type instead. +* encoding='utf-8' : the encoding to use for Unicode translations - passed to all encode/decode methods. +* echo_uow=False : when True, logs unit of work commit plans to the standard output. + +### Using the Proxy Engine {@name=proxy} + +The ProxyEngine is useful for applications that need to swap engines +at runtime, or to create their tables and mappers before they know +what engine they will use. One use case is an application meant to be +pluggable into a mix of other applications, such as a WSGI +application. Well-behaved WSGI applications should be relocatable; and +since that means that two versions of the same application may be +running in the same process (or in the same thread at different +times), WSGI applications ought not to depend on module-level or +global configuration. Using the ProxyEngine allows a WSGI application +to define tables and mappers in a module, but keep the specific +database connection uri as an application instance or thread-local +value. + +The ProxyEngine is used in the same way as any other engine, with one +additional method: + + {python}# define the tables and mappers + from sqlalchemy import * + from sqlalchemy.ext.proxy import ProxyEngine + + engine = ProxyEngine() + + users = Table('users', engine, ... ) + + class Users(object): + pass + + assign_mapper(Users, users) + + def app(environ, start_response): + # later, connect the proxy engine to a real engine via the connect() method + engine.connect(environ['db_uri']) + # now you have a real db connection and can select, insert, etc. + + +#### Using the Global Proxy {@name=defaultproxy} + +There is an instance of ProxyEngine available within the schema package as `default_engine`. You can construct Table objects and not specify the engine parameter, and they will connect to this engine by default. To connect the default_engine, use the `global_connect` function. + + {python}# define the tables and mappers + from sqlalchemy import * + + # specify a table with no explicit engine + users = Table('users', + Column('user_id', Integer, primary_key=True), + Column('user_name', String) + ) + + # connect the global proxy engine + global_connect('sqlite://filename=foo.db') + + # create the table in the selected database + users.create() + + +### Transactions {@name=transactions} + +A SQLEngine also provides an interface to the transactional capabilities of the underlying DBAPI connection object, as well as the connection object itself. Note that when using the object-relational-mapping package, described in a later section, basic transactional operation is handled for you automatically by its "Unit of Work" system; the methods described here will usually apply just to literal SQL update/delete/insert operations or those performed via the SQL construction library. + +Typically, a connection is opened with `autocommit=False`. So to perform SQL operations and just commit as you go, you can simply pull out a connection from the connection pool, keep it in the local scope, and call commit() on it as needed. As long as the connection remains referenced, all other SQL operations within the same thread will use this same connection, including those used by the SQL construction system as well as the object-relational mapper, both described in later sections: + + {python}conn = engine.connection() + + # execute SQL via the engine + engine.execute("insert into mytable values ('foo', 'bar')") + conn.commit() + + # execute SQL via the SQL construction library + mytable.insert().execute(col1='bat', col2='lala') + conn.commit() + +There is a more automated way to do transactions, and that is to use the engine's begin()/commit() functionality. When the begin() method is called off the engine, a connection is checked out from the pool and stored in a thread-local context. That way, all subsequent SQL operations within the same thread will use that same connection. Subsequent commit() or rollback() operations are performed against that same connection. In effect, its a more automated way to perform the "commit as you go" example above. + + {python}engine.begin() + engine.execute("insert into mytable values ('foo', 'bar')") + mytable.insert().execute(col1='foo', col2='bar') + engine.commit() + + +A traditional "rollback on exception" pattern looks like this: + + {python}engine.begin() + try: + engine.execute("insert into mytable values ('foo', 'bar')") + mytable.insert().execute(col1='foo', col2='bar') + except: + engine.rollback() + raise + engine.commit() + + +An shortcut which is equivalent to the above is provided by the `transaction` method: + + {python}def do_stuff(): + engine.execute("insert into mytable values ('foo', 'bar')") + mytable.insert().execute(col1='foo', col2='bar') + + engine.transaction(do_stuff) + +An added bonus to the engine's transaction methods is "reentrant" functionality; once you call begin(), subsequent calls to begin() will increment a counter that must be decremented corresponding to each commit() statement before an actual commit can happen. This way, any number of methods that want to insure a transaction can call begin/commit, and be nested arbitrarily: + + {python}# method_a starts a transaction and calls method_b + def method_a(): + engine.begin() + try: + method_b() + except: + engine.rollback() + raise + engine.commit() + + # method_b starts a transaction, or joins the one already in progress, + # and does some SQL + def method_b(): + engine.begin() + try: + engine.execute("insert into mytable values ('bat', 'lala')") + mytable.insert().execute(col1='bat', col2='lala') + except: + engine.rollback() + raise + engine.commit() + + # call method_a + method_a() + +Above, `method_a` is called first, which calls `engine.begin()`. Then it calls `method_b`. When `method_b` calls `engine.begin()`, it just increments a counter that is decremented when it calls `commit()`. If either `method_a` or `method_b` calls `rollback()`, the whole transaction is rolled back. The transaction is not committed until `method_a` calls the `commit()` method. + +The object-relational-mapper capability of SQLAlchemy includes its own `commit()` method that gathers SQL statements into a batch and runs them within one transaction. That transaction is also invokved within the scope of the "reentrant" methodology above; so multiple objectstore.commit() operations can also be bundled into a larger database transaction via the above methodology. + + </ins></span></pre> </div> </div> </body> </html> |
From: <co...@sq...> - 2006-04-03 01:36: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>[1241] sqlalchemy/trunk/test: merged Rick Morrison / Runar Petursson's MS-SQL module, with adjustments to alias schema-qualified Table objects</title> </head> <body> <div id="msg"> <dl> <dt>Revision</dt> <dd>1241</dd> <dt>Author</dt> <dd>zzzeek</dd> <dt>Date</dt> <dd>2006-04-02 20:35:48 -0500 (Sun, 02 Apr 2006)</dd> </dl> <h3>Log Message</h3> <pre>merged Rick Morrison / Runar Petursson's MS-SQL module, with adjustments to alias schema-qualified Table objects</pre> <h3>Modified Paths</h3> <ul> <li><a href="#sqlalchemytrunklibsqlalchemydatabases__init__py">sqlalchemy/trunk/lib/sqlalchemy/databases/__init__.py</a></li> <li><a href="#sqlalchemytrunklibsqlalchemydatabasesinformation_schemapy">sqlalchemy/trunk/lib/sqlalchemy/databases/information_schema.py</a></li> <li><a href="#sqlalchemytrunklibsqlalchemyenginepy">sqlalchemy/trunk/lib/sqlalchemy/engine.py</a></li> <li><a href="#sqlalchemytrunklibsqlalchemyschemapy">sqlalchemy/trunk/lib/sqlalchemy/schema.py</a></li> <li><a href="#sqlalchemytrunktestindexespy">sqlalchemy/trunk/test/indexes.py</a></li> <li><a href="#sqlalchemytrunktestselectpy">sqlalchemy/trunk/test/select.py</a></li> <li><a href="#sqlalchemytrunktesttestbasepy">sqlalchemy/trunk/test/testbase.py</a></li> <li><a href="#sqlalchemytrunktesttesttypespy">sqlalchemy/trunk/test/testtypes.py</a></li> </ul> </div> <div id="patch"> <h3>Diff</h3> <a id="sqlalchemytrunklibsqlalchemydatabases__init__py"></a> <div class="modfile"><h4>Modified: sqlalchemy/trunk/lib/sqlalchemy/databases/__init__.py (1240 => 1241)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/trunk/lib/sqlalchemy/databases/__init__.py 2006-04-02 22:56:19 UTC (rev 1240) +++ sqlalchemy/trunk/lib/sqlalchemy/databases/__init__.py 2006-04-03 01:35:48 UTC (rev 1241) </span><span class="lines">@@ -5,4 +5,4 @@ </span><span class="cx"> # the MIT License: http://www.opensource.org/licenses/mit-license.php </span><span class="cx"> </span><span class="cx"> </span><del>-__all__ = ['oracle', 'postgres', 'sqlite', 'mysql'] </del><span class="cx">\ No newline at end of file </span><ins>+__all__ = ['oracle', 'postgres', 'sqlite', 'mysql', 'mssql'] </ins></span></pre></div> <a id="sqlalchemytrunklibsqlalchemydatabasesinformation_schemapy"></a> <div class="modfile"><h4>Modified: sqlalchemy/trunk/lib/sqlalchemy/databases/information_schema.py (1240 => 1241)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/trunk/lib/sqlalchemy/databases/information_schema.py 2006-04-02 22:56:19 UTC (rev 1240) +++ sqlalchemy/trunk/lib/sqlalchemy/databases/information_schema.py 2006-04-03 01:35:48 UTC (rev 1241) </span><span class="lines">@@ -56,6 +56,18 @@ </span><span class="cx"> Column("constraint_name", String), </span><span class="cx"> schema="information_schema") </span><span class="cx"> </span><ins>+gen_ref_constraints = schema.Table("referential_constraints", generic_engine, + Column("constraint_catalog", String), + Column("constraint_schema", String), + Column("constraint_name", String), + Column("unique_constraint_catlog", String), + Column("unique_constraint_schema", String), + Column("unique_constraint_name", String), + Column("match_option", String), + Column("update_rule", String), + Column("delete_rule", String), + schema="information_schema") + </ins><span class="cx"> class ISchema(object): </span><span class="cx"> def __init__(self, engine): </span><span class="cx"> self.engine = engine </span></span></pre></div> <a id="sqlalchemytrunklibsqlalchemyenginepy"></a> <div class="modfile"><h4>Modified: sqlalchemy/trunk/lib/sqlalchemy/engine.py (1240 => 1241)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/trunk/lib/sqlalchemy/engine.py 2006-04-02 22:56:19 UTC (rev 1240) +++ sqlalchemy/trunk/lib/sqlalchemy/engine.py 2006-04-03 01:35:48 UTC (rev 1241) </span><span class="lines">@@ -193,7 +193,7 @@ </span><span class="cx"> connection = property(_connection, doc="the connection represented by this SQLSession. The connection is late-connecting, meaning the call to the connection pool only occurs when it is first called (and the pool will typically only connect the first time it is called as well)") </span><span class="cx"> </span><span class="cx"> def begin(self): </span><del>- """begins" a transaction on this SQLSession's connection. repeated calls to begin() will increment a counter that must be decreased by corresponding commit() statements before an actual commit occurs. this is to provide "nested" behavior of transactions so that different functions in a particular call stack can call begin()/commit() independently of each other without knowledge of an existing transaction.""" </del><ins>+ """begins a transaction on this SQLSession's connection. repeated calls to begin() will increment a counter that must be decreased by corresponding commit() statements before an actual commit occurs. this is to provide "nested" behavior of transactions so that different functions in a particular call stack can call begin()/commit() independently of each other without knowledge of an existing transaction. """ </ins><span class="cx"> if self.__tcount == 0: </span><span class="cx"> self.__transaction = self.connection </span><span class="cx"> self.engine.do_begin(self.connection) </span><span class="lines">@@ -506,7 +506,7 @@ </span><span class="cx"> self.commit() </span><span class="cx"> </span><span class="cx"> def begin(self): </span><del>- """"begins a transaction on the current thread's SQLSession.""" </del><ins>+ """ begins a transaction on the current thread SQLSession. """ </ins><span class="cx"> self.session.begin() </span><span class="cx"> </span><span class="cx"> def rollback(self): </span><span class="lines">@@ -647,7 +647,7 @@ </span><span class="cx"> return ResultProxy(cursor, self, typemap=compiled.typemap) </span><span class="cx"> </span><span class="cx"> def execute(self, statement, parameters=None, connection=None, cursor=None, echo=None, typemap=None, commit=False, return_raw=False, **kwargs): </span><del>- """executes the given string-based SQL statement with the given parameters. </del><ins>+ """ executes the given string-based SQL statement with the given parameters. </ins><span class="cx"> </span><span class="cx"> The parameters can be a dictionary or a list, or a list of dictionaries or lists, depending </span><span class="cx"> on the paramstyle of the DBAPI. </span><span class="lines">@@ -659,7 +659,7 @@ </span><span class="cx"> up. </span><span class="cx"> </span><span class="cx"> In all error cases, a rollback() is immediately performed on the connection before </span><del>- propigating the exception outwards. </del><ins>+ propagating the exception outwards. </ins><span class="cx"> </span><span class="cx"> Other options include: </span><span class="cx"> </span></span></pre></div> <a id="sqlalchemytrunklibsqlalchemyschemapy"></a> <div class="modfile"><h4>Modified: sqlalchemy/trunk/lib/sqlalchemy/schema.py (1240 => 1241)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/trunk/lib/sqlalchemy/schema.py 2006-04-02 22:56:19 UTC (rev 1240) +++ sqlalchemy/trunk/lib/sqlalchemy/schema.py 2006-04-03 01:35:48 UTC (rev 1241) </span><span class="lines">@@ -256,7 +256,7 @@ </span><span class="cx"> default=None : a scalar, python callable, or ClauseElement representing the "default value" for this column, </span><span class="cx"> which will be invoked upon insert if this column is not present in the insert list or is given a value </span><span class="cx"> of None. </span><del>- </del><ins>+ </ins><span class="cx"> hidden=False : indicates this column should not be listed in the </span><span class="cx"> table's list of columns. Used for the "oid" column, which generally </span><span class="cx"> isnt in column lists. </span><span class="lines">@@ -271,7 +271,9 @@ </span><span class="cx"> indexed in a unique index . Pass true to autogenerate the index </span><span class="cx"> name. Pass a string to specify the index name. Multiple columns that </span><span class="cx"> specify the same index name will all be included in the index, in the </span><del>- order of their creation. """ </del><ins>+ order of their creation. + + """ </ins><span class="cx"> </span><span class="cx"> name = str(name) # in case of incoming unicode </span><span class="cx"> super(Column, self).__init__(name, None, type) </span><span class="lines">@@ -507,6 +509,7 @@ </span><span class="cx"> """calls the visit_seauence method on the given visitor.""" </span><span class="cx"> return visitor.visit_sequence(self) </span><span class="cx"> </span><ins>+ </ins><span class="cx"> class Index(SchemaItem): </span><span class="cx"> """Represents an index of columns from a database table </span><span class="cx"> """ </span></span></pre></div> <a id="sqlalchemytrunktestindexespy"></a> <div class="modfile"><h4>Modified: sqlalchemy/trunk/test/indexes.py (1240 => 1241)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/trunk/test/indexes.py 2006-04-02 22:56:19 UTC (rev 1240) +++ sqlalchemy/trunk/test/indexes.py 2006-04-03 01:35:48 UTC (rev 1241) </span><span class="lines">@@ -42,9 +42,10 @@ </span><span class="cx"> """test that mixed-case index identifiers are legal""" </span><span class="cx"> employees = Table('companyEmployees', testbase.db, </span><span class="cx"> Column('id', Integer, primary_key=True), </span><del>- Column('firstName', String), - Column('lastName', String), - Column('emailAddress', String)) </del><ins>+ Column('firstName', String(30)), + Column('lastName', String(30)), + Column('emailAddress', String(30))) + </ins><span class="cx"> employees.create() </span><span class="cx"> self.created.append(employees) </span><span class="cx"> </span></span></pre></div> <a id="sqlalchemytrunktestselectpy"></a> <div class="modfile"><h4>Modified: sqlalchemy/trunk/test/select.py (1240 => 1241)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/trunk/test/select.py 2006-04-02 22:56:19 UTC (rev 1240) +++ sqlalchemy/trunk/test/select.py 2006-04-03 01:35:48 UTC (rev 1241) </span><span class="lines">@@ -6,6 +6,7 @@ </span><span class="cx"> import sqlalchemy.databases.sqlite as sqlite </span><span class="cx"> </span><span class="cx"> db = ansisql.engine() </span><ins>+#db = create_engine('mssql') </ins><span class="cx"> </span><span class="cx"> from testbase import PersistTest </span><span class="cx"> import unittest, re </span><span class="lines">@@ -595,6 +596,7 @@ </span><span class="cx"> </span><span class="cx"> class SchemaTest(SQLTest): </span><span class="cx"> def testselect(self): </span><ins>+ # these tests will fail with the MS-SQL compiler since it will alias schema-qualified tables </ins><span class="cx"> self.runtest(table4.select(), "SELECT remotetable.rem_id, remotetable.datatype_id, remotetable.value FROM remote_owner.remotetable") </span><span class="cx"> self.runtest(table4.select(and_(table4.c.datatype_id==7, table4.c.value=='hi')), "SELECT remotetable.rem_id, remotetable.datatype_id, remotetable.value FROM remote_owner.remotetable WHERE remotetable.datatype_id = :remotetable_datatype_id AND remotetable.value = :remotetable_value") </span><span class="cx"> </span></span></pre></div> <a id="sqlalchemytrunktesttestbasepy"></a> <div class="modfile"><h4>Modified: sqlalchemy/trunk/test/testbase.py (1240 => 1241)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/trunk/test/testbase.py 2006-04-02 22:56:19 UTC (rev 1240) +++ sqlalchemy/trunk/test/testbase.py 2006-04-03 01:35:48 UTC (rev 1241) </span><span class="lines">@@ -46,9 +46,11 @@ </span><span class="cx"> elif DBTYPE == 'oracle8': </span><span class="cx"> db_uri = 'oracle://user=scott&password=tiger' </span><span class="cx"> opts = {'use_ansi':False} </span><ins>+ elif DBTYPE == 'mssql': + db_uri = 'mssql://database=test&user=scott&password=tiger' </ins><span class="cx"> </span><span class="cx"> if not db_uri: </span><del>- raise "Could not create engine. specify --db <sqlite|sqlite_file|postgres|mysql|oracle> to test runner." </del><ins>+ raise "Could not create engine. specify --db <sqlite|sqlite_file|postgres|mysql|oracle|oracle8|mssql> to test runner." </ins><span class="cx"> </span><span class="cx"> if PROXY: </span><span class="cx"> db = proxy.ProxyEngine(echo=echo, default_ordering=True, **opts) </span></span></pre></div> <a id="sqlalchemytrunktesttesttypespy"></a> <div class="modfile"><h4>Modified: sqlalchemy/trunk/test/testtypes.py (1240 => 1241)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/trunk/test/testtypes.py 2006-04-02 22:56:19 UTC (rev 1240) +++ sqlalchemy/trunk/test/testtypes.py 2006-04-03 01:35:48 UTC (rev 1241) </span><span class="lines">@@ -147,8 +147,8 @@ </span><span class="cx"> def testbinary(self): </span><span class="cx"> stream1 =self.get_module_stream('sqlalchemy.sql') </span><span class="cx"> stream2 =self.get_module_stream('sqlalchemy.engine') </span><del>- binary_table.insert().execute(misc='sql.pyc', data=stream1, data_slice=stream1[0:100]) - binary_table.insert().execute(misc='engine.pyc', data=stream2, data_slice=stream2[0:99]) </del><ins>+ binary_table.insert().execute(primary_id=1, misc='sql.pyc', data=stream1, data_slice=stream1[0:100]) + binary_table.insert().execute(primary_id=2, misc='engine.pyc', data=stream2, data_slice=stream2[0:99]) </ins><span class="cx"> l = binary_table.select().execute().fetchall() </span><span class="cx"> print len(stream1), len(l[0]['data']), len(l[0]['data_slice']) </span><span class="cx"> self.assert_(list(stream1) == list(l[0]['data'])) </span><span class="lines">@@ -179,7 +179,7 @@ </span><span class="cx"> collist = [Column('user_id', INT, primary_key = True), Column('user_name', VARCHAR(20)), Column('user_datetime', DateTime), </span><span class="cx"> Column('user_date', Date), Column('user_time', Time)] </span><span class="cx"> </span><del>- if db.engine.__module__.endswith('mysql'): </del><ins>+ if db.engine.__module__.endswith('mysql') or db.engine.__module__.endswith('mssql'): </ins><span class="cx"> # strip microseconds -- not supported by this engine (should be an easier way to detect this) </span><span class="cx"> for d in insert_data: </span><span class="cx"> if d[2] is not None: </span><span class="lines">@@ -198,6 +198,7 @@ </span><span class="cx"> users_with_date = Table('query_users_with_date', db, redefine = True, *collist) </span><span class="cx"> users_with_date.create() </span><span class="cx"> insert_dicts = [dict(zip(fnames, d)) for d in insert_data] </span><ins>+ </ins><span class="cx"> for idict in insert_dicts: </span><span class="cx"> users_with_date.insert().execute(**idict) # insert the data </span><span class="cx"> </span></span></pre> </div> </div> </body> </html> |
From: <co...@sq...> - 2006-04-02 22:56:32
|
<!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>[1240] sqlalchemy/trunk/doc/build/txt2myt.py: extra sql statements...</title> </head> <body> <div id="msg"> <dl> <dt>Revision</dt> <dd>1240</dd> <dt>Author</dt> <dd>zzzeek</dd> <dt>Date</dt> <dd>2006-04-02 17:56:19 -0500 (Sun, 02 Apr 2006)</dd> </dl> <h3>Log Message</h3> <pre>extra sql statements...</pre> <h3>Modified Paths</h3> <ul> <li><a href="#sqlalchemytrunkdocbuildtxt2mytpy">sqlalchemy/trunk/doc/build/txt2myt.py</a></li> </ul> </div> <div id="patch"> <h3>Diff</h3> <a id="sqlalchemytrunkdocbuildtxt2mytpy"></a> <div class="modfile"><h4>Modified: sqlalchemy/trunk/doc/build/txt2myt.py (1239 => 1240)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/trunk/doc/build/txt2myt.py 2006-04-02 22:52:13 UTC (rev 1239) +++ sqlalchemy/trunk/doc/build/txt2myt.py 2006-04-02 22:56:19 UTC (rev 1240) </span><span class="lines">@@ -92,7 +92,7 @@ </span><span class="cx"> # consumed as Myghty comments. </span><span class="cx"> text = re.compile(r'^(?!<&)', re.M).sub(' ', text) </span><span class="cx"> </span><del>- sqlre = re.compile(r'{sql}(.*?)((?:SELECT|INSERT|DELETE|UPDATE).*?)\n\s*(\n|$)', re.S) </del><ins>+ sqlre = re.compile(r'{sql}(.*?)((?:SELECT|INSERT|DELETE|UPDATE|CREATE|DROP).*?)\n\s*(\n|$)', re.S) </ins><span class="cx"> text = sqlre.sub(r"<&formatting.myt:poplink&>\1\n<&|formatting.myt:codepopper, link='sql'&>\2</&>\n\n", text) </span><span class="cx"> </span><span class="cx"> pre_parent = parent[pre] </span></span></pre> </div> </div> </body> </html> |
From: <co...@sq...> - 2006-04-02 22:52:24
|
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head><style type="text/css"><!-- #msg dl { border: 1px #006 solid; background: #369; padding: 6px; color: #fff; } #msg dt { float: left; width: 6em; font-weight: bold; } #msg dt:after { content:':';} #msg dl, #msg dt, #msg ul, #msg li { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; } #msg dl a { font-weight: bold} #msg dl a:link { color:#fc3; } #msg dl a:active { color:#ff0; } #msg dl a:visited { color:#cc6; } h3 { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; font-weight: bold; } #msg pre { overflow: auto; background: #ffc; border: 1px #fc0 solid; padding: 6px; } #msg ul, pre { overflow: auto; } #patch { width: 100%; } #patch h4 {font-family: verdana,arial,helvetica,sans-serif;font-size:10pt;padding:8px;background:#369;color:#fff;margin:0;} #patch .propset h4, #patch .binary h4 {margin:0;} #patch pre {padding:0;line-height:1.2em;margin:0;} #patch .diff {width:100%;background:#eee;padding: 0 0 10px 0;overflow:auto;} #patch .propset .diff, #patch .binary .diff {padding:10px 0;} #patch span {display:block;padding:0 10px;} #patch .modfile, #patch .addfile, #patch .delfile, #patch .propset, #patch .binary, #patch .copfile {border:1px solid #ccc;margin:10px 0;} #patch ins {background:#dfd;text-decoration:none;display:block;padding:0 10px;} #patch del {background:#fdd;text-decoration:none;display:block;padding:0 10px;} #patch .lines, .info {color:#888;background:#fff;} --></style> <title>[1239] sqlalchemy/trunk/doc/build: got SQL blocks to work with markdown system, other enhancements</title> </head> <body> <div id="msg"> <dl> <dt>Revision</dt> <dd>1239</dd> <dt>Author</dt> <dd>zzzeek</dd> <dt>Date</dt> <dd>2006-04-02 17:52:13 -0500 (Sun, 02 Apr 2006)</dd> </dl> <h3>Log Message</h3> <pre>got SQL blocks to work with markdown system, other enhancements sqlconstruction converted to markdown syntax</pre> <h3>Modified Paths</h3> <ul> <li><a href="#sqlalchemytrunkdocbuildcomponentsformattingmyt">sqlalchemy/trunk/doc/build/components/formatting.myt</a></li> <li><a href="#sqlalchemytrunkdocbuildtxt2mytpy">sqlalchemy/trunk/doc/build/txt2myt.py</a></li> </ul> <h3>Added Paths</h3> <ul> <li><a href="#sqlalchemytrunkdocbuildcontentsqlconstructiontxt">sqlalchemy/trunk/doc/build/content/sqlconstruction.txt</a></li> </ul> <h3>Removed Paths</h3> <ul> <li><a href="#sqlalchemytrunkdocbuildcontentsqlconstructionmyt">sqlalchemy/trunk/doc/build/content/sqlconstruction.myt</a></li> </ul> </div> <div id="patch"> <h3>Diff</h3> <a id="sqlalchemytrunkdocbuildcomponentsformattingmyt"></a> <div class="modfile"><h4>Modified: sqlalchemy/trunk/doc/build/components/formatting.myt (1238 => 1239)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/trunk/doc/build/components/formatting.myt 2006-04-02 04:13:03 UTC (rev 1238) +++ sqlalchemy/trunk/doc/build/components/formatting.myt 2006-04-02 22:52:13 UTC (rev 1239) </span><span class="lines">@@ -288,7 +288,6 @@ </span><span class="cx"> def hlight(match): </span><span class="cx"> return "<pre>" + highlight.highlight(fix_indent(match.group(1)), html_escape = html_escape, syntaxtype = syntaxtype) + "</pre>" </span><span class="cx"> content = p.sub(hlight, "<pre>" + m.content() + "</pre>") </span><del>- </del><span class="cx"> </%init> </span><span class="cx"> <div class="code"> </span><span class="cx"> % if title is not None: </span></span></pre></div> <a id="sqlalchemytrunkdocbuildcontentsqlconstructionmyt"></a> <div class="delfile"><h4>Deleted: sqlalchemy/trunk/doc/build/content/sqlconstruction.myt (1238 => 1239)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/trunk/doc/build/content/sqlconstruction.myt 2006-04-02 04:13:03 UTC (rev 1238) +++ sqlalchemy/trunk/doc/build/content/sqlconstruction.myt 2006-04-02 22:52:13 UTC (rev 1239) </span><span class="lines">@@ -1,1002 +0,0 @@ </span><del>-<%flags>inherit='document_base.myt'</%flags> -<%attr>title='Constructing SQL Queries via Python Expressions'</%attr> - -<&|doclib.myt:item, name="sql", description="Constructing SQL Queries via Python Expressions" &> - <p><b>Note:</b> This section describes how to use SQLAlchemy to construct SQL queries and receive result sets. It does <b>not</b> cover the object relational mapping capabilities of SQLAlchemy; that is covered later on in <&formatting.myt:link, path="datamapping"&>. However, both areas of functionality work similarly in how selection criterion is constructed, so if you are interested just in ORM, you should probably skim through basic <&formatting.myt:link, path="sql_select_whereclause"&> construction before moving on.</p> - <p>Once you have used the <span class="codeline">sqlalchemy.schema</span> module to construct your tables and/or reflect them from the database, performing SQL queries using those table meta data objects is done via the <span class="codeline">sqlalchemy.sql</span> package. This package defines a large set of classes, each of which represents a particular kind of lexical construct within a SQL query; all are descendants of the common base class <span class="codeline">sqlalchemy.sql.ClauseElement</span>. A full query is represented via a structure of ClauseElements. A set of reasonably intuitive creation functions is provided by the <span class="codeline">sqlalchemy.sql</span> package to create these structures; these functions are described in the rest of this section. </p> - - <p>To execute a query, you create its structure, then call the resulting structure's <span class="codeline">execute()</span> method, which returns a cursor-like object (more on that later). The same clause structure can be used repeatedly. A ClauseElement is compiled into a string representation by an underlying SQLEngine object, which is located by searching through the clause's child items for a Table object, which provides a reference to its SQLEngine. - </p> - <p>The examples below all include a dump of the generated SQL corresponding to the query object, as well as a dump of the statement's bind parameters. In all cases, bind parameters are named parameters using the colon format (i.e. ':name'). A named parameter scheme, either ':name' or '%(name)s', is used with all databases, including those that use positional schemes. For those, the named-parameter statement and its bind values are converted to the proper list-based format right before execution. Therefore a SQLAlchemy application that uses ClauseElements can standardize on named parameters for all databases.</p> - - <p>For this section, we will assume the following tables: - <&|formatting.myt:code&> - from sqlalchemy import * - db = create_engine('sqlite://filename=mydb', echo=True) - - # a table to store users - users = Table('users', db, - Column('user_id', Integer, primary_key = True), - Column('user_name', String(40)), - Column('password', String(80)) - ) - - # a table that stores mailing addresses associated with a specific user - addresses = Table('addresses', db, - Column('address_id', Integer, primary_key = True), - Column('user_id', Integer, ForeignKey("users.user_id")), - Column('street', String(100)), - Column('city', String(80)), - Column('state', String(2)), - Column('zip', String(10)) - ) - - # a table that stores keywords - keywords = Table('keywords', db, - Column('keyword_id', Integer, primary_key = True), - Column('name', VARCHAR(50)) - ) - - # a table that associates keywords with users - userkeywords = Table('userkeywords', db, - Column('user_id', INT, ForeignKey("users")), - Column('keyword_id', INT, ForeignKey("keywords")) - ) - - </&> - </p> - - <&|doclib.myt:item, name="select", description="Simple Select" &> - <p>A select is done by constructing a <span class="codeline">Select</span> object with the proper arguments, adding any extra arguments if desired, then calling its <span class="codeline">execute()</span> method. - <&|formatting.myt:code&> - from sqlalchemy import * - - # use the select() function defined in the sql package - s = select([users]) - - # or, call the select() method off of a Table object - s = users.select() - - # then, call execute on the Select object: -<&formatting.myt:poplink&>c = s.execute() -<&|formatting.myt:codepopper, link="sql" &> -SELECT users.user_id, users.user_name, users.password FROM users -{} -</&> - # the SQL text of any clause object can also be viewed via the str() call: - >>> str(s) - SELECT users.user_id, users.user_name, users.password FROM users - - </&> - <p>The object returned by the execute call is a <span class="codeline">sqlalchemy.engine.ResultProxy</span> object, which acts much like a DBAPI <span class="codeline">cursor</span> object in the context of a result set, except that the rows returned can address their columns by ordinal position, column name, or even column object:</p> - - <&|formatting.myt:code&> - # select rows, get resulting ResultProxy object -<&formatting.myt:poplink&>c = users.select().execute() -<&|formatting.myt:codepopper, link="sql" &> -SELECT users.user_id, users.user_name, users.password FROM users -{} -</&> - # get one row - row = c.fetchone() - - # get the 'user_id' column via integer index: - user_id = row[0] - - # or column name - user_name = row['user_name'] - - # or column object - password = row[users.c.password] - - # or column accessor - password = row.password - - # ResultProxy object also supports fetchall() - rows = c.fetchall() - - # or get the underlying DBAPI cursor object - cursor = c.cursor - - </&> - <&|doclib.myt:item, name="labels", description="Using Column Labels" &> - <p>A common need when writing statements that reference multiple tables is to create labels for columns, thereby separating columns from different tables with the same name. The Select construct supports automatic generation of column labels via the <span class="codeline">use_labels=True</span> parameter:</p> - <&|formatting.myt:code&> - -<&formatting.myt:poplink&>c = select([users, addresses], - users.c.user_id==addresses.c.address_id, - use_labels=True).execute() -<&|formatting.myt:codepopper, link="sql" &> -SELECT users.user_id AS users_user_id, users.user_name AS users_user_name, -users.password AS users_password, addresses.address_id AS addresses_address_id, -addresses.user_id AS addresses_user_id, addresses.street AS addresses_street, -addresses.city AS addresses_city, addresses.state AS addresses_state, -addresses.zip AS addresses_zip -FROM users, addresses -WHERE users.user_id = addresses.address_id -{} -</&> -</&> - <p>The table name part of the label is affected if you use a construct such as a table alias:</p> - <&|formatting.myt:code&> - - person = users.alias('person') - <&formatting.myt:poplink&>c = select([person, addresses], - person.c.user_id==addresses.c.address_id, - use_labels=True).execute() - -<&|formatting.myt:codepopper, link="sql" &> -SELECT person.user_id AS person_user_id, person.user_name AS person_user_name, -person.password AS person_password, addresses.address_id AS addresses_address_id, - addresses.user_id AS addresses_user_id, addresses.street AS addresses_street, - addresses.city AS addresses_city, addresses.state AS addresses_state, - addresses.zip AS addresses_zip FROM users AS person, addresses -WHERE person.user_id = addresses.address_id -</&> - </&> - <p>You can also specify custom labels on a per-column basis using the <span class="codeline">label()</span> function: - <&|formatting.myt:code&> - <&formatting.myt:poplink&>c = select([users.c.user_id.label('id'), users.c.user_name.label('name')]).execute() -<&|formatting.myt:codepopper, link="sql" &> -SELECT users.user_id AS id, users.user_name AS name -FROM users -{} -</&> - - </&> - </&> - - <&|doclib.myt:item, name="columns", description="Table/Column Specification" &> - <P>Calling <span class="codeline">select</span> off a table automatically generates a column clause which includes all the table's columns, in the order they are specified in the source Table object.</p> - <p>But in addition to selecting all the columns off a single table, any set of columns can be specified, as well as full tables, and any combination of the two:</p> - <&|formatting.myt:code&> - # individual columns -<&formatting.myt:poplink&>c = select([users.c.user_id, users.c.user_name]).execute() -<&|formatting.myt:codepopper, link="sql" &> -SELECT users.user_id, users.user_name FROM users -{} -</&> - # full tables -<&formatting.myt:poplink&>c = select([users, addresses]).execute() -<&|formatting.myt:codepopper, link="sql" &> -SELECT users.user_id, users.user_name, users.password, -addresses.address_id, addresses.user_id, -addresses.street, addresses.city, addresses.state, addresses.zip -FROM users, addresses -{} -</&> - # combinations -<&formatting.myt:poplink&>c = select([users, addresses.c.zip]).execute() -<&|formatting.myt:codepopper, link="sql" &> -SELECT users.user_id, users.user_name, users.password, -addresses.zip FROM users, addresses -{} -</&> - </&> - </&> - - <&|doclib.myt:item, name="whereclause", description="WHERE Clause" &> - <P>The WHERE condition is the named keyword argument <span class="codeline">whereclause</span>, or the second positional argument to the <span class="codeline">select()</span> constructor and the first positional argument to the <span class="codeline">select()</span> method of <span class="codeline">Table</span>.</p> - - <p>WHERE conditions are constructed using column objects, literal values, and functions defined in the <span class="codeline">sqlalchemy.sql</span> module. Column objects override the standard Python operators to provide clause compositional objects, which compile down to SQL operations: - - <&|formatting.myt:code&> -<&formatting.myt:poplink&>c = users.select(users.c.user_id == 7).execute() -<&|formatting.myt:codepopper, link="sql" &> -SELECT users.user_id, users.user_name, users.password, -FROM users WHERE users.user_id = :users_user_id -{'users_user_id': 7} -</&> - </&> - <p>Notice that the literal value "7" was broken out of the query and placed into a bind parameter. Databases such as Oracle must parse incoming SQL and create a "plan" when new queries are received, which is an expensive process. By using bind parameters, the same query with various literal values can have its plan compiled only once, and used repeatedly with less overhead. - </p> - <P>More where clauses:</p> - <&|formatting.myt:code&> - # another comparison operator -<&formatting.myt:poplink&>c = select([users], users.c.user_id>7).execute() -<&|formatting.myt:codepopper, link="sql" &> -SELECT users.user_id, users.user_name, users.password, -FROM users WHERE users.user_id > :users_user_id -{'users_user_id': 7} -</&> - - # OR keyword -<&formatting.myt:poplink&>c = users.select(or_(users.c.user_name=='jack', users.c.user_name=='ed')).execute() -<&|formatting.myt:codepopper, link="sql" &> -SELECT users.user_id, users.user_name, users.password -FROM users WHERE users.user_name = :users_user_name -OR users.user_name = :users_user_name_1 -{'users_user_name_1': 'ed', 'users_user_name': 'jack'} - -</&> - - # AND keyword -<&formatting.myt:poplink&>c = users.select(and_(users.c.user_name=='jack', users.c.password=='dog')).execute() -<&|formatting.myt:codepopper, link="sql" &> -SELECT users.user_id, users.user_name, users.password -FROM users WHERE users.user_name = :users_user_name -AND users.password = :users_password -{'users_user_name': 'jack', 'users_password': 'dog'} -</&> - - # NOT keyword - <&formatting.myt:poplink&>c = users.select(not_( - or_(users.c.user_name=='jack', users.c.password=='dog') - )).execute() -<&|formatting.myt:codepopper, link="sql" &> -SELECT users.user_id, users.user_name, users.password -FROM users -WHERE NOT (users.user_name = :users_user_name - OR users.password = :users_password) -{'users_user_name': 'jack', 'users_password': 'dog'} -</&> - - # IN clause -<&formatting.myt:poplink&>c = users.select(users.c.user_name.in_('jack', 'ed', 'fred')).execute() -<&|formatting.myt:codepopper, link="sql" &> -SELECT users.user_id, users.user_name, users.password -FROM users WHERE users.user_name -IN (:users_user_name, :users_user_name_1, :users_user_name_2) -{'users_user_name': 'jack', 'users_user_name_1': 'ed', - 'users_user_name_2': 'fred'} -</&> - - # join users and addresses together -<&formatting.myt:poplink&>c = select([users, addresses], users.c.user_id==addresses.c.address_id).execute() -<&|formatting.myt:codepopper, link="sql" &> -SELECT users.user_id, users.user_name, users.password, -addresses.address_id, addresses.user_id, addresses.street, addresses.city, -addresses.state, addresses.zip -FROM users, addresses -WHERE users.user_id = addresses.address_id -{} -</&> - - # join users and addresses together, but dont specify "addresses" in the - # selection criterion. The WHERE criterion adds it to the FROM list - # automatically. -<&formatting.myt:poplink&>c = select([users], and_( - users.c.user_id==addresses.c.user_id, - users.c.user_name=='fred' - )).execute() -<&|formatting.myt:codepopper, link="sql" &> -SELECT users.user_id, users.user_name, users.password -FROM users, addresses WHERE users.user_id = addresses.user_id -AND users.user_name = :users_user_name -{'users_user_name': 'fred'} -</&> -</&> - - <P>Select statements can also generate a WHERE clause based on the parameters you give it. If a given parameter, which matches the name of a column or its "label" (the combined tablename + "_" + column name), and does not already correspond to a bind parameter in the select object, it will be added as a comparison against that column. This is a shortcut to creating a full WHERE clause:</p> - <&|formatting.myt:code&> - # specify a match for the "user_name" column - <&formatting.myt:poplink&>c = users.select().execute(user_name='ed') -<&|formatting.myt:codepopper, link="sql" &> -SELECT users.user_id, users.user_name, users.password -FROM users WHERE users.user_name = :users_user_name -{'users_user_name': 'ed'} -</&> - # specify a full where clause for the "user_name" column, as well as a - # comparison for the "user_id" column - <&formatting.myt:poplink&>c = users.select(users.c.user_name=='ed').execute(user_id=10) -<&|formatting.myt:codepopper, link="sql" &> -SELECT users.user_id, users.user_name, users.password -FROM users WHERE users.user_name = :users_user_name AND users.user_id = :users_user_id -{'users_user_name': 'ed', 'users_user_id': 10} -</&> - </&> - <&|doclib.myt:item, name="operators", description="Operators" &> - <p>Supported column operators so far are all the numerical comparison operators, i.e. '==', '>', '>=', etc., as well as like(), startswith(), endswith(), between(), and in(). Boolean operators include not_(), and_() and or_(), which also can be used inline via '~', '&', and '|'. Math operators are '+', '-', '*', '/'. Any custom operator can be specified via the op() function shown below.</p> - <&|formatting.myt:code &> - # "like" operator - users.select(users.c.user_name.like('%ter')) - - # equality operator - users.select(users.c.user_name == 'jane') - - # in opertator - users.select(users.c.user_id.in_(1,2,3)) - - # and_, endswith, equality operators - users.select(and_(addresses.c.street.endswith('green street'), addresses.c.zip=='11234')) - - # & operator subsituting for 'and_' - users.select(addresses.c.street.endswith('green street') & (addresses.c.zip=='11234')) - - # + concatenation operator - select([users.c.user_name + '_name']) - - # NOT operator - users.select(~(addresses.c.street == 'Green Street')) - - # any custom operator - select([users.c.user_name.op('||')('_category')]) - </&> - </&> - - </&> - <&|doclib.myt:item, name="engine", description="Specifying the Engine" &> - <p>For queries that don't contain any tables, the SQLEngine can be specified to any constructed statement via the <span class="codeline">engine</span> keyword parameter:</p> - <&|formatting.myt:code &> - # select a literal - select(["hi"], engine=myengine) - - # select a function - select([func.now()], engine=db) - </&> - </&> - <&|doclib.myt:item, name="functions", description="Functions" &> - <p>Functions can be specified using the <span class="codeline">func</span> keyword:</p> - <&|formatting.myt:code &> - <&formatting.myt:poplink&>select([func.count(users.c.user_id)]).execute() - <&|formatting.myt:codepopper, link="sql" &> -SELECT count(users.user_id) FROM users - </&> - - <&formatting.myt:poplink&>users.select(func.substr(users.c.user_name, 1) == 'J').execute() - <&|formatting.myt:codepopper, link="sql" &> -SELECT users.user_id, users.user_name, users.password FROM users -WHERE substr(users.user_name, :substr) = :substr_1 -{'substr_1': 'J', 'substr': 1} - </&> - - </&> - <p>Functions also are callable as standalone values:</p> - <&|formatting.myt:code &> - # call the "now()" function - time = func.now(engine=myengine).scalar() - - # call myfunc(1,2,3) - myvalue = func.myfunc(1, 2, 3, engine=db).execute() - - # or call them off the engine - db.func.now().scalar() - </&> - </&> - <&|doclib.myt:item, name="literals", description="Literals" &> - <p>You can drop in a literal value anywhere there isnt a column to attach to via the <span class="codeline">literal</span> keyword:</p> - <&|formatting.myt:code &> - <&formatting.myt:poplink&>select([literal('foo') + literal('bar'), users.c.user_name]).execute() - <&|formatting.myt:codepopper, link="sql" &> - SELECT :literal + :literal_1, users.user_name - FROM users - {'literal_1': 'bar', 'literal': 'foo'} - </&> - # literals have all the same comparison functions as columns - <&formatting.myt:poplink&>select([literal('foo') == literal('bar')], engine=myengine).scalar() - <&|formatting.myt:codepopper, link="sql" &> - SELECT :literal = :literal_1 - {'literal_1': 'bar', 'literal': 'foo'} - </&> - </&> - <p>Literals also take an optional <span class="codeline">type</span> parameter to give literals a type. This can sometimes be significant, for example when using the "+" operator with SQLite, the String type is detected and the operator is converted to "||":</p> - <&|formatting.myt:code &> - <&formatting.myt:poplink&>select([literal('foo', type=String) + 'bar'], engine=e).execute() - <&|formatting.myt:codepopper, link="sql" &> - SELECT ? || ? - ['foo', 'bar'] - </&> - </&> - - </&> - <&|doclib.myt:item, name="orderby", description="Order By" &> - <P>The ORDER BY clause of a select statement can be specified as individual columns to order by within an array specified via the <span class="codeline">order_by</span> parameter, and optional usage of the asc() and desc() functions: - <&|formatting.myt:code &> - # straight order by -<&formatting.myt:poplink&>c = users.select(order_by=[users.c.user_name]).execute() -<&|formatting.myt:codepopper, link="sql" &> -SELECT users.user_id, users.user_name, users.password -FROM users ORDER BY users.user_name -</&> - # descending/ascending order by on multiple columns -<&formatting.myt:poplink&>c = users.select( - users.c.user_name>'J', - order_by=[desc(users.c.user_id), asc(users.c.user_name)]).execute() -<&|formatting.myt:codepopper, link="sql" &> -SELECT users.user_id, users.user_name, users.password -FROM users WHERE users.user_name > :users_user_name -ORDER BY users.user_id DESC, users.user_name ASC -{'users_user_name':'J'} -</&> - </&> - </&> - <&|doclib.myt:item, name="options", description="DISTINCT, LIMIT and OFFSET" &> - These are specified as keyword arguments: - <&|formatting.myt:code &> - <&formatting.myt:poplink&>c = select([users.c.user_name], distinct=True).execute() -<&|formatting.myt:codepopper, link="sql" &> -SELECT DISTINCT users.user_name FROM users -</&> - <&formatting.myt:poplink&>c = users.select(limit=10, offset=20).execute() -<&|formatting.myt:codepopper, link="sql" &> -SELECT users.user_id, users.user_name, users.password FROM users LIMIT 10 OFFSET 20 -</&> - </&> - The Oracle driver does not support LIMIT and OFFSET directly, but instead wraps the generated query into a subquery and uses the "rownum" variable to control the rows selected (this is somewhat experimental). - </&> - </&> - - <&|doclib.myt:item, name="join", description="Inner and Outer Joins" &> - <p>As some of the examples indicated above, a regular inner join can be implicitly stated, just like in a SQL expression, by just specifying the tables to be joined as well as their join conditions:</p> - <&|formatting.myt:code &> -<&formatting.myt:poplink&>addresses.select(addresses.c.user_id==users.c.user_id).execute() -<&|formatting.myt:codepopper, link="sql" &> -SELECT addresses.address_id, addresses.user_id, addresses.street, -addresses.city, addresses.state, addresses.zip FROM addresses, users -WHERE addresses.user_id = users.user_id -{} -</&> - </&> - <p>There is also an explicit join constructor, which can be embedded into a select query via the <span class="codeline">from_obj</span> parameter of the select statement:</p> - - <&|formatting.myt:code &> - <&formatting.myt:poplink&>addresses.select(from_obj=[ - addresses.join(users, addresses.c.user_id==users.c.user_id) - ]).execute() - <&|formatting.myt:codepopper, link="sql" &> - SELECT addresses.address_id, addresses.user_id, addresses.street, addresses.city, - addresses.state, addresses.zip - FROM addresses JOIN users ON addresses.user_id = users.user_id - {} - </&> - - </&> - - <p>The join constructor can also be used by itself:</p> - <&|formatting.myt:code &> - <&formatting.myt:poplink&>join(users, addresses, users.c.user_id==addresses.c.user_id).select().execute() - <&|formatting.myt:codepopper, link="sql" &> - SELECT users.user_id, users.user_name, users.password, - addresses.address_id, addresses.user_id, addresses.street, addresses.city, - addresses.state, addresses.zip - FROM addresses JOIN users ON addresses.user_id = users.user_id - {} - </&> - </&> - <p>The join criterion in a join() call is optional. If not specified, the condition will be derived from the foreign key relationships of the two tables. If no criterion can be constructed, an exception will be raised.</p> - - <&|formatting.myt:code &> - <&formatting.myt:poplink&>join(users, addresses).select().execute() - <&|formatting.myt:codepopper, link="sql" &> - SELECT users.user_id, users.user_name, users.password, - addresses.address_id, addresses.user_id, addresses.street, addresses.city, - addresses.state, addresses.zip - FROM addresses JOIN users ON addresses.user_id = users.user_id - {} - </&> - </&> - - <p>Notice that this is the first example where the FROM criterion of the select statement is explicitly specified. In most cases, the FROM criterion is automatically determined from the columns requested as well as the WHERE clause. The <span class="codeline">from_obj</span> keyword argument indicates a list of explicit FROM clauses to be used in the statement.</p> - - <p>A join can be created on its own using the <span class="codeline">join</span> or <span class="codeline">outerjoin</span> functions, or can be created off of an existing Table or other selectable unit via the <span class="codeline">join</span> or <span class="codeline">outerjoin</span> methods:</p> - - <&|formatting.myt:code &> - <&formatting.myt:poplink&>outerjoin(users, addresses, users.c.user_id==addresses.c.address_id).select().execute() -<&|formatting.myt:codepopper, link="sql" &> -SELECT users.user_id, users.user_name, users.password, addresses.address_id, -addresses.user_id, addresses.street, addresses.city, addresses.state, addresses.zip -FROM users LEFT OUTER JOIN addresses ON users.user_id = addresses.address_id -{} -</&> - - <&formatting.myt:poplink&>users.select(keywords.c.name=='running', from_obj=[ - users.join( - userkeywords, userkeywords.c.user_id==users.c.user_id).join( - keywords, keywords.c.keyword_id==userkeywords.c.keyword_id) - ]).execute() -<&|formatting.myt:codepopper, link="sql" &> -SELECT users.user_id, users.user_name, users.password FROM users -JOIN userkeywords ON userkeywords.user_id = users.user_id -JOIN keywords ON keywords.keyword_id = userkeywords.keyword_id -WHERE keywords.name = :keywords_name -{'keywords_name': 'running'} -</&> - </&> - - </&> - <&|doclib.myt:item, name="alias", description="Table Aliases" &> - <p>Aliases are used primarily when you want to use the same table more than once as a FROM expression in a statement:</p> - - <&|formatting.myt:code &> - address_b = addresses.alias('addressb') - - <&formatting.myt:poplink&># select users who have an address on Green street as well as Orange street - users.select(and_( - users.c.user_id==addresses.c.user_id, - addresses.c.street.like('%Green%'), - users.c.user_id==address_b.c.user_id, - address_b.c.street.like('%Orange%') - )).execute() -<&|formatting.myt:codepopper, link="sql" &> -SELECT users.user_id, users.user_name, users.password -FROM users, addresses, addresses AS addressb -WHERE users.user_id = addresses.user_id -AND addresses.street LIKE :addresses_street -AND users.user_id = addressb.user_id -AND addressb.street LIKE :addressb_street -{'addressb_street': '%Orange%', 'addresses_street': '%Green%'} -</&> - </&> - </&> - <&|doclib.myt:item, name="subqueries", description="Subqueries" &> - <p>SQLAlchemy allows the creation of select statements from not just Table objects, but from a whole class of objects that implement the <span class="codeline">Selectable</span> interface. This includes Tables, Aliases, Joins and Selects. Therefore, if you have a Select, you can select from the Select:</p> - - <&|formatting.myt:code &> - >>> s = users.select() - >>> str(s) - SELECT users.user_id, users.user_name, users.password FROM users - - >>> s = s.select() - >>> str(s) - SELECT user_id, user_name, password - FROM (SELECT users.user_id, users.user_name, users.password FROM users) - - </&> - <p>Any Select, Join, or Alias object supports the same column accessors as a Table:</p> - <&|formatting.myt:code &> - >>> s = users.select() - >>> [c.key for c in s.columns] - ['user_id', 'user_name', 'password'] - </&> - - - <p> - When you use <span class="codeline">use_labels=True</span> in a Select object, the label version of the column names become the keys of the accessible columns. In effect you can create your own "view objects": - </p> - <&|formatting.myt:code &> - s = select([users, addresses], users.c.user_id==addresses.c.user_id, use_labels=True) - - <&formatting.myt:poplink&>select([ - s.c.users_user_name, s.c.addresses_street, s.c.addresses_zip - ], s.c.addresses_city=='San Francisco').execute() -<&|formatting.myt:codepopper, link="sql" &> -SELECT users_user_name, addresses_street, addresses_zip -FROM (SELECT users.user_id AS users_user_id, users.user_name AS users_user_name, - users.password AS users_password, addresses.address_id AS addresses_address_id, - addresses.user_id AS addresses_user_id, addresses.street AS addresses_street, - addresses.city AS addresses_city, addresses.state AS addresses_state, - addresses.zip AS addresses_zip -FROM users, addresses -WHERE users.user_id = addresses.user_id) -WHERE addresses_city = :addresses_city -{'addresses_city': 'San Francisco'} -</&> - </&> - <p>To specify a SELECT statement as one of the selectable units in a FROM clause, it usually should be given an alias.</p> - <&|formatting.myt:code &> - <&formatting.myt:poplink&>s = users.select().alias('u') - select([addresses, s]).execute() - -<&|formatting.myt:codepopper, link="sql" &> -SELECT addresses.address_id, addresses.user_id, addresses.street, addresses.city, -addresses.state, addresses.zip, u.user_id, u.user_name, u.password -FROM addresses, -(SELECT users.user_id, users.user_name, users.password FROM users) AS u -{} -</&> - </&> - - <p>Select objects can be used in a WHERE condition, in operators such as IN:</p> - <&|formatting.myt:code &> - # select user ids for all users whos name starts with a "p" - s = select([users.c.user_id], users.c.user_name.like('p%')) - - # now select all addresses for those users - <&formatting.myt:poplink&>addresses.select(addresses.c.user_id.in_(s)).execute() -<&|formatting.myt:codepopper, link="sql" &> -SELECT addresses.address_id, addresses.user_id, addresses.street, -addresses.city, addresses.state, addresses.zip -FROM addresses WHERE addresses.address_id IN -(SELECT users.user_id FROM users WHERE users.user_name LIKE :users_user_name) -{'users_user_name': 'p%'}</&> - </&> - - <P>The sql package supports embedding select statements into other select statements as the criterion in a WHERE condition, or as one of the "selectable" objects in the FROM list of the query. It does not at the moment directly support embedding a SELECT statement as one of the column criterion for a statement, although this can be achieved via direct text insertion, described later.</p> - - <&|doclib.myt:item, name="scalar", description="Scalar Column Subqueries"&> - <p>Subqueries can be used in the column clause of a select statement by specifying the <span class="codeline">scalar=True</span> flag:</p> - <&|formatting.myt:code &> -<&formatting.myt:poplink&>select([table2.c.col1, table2.c.col2, select([table1.c.col1], table1.c.col2==7, scalar=True)]) -<&|formatting.myt:codepopper, link="sql" &> -SELECT table2.col1, table2.col2, -(SELECT table1.col1 AS col1 FROM table1 WHERE col2=:table1_col2) -FROM table2 -{'table1_col2': 7} -</&> - </&> - </&> - - <&|doclib.myt:item, name="correlated", description="Correlated Subqueries" &> - <P>When a select object is embedded inside of another select object, and both objects reference the same table, SQLAlchemy makes the assumption that the table should be correlated from the child query to the parent query. To disable this behavior, specify the flag <span class="codeline">correlate=False</span> to the Select statement.</p> - <&|formatting.myt:code &> - # make an alias of a regular select. - s = select([addresses.c.street], addresses.c.user_id==users.c.user_id).alias('s') - >>> str(s) - SELECT addresses.street FROM addresses, users - WHERE addresses.user_id = users.user_id - - # now embed that select into another one. the "users" table is removed from - # the embedded query's FROM list and is instead correlated to the parent query - s2 = select([users, s.c.street]) - >>> str(s2) - SELECT users.user_id, users.user_name, users.password, s.street - FROM users, (SELECT addresses.street FROM addresses - WHERE addresses.user_id = users.user_id) s -</&> - </&> - <&|doclib.myt:item, name="exists", description="EXISTS Clauses" &> - <p>An EXISTS clause can function as a higher-scaling version of an IN clause, and is usually used in a correlated fashion:</p> - <&|formatting.myt:code &> - # find all users who have an address on Green street: - <&formatting.myt:poplink&>users.select( - exists( - [addresses.c.address_id], - and_( - addresses.c.user_id==users.c.user_id, - addresses.c.street.like('%Green%') - ) - )) -<&|formatting.myt:codepopper, link="sql" &> -SELECT users.user_id, users.user_name, users.password -FROM users WHERE EXISTS (SELECT addresses.address_id -FROM addresses WHERE addresses.user_id = users.user_id -AND addresses.street LIKE :addresses_street) -{'addresses_street': '%Green%'} -</&> - </&> - </&> - </&> - <&|doclib.myt:item, name="unions", description="Unions" &> - <p>Unions come in two flavors, UNION and UNION ALL, which are available via module level functions or methods off a Selectable:</p> - <&|formatting.myt:code &> - <&formatting.myt:poplink&>union( - addresses.select(addresses.c.street=='123 Green Street'), - addresses.select(addresses.c.street=='44 Park Ave.'), - addresses.select(addresses.c.street=='3 Mill Road'), - order_by=[addresses.c.street] - ).execute()\ -<&|formatting.myt:codepopper, link="sql" &> -SELECT addresses.address_id, addresses.user_id, addresses.street, -addresses.city, addresses.state, addresses.zip -FROM addresses WHERE addresses.street = :addresses_street -UNION -SELECT addresses.address_id, addresses.user_id, addresses.street, -addresses.city, addresses.state, addresses.zip -FROM addresses WHERE addresses.street = :addresses_street_1 -UNION -SELECT addresses.address_id, addresses.user_id, addresses.street, -addresses.city, addresses.state, addresses.zip -FROM addresses WHERE addresses.street = :addresses_street_2 -ORDER BY addresses.street -{'addresses_street_1': '44 Park Ave.', -'addresses_street': '123 Green Street', -'addresses_street_2': '3 Mill Road'} -</&> - <&formatting.myt:poplink&>users.select( - users.c.user_id==7 - ).union_all( - users.select( - users.c.user_id==9 - ), - order_by=[users.c.user_id] # order_by is an argument to union_all() - ).execute() -<&|formatting.myt:codepopper, link="sql" &> -SELECT users.user_id, users.user_name, users.password -FROM users WHERE users.user_id = :users_user_id -UNION ALL -SELECT users.user_id, users.user_name, users.password -FROM users WHERE users.user_id = :users_user_id_1 -ORDER BY users.user_id -{'users_user_id_1': 9, 'users_user_id': 7} -</&> - </&> - </&> - <&|doclib.myt:item, name="bindparams", description="Custom Bind Parameters" &> - <p>Throughout all these examples, SQLAlchemy is busy creating bind parameters wherever literal expressions occur. You can also specify your own bind parameters with your own names, and use the same statement repeatedly. As mentioned at the top of this section, named bind parameters are always used regardless of the type of DBAPI being used; for DBAPI's that expect positional arguments, bind parameters are converted to lists right before execution, and Pyformat strings in statements, i.e. '%(name)s', are converted to the appropriate positional style.</p> - <&|formatting.myt:code &> - s = users.select(users.c.user_name==bindparam('username')) - <&formatting.myt:poplink&>s.execute(username='fred')\ -<&|formatting.myt:codepopper, link="sql" &> -SELECT users.user_id, users.user_name, users.password -FROM users WHERE users.user_name = :username -{'username': 'fred'} -</&> - <&formatting.myt:poplink&>s.execute(username='jane')\ -<&|formatting.myt:codepopper, link="sql" &> -SELECT users.user_id, users.user_name, users.password -FROM users WHERE users.user_name = :username -{'username': 'jane'} -</&> - <&formatting.myt:poplink&>s.execute(username='mary')\ -<&|formatting.myt:codepopper, link="sql" &> -SELECT users.user_id, users.user_name, users.password -FROM users WHERE users.user_name = :username -{'username': 'mary'} -</&> - </&> - <p><span class="codeline">executemany()</span> is also available, but that applies more to INSERT/UPDATE/DELETE, described later.</p> - <P>The generation of bind parameters is performed specific to the engine being used. The examples in this document all show "named" parameters like those used in sqlite and oracle. Depending on the parameter type specified by the DBAPI module, the correct bind parameter scheme will be used.</p> - <&|doclib.myt:item, name="precompiling", description="Precompiling a Query" &> - <p>By throwing the <span class="codeline">compile()</span> method onto the end of any query object, the query can be "compiled" by the SQLEngine into a <span class="codeline">sqlalchemy.sql.Compiled</span> object just once, and the resulting compiled object reused, which eliminates repeated internal compilation of the SQL string:</p> - <&|formatting.myt:code &> - s = users.select(users.c.user_name==bindparam('username')).compile() - s.execute(username='fred') - s.execute(username='jane') - s.execute(username='mary') - </&> - </&> - </&> - <&|doclib.myt:item, name="textual", description="Literal Text Blocks" &> - <p>The sql package tries to allow free textual placement in as many ways as possible. In the examples below, note that the from_obj parameter is used only when no other information exists within the select object with which to determine table metadata. Also note that in a query where there isnt even table metadata used, the SQLEngine to be used for the query has to be explicitly specified: - <&|formatting.myt:code &> - # strings as column clauses - <&formatting.myt:poplink&>select(["user_id", "user_name"], from_obj=[users]).execute() -<&|formatting.myt:codepopper, link="sql" &> -SELECT user_id, user_name FROM users -{} -</&> - # strings for full column lists - <&formatting.myt:poplink&>select( - ["user_id, user_name, password, addresses.*"], - from_obj=[users.alias('u'), addresses]).execute() -<&|formatting.myt:codepopper, link="sql" &> -SELECT u.user_id, u.user_name, u.password, addresses.* -FROM users AS u, addresses -{} -</&> - # functions, etc. - <&formatting.myt:poplink&>select([users.c.user_id, "process_string(user_name)"]).execute() -<&|formatting.myt:codepopper, link="sql" &> -SELECT users.user_id, process_string(user_name) FROM users -{} -</&> - # where clauses - <&formatting.myt:poplink&>users.select(and_(users.c.user_id==7, "process_string(user_name)=27")).execute() -<&|formatting.myt:codepopper, link="sql" &> -SELECT users.user_id, users.user_name, users.password FROM users -WHERE users.user_id = :users_user_id AND process_string(user_name)=27 -{'users_user_id': 7} -</&> - # subqueries - <&formatting.myt:poplink&>users.select( - "exists (select 1 from addresses where addresses.user_id=users.user_id)").execute() -<&|formatting.myt:codepopper, link="sql" &> -SELECT users.user_id, users.user_name, users.password FROM users -WHERE exists (select 1 from addresses where addresses.user_id=users.user_id) -{} -</&> - # custom FROM objects - <&formatting.myt:poplink&>select( - ["*"], - from_obj=["(select user_id, user_name from users)"], - engine=db).execute() -<&|formatting.myt:codepopper, link="sql" &> -SELECT * FROM (select user_id, user_name from users) -{} -</&> - # a full query - <&formatting.myt:poplink&>text("select user_name from users", engine=db).execute() -<&|formatting.myt:codepopper, link="sql" &> -select user_name from users -{} -</&> - # or call text() off of the engine - engine.text("select user_name from users").execute() - - # execute off the engine directly - you must use the engine's native bind parameter - # style (i.e. named, pyformat, positional, etc.) - <&formatting.myt:poplink&>db.execute( - "select user_name from users where user_id=:user_id", - {'user_id':7}).execute() -<&|formatting.myt:codepopper, link="sql" &> -select user_name from users where user_id=:user_id -{'user_id':7} -</&> - - - </&> - - <&|doclib.myt:item, name="textual_binds", description="Using Bind Parameters in Text Blocks" &> - <p>Use the format <span class="codeline"><% ':<paramname>' |h %></span> to define bind parameters inside of a text block. They will be converted to the appropriate format upon compilation:</p> - <&|formatting.myt:code &> - t = engine.text("select foo from mytable where lala=:hoho") - r = t.execute(hoho=7) - </&> - <p>Bind parameters can also be explicit, which allows typing information to be added. Just specify them as a list with - keys that match those inside the textual statement:</p> - <&|formatting.myt:code &> - t = engine.text("select foo from mytable where lala=:hoho", - bindparams=[bindparam('hoho', type=types.String)]) - r = t.execute(hoho="im hoho") - </&> - <p>Result-row type processing can be added via the <span class="codeline">typemap</span> argument, which - is a dictionary of return columns mapped to types:</p> - <&|formatting.myt:code &> - # specify DateTime type for the 'foo' column in the result set - # sqlite, for example, uses result-row post-processing to construct dates - t = engine.text("select foo from mytable where lala=:hoho", - bindparams=[bindparam('hoho', type=types.String)], - typemap={'foo':types.DateTime} - ) - r = t.execute(hoho="im hoho") - - # 'foo' is a datetime - year = r.fetchone()['foo'].year - </&> - - </&> - </&> - <&|doclib.myt:item, name="building", description="Building Select Objects" &> - <p>One of the primary motivations for a programmatic SQL library is to allow the piecemeal construction of a SQL statement based on program variables. All the above examples typically show Select objects being created all at once. The Select object also includes "builder" methods to allow building up an object. The below example is a "user search" function, where users can be selected based on primary key, user name, street address, keywords, or any combination: - <&|formatting.myt:code &> - def find_users(id=None, name=None, street=None, keywords=None): - statement = users.select() - if id is not None: - statement.append_whereclause(users.c.user_id==id) - if name is not None: - statement.append_whereclause(users.c.user_name==name) - if street is not None: - # append_whereclause joins "WHERE" conditions together with AND - statement.append_whereclause(users.c.user_id==addresses.c.user_id) - statement.append_whereclause(addresses.c.street==street) - if keywords is not None: - statement.append_from( - users.join(userkeywords, users.c.user_id==userkeywords.c.user_id).join( - keywords, userkeywords.c.keyword_id==keywords.c.keyword_id)) - statement.append_whereclause(keywords.c.name.in_(keywords)) - # to avoid multiple repeats, set query to be DISTINCT: - statement.distinct=True - return statement.execute() - - <&formatting.myt:poplink&>find_users(id=7) -<&|formatting.myt:codepopper, link="sql" &> -SELECT users.user_id, users.user_name, users.password -FROM users -WHERE users.user_id = :users_user_id -{'users_user_id': 7} -</&> - <&formatting.myt:poplink&>find_users(street='123 Green Street') -<&|formatting.myt:codepopper, link="sql" &> -SELECT users.user_id, users.user_name, users.password -FROM users, addresses -WHERE users.user_id = addresses.user_id AND addresses.street = :addresses_street -{'addresses_street': '123 Green Street'} -</&> - <&formatting.myt:poplink&>find_users(name='Jack', keywords=['jack','foo']) -<&|formatting.myt:codepopper, link="sql" &> -SELECT DISTINCT users.user_id, users.user_name, users.password -FROM users JOIN userkeywords ON users.user_id = userkeywords.user_id -JOIN keywords ON userkeywords.keyword_id = keywords.keyword_id -WHERE users.user_name = :users_user_name AND keywords.name IN ('jack', 'foo') -{'users_user_name': 'Jack'} -</&> - - </&> - </&> - <&|doclib.myt:item, name="insert", description="Inserts" &> - <p>An INSERT involves just one table. The Insert object is used via the insert() function, and the specified columns determine what columns show up in the generated SQL. If primary key columns are left out of the criterion, the SQL generator will try to populate them as specified by the particular database engine and sequences, i.e. relying upon an auto-incremented column or explicitly calling a sequence beforehand. Insert statements, as well as updates and deletes, can also execute multiple parameters in one pass via specifying an array of dictionaries as parameters.</p> - <p>The values to be populated for an INSERT or an UPDATE can be specified to the insert()/update() functions as the <span class="codeline">values</span> named argument, or the query will be compiled based on the values of the parameters sent to the execute() method.</p> -<&|formatting.myt:code &> - # basic insert - <&formatting.myt:poplink&>users.insert().execute(user_id=1, user_name='jack', password='asdfdaf') -<&|formatting.myt:codepopper, link="sql" &> -INSERT INTO users (user_id, user_name, password) -VALUES (:user_id, :user_name, :password) -{'user_name': 'jack', 'password': 'asdfdaf', 'user_id': 1} -</&> - # insert just user_name, NULL for others - # will auto-populate primary key columns if they are configured - # to do so - <&formatting.myt:poplink&>users.insert().execute(user_name='ed') -<&|formatting.myt:co... [truncated message content] |
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head><style type="text/css"><!-- #msg dl { border: 1px #006 solid; background: #369; padding: 6px; color: #fff; } #msg dt { float: left; width: 6em; font-weight: bold; } #msg dt:after { content:':';} #msg dl, #msg dt, #msg ul, #msg li { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; } #msg dl a { font-weight: bold} #msg dl a:link { color:#fc3; } #msg dl a:active { color:#ff0; } #msg dl a:visited { color:#cc6; } h3 { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; font-weight: bold; } #msg pre { overflow: auto; background: #ffc; border: 1px #fc0 solid; padding: 6px; } #msg ul, pre { overflow: auto; } #patch { width: 100%; } #patch h4 {font-family: verdana,arial,helvetica,sans-serif;font-size:10pt;padding:8px;background:#369;color:#fff;margin:0;} #patch .propset h4, #patch .binary h4 {margin:0;} #patch pre {padding:0;line-height:1.2em;margin:0;} #patch .diff {width:100%;background:#eee;padding: 0 0 10px 0;overflow:auto;} #patch .propset .diff, #patch .binary .diff {padding:10px 0;} #patch span {display:block;padding:0 10px;} #patch .modfile, #patch .addfile, #patch .delfile, #patch .propset, #patch .binary, #patch .copfile {border:1px solid #ccc;margin:10px 0;} #patch ins {background:#dfd;text-decoration:none;display:block;padding:0 10px;} #patch del {background:#fdd;text-decoration:none;display:block;padding:0 10px;} #patch .lines, .info {color:#888;background:#fff;} --></style> <title>[1238] sqlalchemy/trunk/test: fixed reset_history method when applied to an attribute that had an attribute-level TriggeredAttribute set on it, added unit test.</title> </head> <body> <div id="msg"> <dl> <dt>Revision</dt> <dd>1238</dd> <dt>Author</dt> <dd>zzzeek</dd> <dt>Date</dt> <dd>2006-04-01 22:13:03 -0600 (Sat, 01 Apr 2006)</dd> </dl> <h3>Log Message</h3> <pre>fixed reset_history method when applied to an attribute that had an attribute-level TriggeredAttribute set on it, added unit test.</pre> <h3>Modified Paths</h3> <ul> <li><a href="#sqlalchemytrunklibsqlalchemyattributespy">sqlalchemy/trunk/lib/sqlalchemy/attributes.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 (1237 => 1238)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/trunk/lib/sqlalchemy/attributes.py 2006-04-02 04:09:05 UTC (rev 1237) +++ sqlalchemy/trunk/lib/sqlalchemy/attributes.py 2006-04-02 04:13:03 UTC (rev 1238) </span><span class="lines">@@ -200,6 +200,9 @@ </span><span class="cx"> self.uselist = uselist </span><span class="cx"> self.kwargs = kwargs </span><span class="cx"> </span><ins>+ def clear(self): + self.plain_init(self.manager.attribute_history(self.obj)) + </ins><span class="cx"> def plain_init(self, attrhist): </span><span class="cx"> if not self.uselist: </span><span class="cx"> p = ScalarAttribute(self.obj, self.key, **self.kwargs) </span></span></pre></div> <a id="sqlalchemytrunktestmapperpy"></a> <div class="modfile"><h4>Modified: sqlalchemy/trunk/test/mapper.py (1237 => 1238)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/trunk/test/mapper.py 2006-04-02 04:09:05 UTC (rev 1237) +++ sqlalchemy/trunk/test/mapper.py 2006-04-02 04:13:03 UTC (rev 1238) </span><span class="lines">@@ -115,6 +115,15 @@ </span><span class="cx"> self.assert_(u.user_name == 'jack') </span><span class="cx"> self.assert_(a not in u.addresses) </span><span class="cx"> </span><ins>+ def testrefresh_lazy(self): + """tests that when a lazy loader is set as a trigger on an object's attribute (at the attribute level, not the class level), a refresh() operation doesnt fire the lazy loader or create any problems""" + m = mapper(User, users, properties={'addresses':relation(mapper(Address, addresses))}) + m2 = m.options(lazyload('addresses')) + u = m2.selectfirst(users.c.user_id==8) + def go(): + objectstore.refresh(u) + self.assert_sql_count(db, go, 1) + </ins><span class="cx"> def testexpire(self): </span><span class="cx"> m = mapper(User, users, properties={'addresses':relation(mapper(Address, addresses))}) </span><span class="cx"> u = m.get(7) </span></span></pre> </div> </div> </body> </html> |
From: <co...@sq...> - 2006-04-02 04:09:16
|
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head><style type="text/css"><!-- #msg dl { border: 1px #006 solid; background: #369; padding: 6px; color: #fff; } #msg dt { float: left; width: 6em; font-weight: bold; } #msg dt:after { content:':';} #msg dl, #msg dt, #msg ul, #msg li { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; } #msg dl a { font-weight: bold} #msg dl a:link { color:#fc3; } #msg dl a:active { color:#ff0; } #msg dl a:visited { color:#cc6; } h3 { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; font-weight: bold; } #msg pre { overflow: auto; background: #ffc; border: 1px #fc0 solid; padding: 6px; } #msg ul, pre { overflow: auto; } #patch { width: 100%; } #patch h4 {font-family: verdana,arial,helvetica,sans-serif;font-size:10pt;padding:8px;background:#369;color:#fff;margin:0;} #patch .propset h4, #patch .binary h4 {margin:0;} #patch pre {padding:0;line-height:1.2em;margin:0;} #patch .diff {width:100%;background:#eee;padding: 0 0 10px 0;overflow:auto;} #patch .propset .diff, #patch .binary .diff {padding:10px 0;} #patch span {display:block;padding:0 10px;} #patch .modfile, #patch .addfile, #patch .delfile, #patch .propset, #patch .binary, #patch .copfile {border:1px solid #ccc;margin:10px 0;} #patch ins {background:#dfd;text-decoration:none;display:block;padding:0 10px;} #patch del {background:#fdd;text-decoration:none;display:block;padding:0 10px;} #patch .lines, .info {color:#888;background:#fff;} --></style> <title>[1237] sqlalchemy/trunk/test/select.py: fix to oeprator test for new parenthesized rules</title> </head> <body> <div id="msg"> <dl> <dt>Revision</dt> <dd>1237</dd> <dt>Author</dt> <dd>zzzeek</dd> <dt>Date</dt> <dd>2006-04-01 22:09:05 -0600 (Sat, 01 Apr 2006)</dd> </dl> <h3>Log Message</h3> <pre>fix to oeprator test for new parenthesized rules</pre> <h3>Modified Paths</h3> <ul> <li><a href="#sqlalchemytrunktestselectpy">sqlalchemy/trunk/test/select.py</a></li> </ul> </div> <div id="patch"> <h3>Diff</h3> <a id="sqlalchemytrunktestselectpy"></a> <div class="modfile"><h4>Modified: sqlalchemy/trunk/test/select.py (1236 => 1237)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/trunk/test/select.py 2006-04-02 03:22:17 UTC (rev 1236) +++ sqlalchemy/trunk/test/select.py 2006-04-02 04:09:05 UTC (rev 1237) </span><span class="lines">@@ -210,7 +210,7 @@ </span><span class="cx"> ) </span><span class="cx"> </span><span class="cx"> self.runtest( </span><del>- literal("a") + literal("b") * literal("c"), ":literal + :liter_1 * :liter_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></pre> </div> </div> </body> </html> |
From: <co...@sq...> - 2006-04-02 03:22:29
|
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head><style type="text/css"><!-- #msg dl { border: 1px #006 solid; background: #369; padding: 6px; color: #fff; } #msg dt { float: left; width: 6em; font-weight: bold; } #msg dt:after { content:':';} #msg dl, #msg dt, #msg ul, #msg li { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; } #msg dl a { font-weight: bold} #msg dl a:link { color:#fc3; } #msg dl a:active { color:#ff0; } #msg dl a:visited { color:#cc6; } h3 { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; font-weight: bold; } #msg pre { overflow: auto; background: #ffc; border: 1px #fc0 solid; padding: 6px; } #msg ul, pre { overflow: auto; } #patch { width: 100%; } #patch h4 {font-family: verdana,arial,helvetica,sans-serif;font-size:10pt;padding:8px;background:#369;color:#fff;margin:0;} #patch .propset h4, #patch .binary h4 {margin:0;} #patch pre {padding:0;line-height:1.2em;margin:0;} #patch .diff {width:100%;background:#eee;padding: 0 0 10px 0;overflow:auto;} #patch .propset .diff, #patch .binary .diff {padding:10px 0;} #patch span {display:block;padding:0 10px;} #patch .modfile, #patch .addfile, #patch .delfile, #patch .propset, #patch .binary, #patch .copfile {border:1px solid #ccc;margin:10px 0;} #patch ins {background:#dfd;text-decoration:none;display:block;padding:0 10px;} #patch del {background:#fdd;text-decoration:none;display:block;padding:0 10px;} #patch .lines, .info {color:#888;background:#fff;} --></style> <title>[1236] sqlalchemy/trunk/test: added "parenthesis" check on binary clauses referencing binary clauses, for [ticket:144]</title> </head> <body> <div id="msg"> <dl> <dt>Revision</dt> <dd>1236</dd> <dt>Author</dt> <dd>zzzeek</dd> <dt>Date</dt> <dd>2006-04-01 21:22:17 -0600 (Sat, 01 Apr 2006)</dd> </dl> <h3>Log Message</h3> <pre>added "parenthesis" check on binary clauses referencing binary clauses, for [ticket:144]</pre> <h3>Modified Paths</h3> <ul> <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="sqlalchemytrunklibsqlalchemysqlpy"></a> <div class="modfile"><h4>Modified: sqlalchemy/trunk/lib/sqlalchemy/sql.py (1235 => 1236)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/trunk/lib/sqlalchemy/sql.py 2006-04-01 21:00:41 UTC (rev 1235) +++ sqlalchemy/trunk/lib/sqlalchemy/sql.py 2006-04-02 03:22:17 UTC (rev 1236) </span><span class="lines">@@ -850,6 +850,10 @@ </span><span class="cx"> self.operator = operator </span><span class="cx"> self.type = type </span><span class="cx"> self.parens = False </span><ins>+ if isinstance(self.left, BinaryClause): + self.left.parens = True + if isinstance(self.right, BinaryClause): + self.right.parens = True </ins><span class="cx"> def copy_container(self): </span><span class="cx"> return BinaryClause(self.left.copy_container(), self.right.copy_container(), self.operator) </span><span class="cx"> def _get_from_objects(self): </span></span></pre></div> <a id="sqlalchemytrunktestselectpy"></a> <div class="modfile"><h4>Modified: sqlalchemy/trunk/test/select.py (1235 => 1236)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/trunk/test/select.py 2006-04-01 21:00:41 UTC (rev 1235) +++ sqlalchemy/trunk/test/select.py 2006-04-02 03:22:17 UTC (rev 1236) </span><span class="lines">@@ -346,6 +346,30 @@ </span><span class="cx"> self.runtest(select([literal("foo") + literal("bar")], from_obj=[table1]), </span><span class="cx"> "SELECT :literal + :liter_1 FROM mytable") </span><span class="cx"> </span><ins>+ def testcalculatedcolumns(self): + value_tbl = table('values', + Column('id', Integer), + Column('val1', Float), + Column('val2', Float), + ) + + self.runtest( + select([value_tbl.c.id, (value_tbl.c.val2 - + value_tbl.c.val1)/value_tbl.c.val1]), + "SELECT values.id, (values.val2 - values.val1) / values.val1 FROM values" + ) + + self.runtest( + select([value_tbl.c.id], (value_tbl.c.val2 - + value_tbl.c.val1)/value_tbl.c.val1 > 2.0), + "SELECT values.id FROM values WHERE ((values.val2 - values.val1) / values.val1) > :literal" + ) + + self.runtest( + select([value_tbl.c.id], value_tbl.c.val1 / (value_tbl.c.val2 - value_tbl.c.val1) /value_tbl.c.val1 > 2.0), + "SELECT values.id FROM values WHERE ((values.val1 / (values.val2 - values.val1)) / values.val1) > :literal" + ) + </ins><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="lines">@@ -554,7 +578,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, :liter_2), name=mytable.name + :mytable_name WHERE mytable.myid = hoho(:hoho) AND mytable.name = :literal + mytable.name + :liter_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> |
<!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>[1235] sqlalchemy/trunk/test: cleanup of attributes, better naming, added weak reference to base managed attribute to break circular refs, slightly shorter codepaths in some cases.</title> </head> <body> <div id="msg"> <dl> <dt>Revision</dt> <dd>1235</dd> <dt>Author</dt> <dd>zzzeek</dd> <dt>Date</dt> <dd>2006-04-01 15:00:41 -0600 (Sat, 01 Apr 2006)</dd> </dl> <h3>Log Message</h3> <pre>cleanup of attributes, better naming, added weak reference to base managed attribute to break circular refs, slightly shorter codepaths in some cases. added performance tester</pre> <h3>Modified Paths</h3> <ul> <li><a href="#sqlalchemytrunklibsqlalchemyattributespy">sqlalchemy/trunk/lib/sqlalchemy/attributes.py</a></li> </ul> <h3>Added Paths</h3> <ul> <li><a href="#sqlalchemytrunktestmasscreatepy">sqlalchemy/trunk/test/masscreate.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 (1234 => 1235)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/trunk/lib/sqlalchemy/attributes.py 2006-03-31 23:46:02 UTC (rev 1234) +++ sqlalchemy/trunk/lib/sqlalchemy/attributes.py 2006-04-01 21:00:41 UTC (rev 1235) </span><span class="lines">@@ -60,8 +60,18 @@ </span><span class="cx"> """appends a value to a list-based attribute without triggering a history event.""" </span><span class="cx"> h = self.manager.get_history(obj, self.key) </span><span class="cx"> h.append_nohistory(value) </span><ins>+ +class ManagedAttribute(object): + def __init__(self, obj, key): + self.__obj = weakref.ref(obj) + self.key = key + obj = property(lambda s:s.__obj()) + def history(self, **kwargs): + return self + def plain_init(self, *args, **kwargs): + pass </ins><span class="cx"> </span><del>-class PropHistory(object): </del><ins>+class ScalarAttribute(ManagedAttribute): </ins><span class="cx"> """Used by AttributeManager to track the history of a scalar attribute </span><span class="cx"> on an object instance. This is the "scalar history container" object. </span><span class="cx"> Has an interface similar to util.HistoryList </span><span class="lines">@@ -69,14 +79,9 @@ </span><span class="cx"> # make our own NONE to distinguish from "None" </span><span class="cx"> NONE = object() </span><span class="cx"> def __init__(self, obj, key, extension=None, **kwargs): </span><del>- self.obj = obj - self.key = key - self.orig = PropHistory.NONE </del><ins>+ ManagedAttribute.__init__(self, obj, key) + self.orig = ScalarAttribute.NONE </ins><span class="cx"> self.extension = extension </span><del>- def plain_init(self, *args): - pass - def gethistory(self, *args, **kwargs): - return self </del><span class="cx"> def clear(self): </span><span class="cx"> del self.obj.__dict__[self.key] </span><span class="cx"> def history_contains(self, obj): </span><span class="lines">@@ -85,22 +90,22 @@ </span><span class="cx"> self.obj.__dict__[self.key] = value </span><span class="cx"> def delattr_clean(self): </span><span class="cx"> del self.obj.__dict__[self.key] </span><del>- def getattr(self): </del><ins>+ def getattr(self, **kwargs): </ins><span class="cx"> return self.obj.__dict__[self.key] </span><del>- def setattr(self, value): </del><ins>+ def setattr(self, value, **kwargs): </ins><span class="cx"> if isinstance(value, list): </span><span class="cx"> raise InvalidRequestError("assigning a list to scalar property '%s' on '%s' instance %d" % (self.key, self.obj.__class__.__name__, id(self.obj))) </span><span class="cx"> orig = self.obj.__dict__.get(self.key, None) </span><span class="cx"> if orig is value: </span><span class="cx"> return </span><del>- if self.orig is PropHistory.NONE: </del><ins>+ if self.orig is ScalarAttribute.NONE: </ins><span class="cx"> self.orig = orig </span><span class="cx"> self.obj.__dict__[self.key] = value </span><span class="cx"> if self.extension is not None: </span><span class="cx"> self.extension.set(self.obj, value, orig) </span><del>- def delattr(self): </del><ins>+ def delattr(self, **kwargs): </ins><span class="cx"> orig = self.obj.__dict__.get(self.key, None) </span><del>- if self.orig is PropHistory.NONE: </del><ins>+ if self.orig is ScalarAttribute.NONE: </ins><span class="cx"> self.orig = orig </span><span class="cx"> self.obj.__dict__[self.key] = None </span><span class="cx"> if self.extension is not None: </span><span class="lines">@@ -110,35 +115,34 @@ </span><span class="cx"> def remove(self, obj): </span><span class="cx"> self.delattr() </span><span class="cx"> def rollback(self): </span><del>- if self.orig is not PropHistory.NONE: </del><ins>+ if self.orig is not ScalarAttribute.NONE: </ins><span class="cx"> self.obj.__dict__[self.key] = self.orig </span><del>- self.orig = PropHistory.NONE </del><ins>+ self.orig = ScalarAttribute.NONE </ins><span class="cx"> def commit(self): </span><del>- self.orig = PropHistory.NONE </del><ins>+ self.orig = ScalarAttribute.NONE </ins><span class="cx"> def added_items(self): </span><del>- if self.orig is not PropHistory.NONE: </del><ins>+ if self.orig is not ScalarAttribute.NONE: </ins><span class="cx"> return [self.obj.__dict__[self.key]] </span><span class="cx"> else: </span><span class="cx"> return [] </span><span class="cx"> def deleted_items(self): </span><del>- if self.orig is not PropHistory.NONE and self.orig is not None: </del><ins>+ if self.orig is not ScalarAttribute.NONE and self.orig is not None: </ins><span class="cx"> return [self.orig] </span><span class="cx"> else: </span><span class="cx"> return [] </span><span class="cx"> def unchanged_items(self): </span><del>- if self.orig is PropHistory.NONE: </del><ins>+ if self.orig is ScalarAttribute.NONE: </ins><span class="cx"> return [self.obj.__dict__[self.key]] </span><span class="cx"> else: </span><span class="cx"> return [] </span><span class="cx"> </span><del>-class ListElement(util.HistoryArraySet): </del><ins>+class ListAttribute(util.HistoryArraySet, ManagedAttribute): </ins><span class="cx"> """Used by AttributeManager to track the history of a list-based object attribute. </span><span class="cx"> This is the "list history container" object. </span><span class="cx"> Subclasses util.HistoryArraySet to provide "onchange" event handling as well </span><span class="cx"> as a plugin point for BackrefExtension objects.""" </span><span class="cx"> def __init__(self, obj, key, data=None, extension=None, **kwargs): </span><del>- self.obj = obj - self.key = key </del><ins>+ ManagedAttribute.__init__(self, obj, key) </ins><span class="cx"> self.extension = extension </span><span class="cx"> # if we are given a list, try to behave nicely with an existing </span><span class="cx"> # list that might be set on the object already </span><span class="lines">@@ -157,16 +161,12 @@ </span><span class="cx"> obj.__dict__[key] = [] </span><span class="cx"> </span><span class="cx"> util.HistoryArraySet.__init__(self, list_, readonly=kwargs.get('readonly', False)) </span><del>- def plain_init(self, *args): - pass - def gethistory(self, *args, **kwargs): - return self </del><span class="cx"> def list_value_changed(self, obj, key, item, listval, isdelete): </span><span class="cx"> pass </span><del>- def setattr(self, value): </del><ins>+ def setattr(self, value, **kwargs): </ins><span class="cx"> self.obj.__dict__[self.key] = value </span><span class="cx"> self.set_data(value) </span><del>- def delattr(self, value): </del><ins>+ def delattr(self, value, **kwargs): </ins><span class="cx"> pass </span><span class="cx"> def _setrecord(self, item): </span><span class="cx"> res = util.HistoryArraySet._setrecord(self, item) </span><span class="lines">@@ -182,33 +182,39 @@ </span><span class="cx"> if self.extension is not None: </span><span class="cx"> self.extension.delete(self.obj, item) </span><span class="cx"> return res </span><del>- -class CallableProp(object): </del><ins>+ +# deprecated +class ListElement(ListAttribute):pass + +class TriggeredAttribute(ManagedAttribute): </ins><span class="cx"> """Used by AttributeManager to allow the attaching of a callable item, representing the future value </span><del>- of a particular attribute on a particular object instance, to an attribute on an object. - This is the "callable history container" object. - When the attributemanager first accesses the object attribute, either to get its history or - its real value, the __call__ method - is invoked which runs the underlying callable_ and sets the new value to the object attribute, - at which point the CallableProp itself is dereferenced.""" </del><ins>+ of a particular attribute on a particular object instance, as the current attribute on an object. + When accessed normally, its history() method is invoked to run the underlying callable, which + is then used to create a new ScalarAttribute or ListAttribute. This new attribute object + is then registered with the attribute manager to replace this TriggeredAttribute as the + current ManagedAttribute.""" </ins><span class="cx"> def __init__(self, manager, callable_, obj, key, uselist = False, live = False, **kwargs): </span><ins>+ ManagedAttribute.__init__(self, obj, key) </ins><span class="cx"> self.manager = manager </span><span class="cx"> self.callable_ = callable_ </span><del>- self.obj = obj - self.key = key </del><span class="cx"> self.uselist = uselist </span><del>- self.live = live </del><span class="cx"> self.kwargs = kwargs </span><span class="cx"> </span><span class="cx"> def plain_init(self, attrhist): </span><span class="cx"> if not self.uselist: </span><del>- p = PropHistory(self.obj, self.key, **self.kwargs) </del><ins>+ p = ScalarAttribute(self.obj, self.key, **self.kwargs) </ins><span class="cx"> self.obj.__dict__[self.key] = None </span><span class="cx"> else: </span><del>- p = self.manager.create_list(self.obj, self.key, None, readonly=self.live, **self.kwargs) </del><ins>+ p = self.manager.create_list(self.obj, self.key, None, **self.kwargs) </ins><span class="cx"> attrhist[self.key] = p </span><del>- - def gethistory(self, passive=False, *args, **kwargs): </del><ins>+ + def __getattr__(self, key): + def callit(*args, **kwargs): + passive = kwargs.pop('passive', False) + return getattr(self.history(passive=passive), key)(*args, **kwargs) + return callit + + def history(self, passive=False): </ins><span class="cx"> if not self.uselist: </span><span class="cx"> if self.obj.__dict__.get(self.key, None) is None: </span><span class="cx"> if passive: </span><span class="lines">@@ -222,9 +228,9 @@ </span><span class="cx"> raise AssertionError("AttributeError caught in callable prop:" + str(e.args)) </span><span class="cx"> self.obj.__dict__[self.key] = value </span><span class="cx"> </span><del>- p = PropHistory(self.obj, self.key, **self.kwargs) </del><ins>+ p = ScalarAttribute(self.obj, self.key, **self.kwargs) </ins><span class="cx"> else: </span><del>- if self.live or not self.obj.__dict__.has_key(self.key) or len(self.obj.__dict__[self.key]) == 0: </del><ins>+ if not self.obj.__dict__.has_key(self.key) or len(self.obj.__dict__[self.key]) == 0: </ins><span class="cx"> if passive: </span><span class="cx"> value = None </span><span class="cx"> else: </span><span class="lines">@@ -236,12 +242,11 @@ </span><span class="cx"> raise AssertionError("AttributeError caught in callable prop:" + str(e.args)) </span><span class="cx"> else: </span><span class="cx"> value = None </span><del>- p = self.manager.create_list(self.obj, self.key, value, readonly=self.live, **self.kwargs) - if not self.live and not passive: </del><ins>+ p = self.manager.create_list(self.obj, self.key, value, **self.kwargs) + if not passive: </ins><span class="cx"> # set the new history list as the new attribute, discards ourself </span><span class="cx"> self.manager.attribute_history(self.obj)[self.key] = p </span><span class="cx"> self.manager = None </span><del>- # unless we are "live", in which case we stay around to execute again </del><span class="cx"> return p </span><span class="cx"> </span><span class="cx"> def commit(self): </span><span class="lines">@@ -260,6 +265,11 @@ </span><span class="cx"> pass </span><span class="cx"> </span><span class="cx"> class GenericBackrefExtension(AttributeExtension): </span><ins>+ """an attachment to a ScalarAttribute or ListAttribute which receives change events, + and upon such an event synchronizes a two-way relationship. A typical two-way + relationship is a parent object containing a list of child objects, where each + child object references the parent. The other are two objects which contain + scalar references to each other.""" </ins><span class="cx"> def __init__(self, key): </span><span class="cx"> self.key = key </span><span class="cx"> def set(self, obj, child, oldchild): </span><span class="lines">@@ -292,22 +302,22 @@ </span><span class="cx"> will communicate change events back to this AttributeManager.""" </span><span class="cx"> return SmartProperty(self, key, uselist) </span><span class="cx"> def create_list(self, obj, key, list_, **kwargs): </span><del>- """creates a history-aware list property, defaulting to a ListElement which </del><ins>+ """creates a history-aware list property, defaulting to a ListAttribute which </ins><span class="cx"> is a subclass of HistoryArrayList.""" </span><del>- return ListElement(obj, key, list_, **kwargs) </del><ins>+ return ListAttribute(obj, key, list_, **kwargs) </ins><span class="cx"> def create_callable(self, obj, key, func, uselist, **kwargs): </span><span class="cx"> """creates a callable container that will invoke a function the first </span><span class="cx"> time an object property is accessed. The return value of the function </span><span class="cx"> will become the object property's new value.""" </span><del>- return CallableProp(self, func, obj, key, uselist, **kwargs) </del><ins>+ return TriggeredAttribute(self, func, obj, key, uselist, **kwargs) </ins><span class="cx"> </span><span class="cx"> def get_attribute(self, obj, key, **kwargs): </span><span class="cx"> """returns the value of an object's scalar attribute, or None if </span><span class="cx"> its not defined on the object (since we are a property accessor, this </span><span class="cx"> is considered more appropriate than raising AttributeError).""" </span><del>- h = self.get_history(obj, key, **kwargs) </del><ins>+ h = self.get_unexec_history(obj, key) </ins><span class="cx"> try: </span><del>- return h.getattr() </del><ins>+ return h.getattr(**kwargs) </ins><span class="cx"> except KeyError: </span><span class="cx"> return None </span><span class="cx"> </span><span class="lines">@@ -317,12 +327,12 @@ </span><span class="cx"> </span><span class="cx"> def set_attribute(self, obj, key, value, **kwargs): </span><span class="cx"> """sets the value of an object's attribute.""" </span><del>- self.get_history(obj, key, **kwargs).setattr(value) </del><ins>+ self.get_unexec_history(obj, key).setattr(value, **kwargs) </ins><span class="cx"> self.value_changed(obj, key, value) </span><span class="cx"> </span><span class="cx"> def delete_attribute(self, obj, key, **kwargs): </span><span class="cx"> """deletes the value from an object's attribute.""" </span><del>- self.get_history(obj, key, **kwargs).delattr() </del><ins>+ self.get_unexec_history(obj, key).delattr(**kwargs) </ins><span class="cx"> self.value_changed(obj, key, None) </span><span class="cx"> </span><span class="cx"> def rollback(self, *obj): </span><span class="lines">@@ -363,47 +373,55 @@ </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 = managed_attribute_dict() - obj.__dict__['_managed_attributes'] = d </del><ins>+ d = {} + obj._managed_attributes = d </ins><span class="cx"> cls_managed = self.class_managed(obj.__class__) </span><span class="cx"> for value in cls_managed.values(): </span><span class="cx"> value(obj, d).plain_init(d) </span><span class="cx"> </span><del>- def get_history(self, obj, key, passive=False, **kwargs): </del><ins>+ def get_unexec_history(self, obj, key): </ins><span class="cx"> """returns the "history" container for the given attribute on the given object. </span><span class="cx"> If the container does not exist, it will be created based on the class-level </span><span class="cx"> history container definition.""" </span><span class="cx"> try: </span><del>- return obj.__dict__['_managed_attributes'][key].gethistory(passive=passive, **kwargs) </del><ins>+ return obj._managed_attributes[key] + except AttributeError, ae: + return self.class_managed(obj.__class__)[key](obj) </ins><span class="cx"> except KeyError, e: </span><del>- return self.class_managed(obj.__class__)[key](obj, **kwargs).gethistory(passive=passive, **kwargs) </del><ins>+ return self.class_managed(obj.__class__)[key](obj) </ins><span class="cx"> </span><ins>+ def get_history(self, obj, key, **kwargs): + """returns the "history" container, and calls its history() method, + which for a TriggeredAttribute will execute the underlying callable and return the + resulting ScalarAttribute or ListHistory object.""" + return self.get_unexec_history(obj, key).history(**kwargs) + </ins><span class="cx"> def attribute_history(self, obj): </span><span class="cx"> """returns a dictionary of "history" containers corresponding to the given object. </span><span class="cx"> this dictionary is attached to the object via the attribute '_managed_attributes'. </span><span class="cx"> If the dictionary does not exist, it will be created.""" </span><span class="cx"> try: </span><del>- return obj.__dict__['_managed_attributes'] - except KeyError: </del><ins>+ return obj._managed_attributes + except AttributeError: </ins><span class="cx"> trigger = obj.__dict__.pop('_managed_trigger', None) </span><span class="cx"> if trigger: </span><span class="cx"> trigger() </span><del>- attr = managed_attribute_dict() - obj.__dict__['_managed_attributes'] = attr </del><ins>+ attr = {} + obj._managed_attributes = attr </ins><span class="cx"> return attr </span><span class="cx"> </span><span class="cx"> def trigger_history(self, obj, callable): </span><span class="cx"> try: </span><del>- del obj.__dict__['_managed_attributes'] </del><ins>+ del obj._managed_attributes </ins><span class="cx"> except KeyError: </span><span class="cx"> pass </span><del>- obj.__dict__['_managed_trigger'] = callable </del><ins>+ obj._managed_trigger = callable </ins><span class="cx"> </span><span class="cx"> def untrigger_history(self, obj): </span><del>- del obj.__dict__['_managed_trigger'] </del><ins>+ del obj._managed_trigger </ins><span class="cx"> </span><span class="cx"> def has_trigger(self, obj): </span><del>- return obj.__dict__.has_key('_managed_trigger') </del><ins>+ return hasattr(obj, '_managed_trigger') </ins><span class="cx"> </span><span class="cx"> def reset_history(self, obj, key): </span><span class="cx"> """removes the history object for the given attribute on the given object. </span><span class="lines">@@ -423,7 +441,7 @@ </span><span class="cx"> """returns a dictionary of "history container definitions", which is attached to a </span><span class="cx"> class. creates the dictionary if it doesnt exist.""" </span><span class="cx"> try: </span><del>- attr = getattr(class_, '_class_managed_attributes') </del><ins>+ attr = class_._class_managed_attributes </ins><span class="cx"> except AttributeError: </span><span class="cx"> attr = {} </span><span class="cx"> class_._class_managed_attributes = attr </span><span class="lines">@@ -432,10 +450,10 @@ </span><span class="cx"> </span><span class="cx"> def reset_class_managed(self, class_): </span><span class="cx"> try: </span><del>- attr = getattr(class_, '_class_managed_attributes') </del><ins>+ attr = class_._class_managed_attributes </ins><span class="cx"> for key in attr.keys(): </span><span class="cx"> delattr(class_, key) </span><del>- delattr(class_, '_class_managed_attributes') </del><ins>+ del class_._class_managed_attributes </ins><span class="cx"> except AttributeError: </span><span class="cx"> pass </span><span class="cx"> </span><span class="lines">@@ -450,7 +468,7 @@ </span><span class="cx"> if callable_ is not None: </span><span class="cx"> return self.create_callable(obj, key, callable_, uselist=uselist, **kwargs) </span><span class="cx"> elif not uselist: </span><del>- return PropHistory(obj, key, **kwargs) </del><ins>+ return ScalarAttribute(obj, key, **kwargs) </ins><span class="cx"> else: </span><span class="cx"> return self.create_list(obj, key, None, **kwargs) </span><span class="cx"> </span><span class="lines">@@ -478,9 +496,3 @@ </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><del>-# make this function return a weakref.WeakValueDictionary to avoid -# creating circular references in objects -def managed_attribute_dict(): - return {} -# return weakref.WeakValueDictionary() - </del></span></pre></div> <a id="sqlalchemytrunktestmasscreatepy"></a> <div class="addfile"><h4>Added: sqlalchemy/trunk/test/masscreate.py (1234 => 1235)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/trunk/test/masscreate.py 2006-03-31 23:46:02 UTC (rev 1234) +++ sqlalchemy/trunk/test/masscreate.py 2006-04-01 21:00:41 UTC (rev 1235) </span><span class="lines">@@ -0,0 +1,38 @@ </span><ins>+# times how long it takes to create 26000 objects + +from sqlalchemy.attributes import * +import time + +manage_attributes = True +init_attributes = manage_attributes and True + +class User(object): + pass +class Address(object): + pass + +attr_manager = AttributeManager() +if manage_attributes: + attr_manager.register_attribute(User, 'id', uselist=False) + attr_manager.register_attribute(User, 'name', uselist=False) + attr_manager.register_attribute(User, 'addresses', uselist=True) + attr_manager.register_attribute(Address, 'email', uselist=False) + +now = time.time() +for i in range(0,130): + u = User() + if init_attributes: + attr_manager.init_attr(u) + u.id = i + u.name = "user " + str(i) + if not manage_attributes: + u.addresses = [] + for j in range(0,200): + a = Address() + if init_attributes: + attr_manager.init_attr(a) + a.email = 'fo...@ba...' + u.addresses.append(u) + +total = time.time() - now +print "Total time", total </ins><span class="cx">\ No newline at end of file </span></span></pre> </div> </div> </body> </html> |
From: <co...@sq...> - 2006-03-31 23:46: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>[1234] sqlalchemy/trunk/test/objectstore.py: second assertion</title> </head> <body> <div id="msg"> <dl> <dt>Revision</dt> <dd>1234</dd> <dt>Author</dt> <dd>zzzeek</dd> <dt>Date</dt> <dd>2006-03-31 17:46:02 -0600 (Fri, 31 Mar 2006)</dd> </dl> <h3>Log Message</h3> <pre>second assertion</pre> <h3>Modified Paths</h3> <ul> <li><a href="#sqlalchemytrunktestobjectstorepy">sqlalchemy/trunk/test/objectstore.py</a></li> </ul> </div> <div id="patch"> <h3>Diff</h3> <a id="sqlalchemytrunktestobjectstorepy"></a> <div class="modfile"><h4>Modified: sqlalchemy/trunk/test/objectstore.py (1233 => 1234)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/trunk/test/objectstore.py 2006-03-31 23:43:20 UTC (rev 1233) +++ sqlalchemy/trunk/test/objectstore.py 2006-03-31 23:46:02 UTC (rev 1234) </span><span class="lines">@@ -336,7 +336,8 @@ </span><span class="cx"> b.a = a2 </span><span class="cx"> assert b not in sess.deleted </span><span class="cx"> objectstore.commit() </span><del>- </del><ins>+ assert b in sess.identity_map.values() + </ins><span class="cx"> class DefaultTest(AssertMixin): </span><span class="cx"> """tests that when saving objects whose table contains DefaultGenerators, either python-side, preexec or database-side, </span><span class="cx"> the newly saved instances receive all the default values either through a post-fetch or getting the pre-exec'ed </span></span></pre> </div> </div> </body> </html> |
From: <co...@sq...> - 2006-03-31 23:43: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>[1233] sqlalchemy/trunk/test: added unit test for the old commit that was in [changeset:1186].</title> </head> <body> <div id="msg"> <dl> <dt>Revision</dt> <dd>1233</dd> <dt>Author</dt> <dd>zzzeek</dd> <dt>Date</dt> <dd>2006-03-31 17:43:20 -0600 (Fri, 31 Mar 2006)</dd> </dl> <h3>Log Message</h3> <pre>added unit test for the old commit that was in [changeset:1186]. modified its behavior a bit to not delete private relationships unless they were already marked as deleted at the attribute manipulation level. got "switching" behavior from one private relationship to another to work, added a unit test for that.</pre> <h3>Modified Paths</h3> <ul> <li><a href="#sqlalchemytrunklibsqlalchemymappingpropertiespy">sqlalchemy/trunk/lib/sqlalchemy/mapping/properties.py</a></li> <li><a href="#sqlalchemytrunklibsqlalchemymappingunitofworkpy">sqlalchemy/trunk/lib/sqlalchemy/mapping/unitofwork.py</a></li> <li><a href="#sqlalchemytrunktestobjectstorepy">sqlalchemy/trunk/test/objectstore.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 (1232 => 1233)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/trunk/lib/sqlalchemy/mapping/properties.py 2006-03-31 22:28:17 UTC (rev 1232) +++ sqlalchemy/trunk/lib/sqlalchemy/mapping/properties.py 2006-03-31 23:43:20 UTC (rev 1233) </span><span class="lines">@@ -451,7 +451,10 @@ </span><span class="cx"> for child in childlist.deleted_items() + childlist.unchanged_items(): </span><span class="cx"> if child is None: </span><span class="cx"> continue </span><del>- uowcommit.register_object(child, isdelete=True) </del><ins>+ # if private child object, and is in the uow's "deleted" list, + # insure its in the list of items to be deleted + if child in uowcommit.uow.deleted: + uowcommit.register_object(child, isdelete=True) </ins><span class="cx"> elif self.post_update: </span><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="lines">@@ -462,16 +465,15 @@ </span><span class="cx"> # head object is being deleted, and we manage its list of child objects </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><del>- # if we are privately managed, then all our objects should - # 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. </del><span class="cx"> for obj in deplist: </span><span class="cx"> childlist = getlist(obj, False) </span><span class="cx"> for child in childlist.deleted_items() + childlist.unchanged_items(): </span><span class="cx"> if child is None: </span><span class="cx"> continue </span><del>- uowcommit.register_object(child, isdelete=True) </del><ins>+ # if private child object, and is in the uow's "deleted" list, + # insure its in the list of items to be deleted + if child in uowcommit.uow.deleted: + uowcommit.register_object(child, isdelete=True) </ins><span class="cx"> else: </span><span class="cx"> for obj in deplist: </span><span class="cx"> childlist = getlist(obj, False) </span><span class="lines">@@ -529,8 +531,6 @@ </span><span class="cx"> for child in childlist.deleted_items(): </span><span class="cx"> if not self.private: </span><span class="cx"> self._synchronize(obj, child, None, True) </span><del>- if self.direction == PropertyLoader.ONETOMANY: - # for a cyclical task, this registration is handled by the objectstore </del><span class="cx"> uowcommit.register_object(child, isdelete=self.private) </span><span class="cx"> </span><span class="cx"> def execute(self, instance, row, identitykey, imap, isnew): </span></span></pre></div> <a id="sqlalchemytrunklibsqlalchemymappingunitofworkpy"></a> <div class="modfile"><h4>Modified: sqlalchemy/trunk/lib/sqlalchemy/mapping/unitofwork.py (1232 => 1233)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/trunk/lib/sqlalchemy/mapping/unitofwork.py 2006-03-31 22:28:17 UTC (rev 1232) +++ sqlalchemy/trunk/lib/sqlalchemy/mapping/unitofwork.py 2006-03-31 23:43:20 UTC (rev 1233) </span><span class="lines">@@ -44,7 +44,8 @@ </span><span class="cx"> def list_value_changed(self, obj, key, item, listval, isdelete): </span><span class="cx"> sess = get_session(obj) </span><span class="cx"> if not isdelete and sess.deleted.contains(item): </span><del>- raise InvalidRequestError("re-inserting a deleted value into a list") </del><ins>+ #raise InvalidRequestError("re-inserting a deleted value into a list") + del sess.deleted[item] </ins><span class="cx"> sess.modified_lists.append(self) </span><span class="cx"> if self.deleteremoved and isdelete: </span><span class="cx"> sess.register_deleted(item) </span></span></pre></div> <a id="sqlalchemytrunktestobjectstorepy"></a> <div class="modfile"><h4>Modified: sqlalchemy/trunk/test/objectstore.py (1232 => 1233)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/trunk/test/objectstore.py 2006-03-31 22:28:17 UTC (rev 1232) +++ sqlalchemy/trunk/test/objectstore.py 2006-03-31 23:43:20 UTC (rev 1233) </span><span class="lines">@@ -263,7 +263,80 @@ </span><span class="cx"> e.assigned = datetime.date.today() </span><span class="cx"> e.data = 'some more data' </span><span class="cx"> objectstore.commit() </span><ins>+ +class PrivateAttrTest(AssertMixin): + """tests various things to do with private=True mappers""" + def setUpAll(self): + global a_table, b_table + a_table = Table('a',testbase.db, + Column('a_id', Integer, Sequence('next_a_id'), primary_key=True), + Column('data', String(10)), + ).create() + + b_table = Table('b',testbase.db, + Column('b_id',Integer,Sequence('next_b_id'),primary_key=True), + Column('a_id',Integer,ForeignKey('a.a_id')), + Column('data',String(10))).create() + def tearDownAll(self): + b_table.drop() + a_table.drop() + def setUp(self): + objectstore.clear() + clear_mappers() + + def testsinglecommit(self): + """tests that a commit of a single object deletes private relationships""" + class A(object):pass + class B(object):pass + + assign_mapper(B,b_table) + assign_mapper(A,a_table,properties= {'bs' : relation + (B.mapper,private=True)}) + + # create some objects + a = A(data='a1') + a.bs = [] + + # add a 'B' instance + b1 = B(data='1111') + a.bs.append(b1) + + # add another one + b2 = B(data='2222') + a.bs.append(b2) + + # inserts both A and Bs + objectstore.commit(a) + + objectstore.delete(a) + objectstore.commit(a) </ins><span class="cx"> </span><ins>+ assert b_table.count().scalar() == 0 + + def testswitchparent(self): + """tests that you can switch the parent of an object in a backref scenario""" + class A(object):pass + class B(object):pass + + assign_mapper(B,b_table) + assign_mapper(A,a_table,properties= { + 'bs' : relation (B.mapper,private=True, backref='a')} + ) + a1 = A(data='testa1') + a2 = A(data='testa2') + b = B(data='testb') + b.a = a1 + objectstore.commit() + objectstore.clear() + sess = objectstore.get_session() + a1 = A.mapper.get(a1.a_id) + a2 = A.mapper.get(a2.a_id) + assert a1.bs[0].a is a1 + b = a1.bs[0] + b.a = a2 + assert b not in sess.deleted + objectstore.commit() + </ins><span class="cx"> class DefaultTest(AssertMixin): </span><span class="cx"> """tests that when saving objects whose table contains DefaultGenerators, either python-side, preexec or database-side, </span><span class="cx"> the newly saved instances receive all the default values either through a post-fetch or getting the pre-exec'ed </span></span></pre> </div> </div> </body> </html> |