[Sqlalchemy-commits] [1112] sqlalchemy/trunk/test: serious overhaul to get eager loads to work inlin
Brought to you by:
zzzeek
<!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>[1112] sqlalchemy/trunk/test: serious overhaul to get eager loads to work inline with an inheriting mapper, when the inheritance/eager loads share the same table.</title> </head> <body> <div id="msg"> <dl> <dt>Revision</dt> <dd>1112</dd> <dt>Author</dt> <dd>zzzeek</dd> <dt>Date</dt> <dd>2006-03-08 14:51:51 -0600 (Wed, 08 Mar 2006)</dd> </dl> <h3>Log Message</h3> <pre>serious overhaul to get eager loads to work inline with an inheriting mapper, when the inheritance/eager loads share the same table. mapper inheritance will also favor the columns from the child table over those of the parent table when assigning column values to object attributes. "correlated subqueries" require a flag "correlated=True" if they are in the FROM clause of another SELECT statement, and they want to be correlated. this flag is set by default when using an "exists" clause.</pre> <h3>Modified Paths</h3> <ul> <li><a href="#sqlalchemytrunklibsqlalchemyenginepy">sqlalchemy/trunk/lib/sqlalchemy/engine.py</a></li> <li><a href="#sqlalchemytrunklibsqlalchemymappingmapperpy">sqlalchemy/trunk/lib/sqlalchemy/mapping/mapper.py</a></li> <li><a href="#sqlalchemytrunklibsqlalchemymappingpropertiespy">sqlalchemy/trunk/lib/sqlalchemy/mapping/properties.py</a></li> <li><a href="#sqlalchemytrunklibsqlalchemysqlpy">sqlalchemy/trunk/lib/sqlalchemy/sql.py</a></li> <li><a href="#sqlalchemytrunklibsqlalchemyutilpy">sqlalchemy/trunk/lib/sqlalchemy/util.py</a></li> <li><a href="#sqlalchemytrunktestinheritancepy">sqlalchemy/trunk/test/inheritance.py</a></li> <li><a href="#sqlalchemytrunktestselectpy">sqlalchemy/trunk/test/select.py</a></li> </ul> </div> <div id="patch"> <h3>Diff</h3> <a id="sqlalchemytrunklibsqlalchemyenginepy"></a> <div class="modfile"><h4>Modified: sqlalchemy/trunk/lib/sqlalchemy/engine.py (1111 => 1112)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/trunk/lib/sqlalchemy/engine.py 2006-03-08 01:18:26 UTC (rev 1111) +++ sqlalchemy/trunk/lib/sqlalchemy/engine.py 2006-03-08 20:51:51 UTC (rev 1112) </span><span class="lines">@@ -712,6 +712,7 @@ </span><span class="cx"> if self.props.setdefault(colname, rec) is not rec: </span><span class="cx"> self.props[colname] = (ResultProxy.AmbiguousColumn(colname), 0) </span><span class="cx"> self.keys.append(colname) </span><ins>+ #print "COLNAME", colname </ins><span class="cx"> self.props[i] = rec </span><span class="cx"> i+=1 </span><span class="cx"> </span><span class="lines">@@ -719,6 +720,7 @@ </span><span class="cx"> if isinstance(key, schema.Column) or isinstance(key, sql.ColumnElement): </span><span class="cx"> try: </span><span class="cx"> rec = self.props[key._label.lower()] </span><ins>+ #print "GOT IT FROM LABEL FOR ", key._label </ins><span class="cx"> except KeyError: </span><span class="cx"> try: </span><span class="cx"> rec = self.props[key.key.lower()] </span></span></pre></div> <a id="sqlalchemytrunklibsqlalchemymappingmapperpy"></a> <div class="modfile"><h4>Modified: sqlalchemy/trunk/lib/sqlalchemy/mapping/mapper.py (1111 => 1112)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/trunk/lib/sqlalchemy/mapping/mapper.py 2006-03-08 01:18:26 UTC (rev 1111) +++ sqlalchemy/trunk/lib/sqlalchemy/mapping/mapper.py 2006-03-08 20:51:51 UTC (rev 1112) </span><span class="lines">@@ -145,7 +145,12 @@ </span><span class="cx"> prop = ColumnProperty(column) </span><span class="cx"> self.props[column.key] = prop </span><span class="cx"> elif isinstance(prop, ColumnProperty): </span><del>- prop.columns.append(column) </del><ins>+ # the order which columns are appended to a ColumnProperty is significant, as the + # column at index 0 determines which result column is used to populate the object + # attribute, in the case of mapping against a join with column names repeated + # (and particularly in an inheritance relationship) + prop.columns.insert(0, column) + #prop.columns.append(column) </ins><span class="cx"> else: </span><span class="cx"> if not allow_column_override: </span><span class="cx"> raise ArgumentError("WARNING: column '%s' not being added due to property '%s'. Specify 'allow_column_override=True' to mapper() to ignore this condition." % (column.key, repr(prop))) </span><span class="lines">@@ -179,6 +184,12 @@ </span><span class="cx"> if getattr(prop, 'key', None) is None: </span><span class="cx"> prop.init(key, self) </span><span class="cx"> </span><ins>+ # this prints a summary of the object attributes and how they + # will be mapped to table columns + #print "mapper %s, columntoproperty:" % (self.class_.__name__) + #for key, value in self.columntoproperty.iteritems(): + # print key.table.name, key.key, [(v.key, v) for v in value] + </ins><span class="cx"> engines = property(lambda s: [t.engine for t in s.tables]) </span><span class="cx"> </span><span class="cx"> def add_property(self, key, prop): </span><span class="lines">@@ -638,9 +649,7 @@ </span><span class="cx"> def delete_obj(self, objects, uow): </span><span class="cx"> """called by a UnitOfWork object to delete objects, which involves a </span><span class="cx"> DELETE statement for each table used by this mapper, for each object in the list.""" </span><del>- l = list(self.tables) - l.reverse() - for table in l: </del><ins>+ for table in util.reversed(self.tables): </ins><span class="cx"> if not self._has_pks(table): </span><span class="cx"> continue </span><span class="cx"> delete = [] </span><span class="lines">@@ -703,7 +712,8 @@ </span><span class="cx"> order_by = self.table.default_order_by() </span><span class="cx"> </span><span class="cx"> if self._should_nest(**kwargs): </span><del>- s2 = sql.select(self.table.primary_key, whereclause, use_labels=True, **kwargs) </del><ins>+ s2 = sql.select(self.table.primary_key, whereclause, use_labels=True, from_obj=[self.table], **kwargs) +# raise "ok first thing", str(s2) </ins><span class="cx"> if not kwargs.get('distinct', False) and order_by: </span><span class="cx"> s2.order_by(*util.to_list(order_by)) </span><span class="cx"> s3 = s2.alias('rowcount') </span><span class="lines">@@ -711,6 +721,7 @@ </span><span class="cx"> for i in range(0, len(self.table.primary_key)): </span><span class="cx"> crit.append(s3.primary_key[i] == self.table.primary_key[i]) </span><span class="cx"> statement = sql.select([], sql.and_(*crit), from_obj=[self.table], use_labels=True) </span><ins>+ # raise "OK statement", str(statement) </ins><span class="cx"> if order_by: </span><span class="cx"> statement.order_by(*util.to_list(order_by)) </span><span class="cx"> else: </span><span class="lines">@@ -930,6 +941,8 @@ </span><span class="cx"> table.accept_visitor(self) </span><span class="cx"> def visit_table(self, table): </span><span class="cx"> self.tables.append(table) </span><ins>+ def __len__(self): + return len(self.tables) </ins><span class="cx"> def __getitem__(self, i): </span><span class="cx"> return self.tables[i] </span><span class="cx"> def __iter__(self): </span></span></pre></div> <a id="sqlalchemytrunklibsqlalchemymappingpropertiespy"></a> <div class="modfile"><h4>Modified: sqlalchemy/trunk/lib/sqlalchemy/mapping/properties.py (1111 => 1112)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/trunk/lib/sqlalchemy/mapping/properties.py 2006-03-08 01:18:26 UTC (rev 1111) +++ sqlalchemy/trunk/lib/sqlalchemy/mapping/properties.py 2006-03-08 20:51:51 UTC (rev 1112) </span><span class="lines">@@ -48,6 +48,7 @@ </span><span class="cx"> objectstore.uow().register_attribute(parent.class_, key, uselist = False) </span><span class="cx"> def execute(self, instance, row, identitykey, imap, isnew): </span><span class="cx"> if isnew: </span><ins>+ #print "POPULATING OBJ", instance.__class__.__name__, "COL", self.columns[0]._label, "WITH DATA", row[self.columns[0]], "ROW IS A", row.__class__.__name__, "COL ID", id(self.columns[0]) </ins><span class="cx"> instance.__dict__[self.key] = row[self.columns[0]] </span><span class="cx"> def __repr__(self): </span><span class="cx"> return "ColumnProperty(%s)" % repr([str(c) for c in self.columns]) </span><span class="lines">@@ -648,16 +649,19 @@ </span><span class="cx"> parent._has_eager = True </span><span class="cx"> </span><span class="cx"> self.eagertarget = self.target.alias() </span><ins>+# print "ALIAS", str(self.eagertarget.select()) #selectable.__class__.__name__ </ins><span class="cx"> if self.secondary: </span><span class="cx"> self.eagersecondary = self.secondary.alias() </span><span class="cx"> self.aliasizer = Aliasizer(self.target, self.secondary, aliases={ </span><span class="cx"> self.target:self.eagertarget, </span><span class="cx"> self.secondary:self.eagersecondary </span><span class="cx"> }) </span><ins>+ #print "TARGET", self.target </ins><span class="cx"> self.eagersecondaryjoin = self.secondaryjoin.copy_container() </span><span class="cx"> self.eagersecondaryjoin.accept_visitor(self.aliasizer) </span><span class="cx"> self.eagerprimary = self.primaryjoin.copy_container() </span><span class="cx"> self.eagerprimary.accept_visitor(self.aliasizer) </span><ins>+ #print "JOINS:", str(self.eagerprimary), "|", str(self.eagersecondaryjoin) </ins><span class="cx"> else: </span><span class="cx"> self.aliasizer = Aliasizer(self.target, aliases={self.target:self.eagertarget}) </span><span class="cx"> self.eagerprimary = self.primaryjoin.copy_container() </span><span class="lines">@@ -778,7 +782,8 @@ </span><span class="cx"> """gets an instance from a row, via this EagerLoader's mapper.""" </span><span class="cx"> fakerow = util.DictDecorator(row) </span><span class="cx"> for c in self.eagertarget.c: </span><del>- fakerow[c.parent] = row[c] </del><ins>+ parent = self.target._get_col_by_original(c.original) + fakerow[parent] = row[c] </ins><span class="cx"> row = fakerow </span><span class="cx"> return self.mapper._instance(row, imap, result_list) </span><span class="cx"> </span><span class="lines">@@ -882,15 +887,18 @@ </span><span class="cx"> """converts a table instance within an expression to be an alias of that table.""" </span><span class="cx"> def __init__(self, *tables, **kwargs): </span><span class="cx"> self.tables = {} </span><ins>+ self.aliases = kwargs.get('aliases', {}) </ins><span class="cx"> for t in tables: </span><span class="cx"> self.tables[t] = t </span><ins>+ if not self.aliases.has_key(t): + self.aliases[t] = sql.alias(t) + if isinstance(t, sql.Join): + for t2 in t.columns: + self.tables[t2.table] = t2 + self.aliases[t2.table] = self.aliases[t] </ins><span class="cx"> self.binary = None </span><del>- self.aliases = kwargs.get('aliases', {}) </del><span class="cx"> def get_alias(self, table): </span><del>- try: - return self.aliases[table] - except: - return self.aliases.setdefault(table, sql.alias(table)) </del><ins>+ return self.aliases[table] </ins><span class="cx"> def visit_compound(self, compound): </span><span class="cx"> self.visit_clauselist(compound) </span><span class="cx"> def visit_clauselist(self, clist): </span></span></pre></div> <a id="sqlalchemytrunklibsqlalchemysqlpy"></a> <div class="modfile"><h4>Modified: sqlalchemy/trunk/lib/sqlalchemy/sql.py (1111 => 1112)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/trunk/lib/sqlalchemy/sql.py 2006-03-08 01:18:26 UTC (rev 1111) +++ sqlalchemy/trunk/lib/sqlalchemy/sql.py 2006-03-08 20:51:51 UTC (rev 1112) </span><span class="lines">@@ -129,6 +129,7 @@ </span><span class="cx"> return BooleanExpression(ctest, and_(cleft, cright), 'BETWEEN') </span><span class="cx"> </span><span class="cx"> def exists(*args, **params): </span><ins>+ params['correlate'] = True </ins><span class="cx"> s = select(*args, **params) </span><span class="cx"> return BooleanExpression(TextClause("EXISTS"), s, None) </span><span class="cx"> </span><span class="lines">@@ -839,7 +840,7 @@ </span><span class="cx"> self.left = left </span><span class="cx"> self.right = right </span><span class="cx"> self.id = self.left.id + "_" + self.right.id </span><del>- </del><ins>+ </ins><span class="cx"> # TODO: if no onclause, do NATURAL JOIN </span><span class="cx"> if onclause is None: </span><span class="cx"> self.onclause = self._match_primaries(left, right) </span><span class="lines">@@ -852,7 +853,7 @@ </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="cx"> def _proxy_column(self, column): </span><del>- self._columns[column.table.name + "_" + column.key] = column </del><ins>+ self._columns[column._label] = column </ins><span class="cx"> if column.primary_key: </span><span class="cx"> self._primary_key.append(column) </span><span class="cx"> if column.foreign_key: </span><span class="lines">@@ -894,7 +895,9 @@ </span><span class="cx"> self.join = join </span><span class="cx"> def _exportable_columns(self): </span><span class="cx"> return [] </span><del>- </del><ins>+ + def alias(self, name=None): + return self.select(use_labels=True).alias(name) </ins><span class="cx"> def _process_from_dict(self, data, asfrom): </span><span class="cx"> for f in self.onclause._get_from_objects(): </span><span class="cx"> data[f.id] = f </span><span class="lines">@@ -915,7 +918,7 @@ </span><span class="cx"> self.original = baseselectable </span><span class="cx"> self.selectable = selectable </span><span class="cx"> if alias is None: </span><del>- n = getattr(self.original, 'name') </del><ins>+ n = getattr(self.original, 'name', None) </ins><span class="cx"> if n is None: </span><span class="cx"> n = 'anon' </span><span class="cx"> elif len(n) > 15: </span><span class="lines">@@ -974,7 +977,7 @@ </span><span class="cx"> self.__label = None </span><span class="cx"> def _get_label(self): </span><span class="cx"> if self.__label is None: </span><del>- if self.table is not None: </del><ins>+ if self.table is not None and self.table.name is not None: </ins><span class="cx"> self.__label = self.table.name + "_" + self.text </span><span class="cx"> else: </span><span class="cx"> self.__label = self.text </span><span class="lines">@@ -1164,7 +1167,7 @@ </span><span class="cx"> class Select(SelectBaseMixin, FromClause): </span><span class="cx"> """represents a SELECT statement, with appendable clauses, as well as </span><span class="cx"> the ability to execute itself and return a result set.""" </span><del>- def __init__(self, columns=None, whereclause = None, from_obj = [], order_by = None, group_by=None, having=None, use_labels = False, distinct=False, engine = None, limit=None, offset=None): </del><ins>+ def __init__(self, columns=None, whereclause = None, from_obj = [], order_by = None, group_by=None, having=None, use_labels = False, distinct=False, engine = None, limit=None, offset=None, correlate=False): </ins><span class="cx"> self._froms = util.OrderedDict() </span><span class="cx"> self.use_labels = use_labels </span><span class="cx"> self.id = "Select(%d)" % id(self) </span><span class="lines">@@ -1175,6 +1178,7 @@ </span><span class="cx"> self.oid_column = None </span><span class="cx"> self.limit = limit </span><span class="cx"> self.offset = offset </span><ins>+ self.correlate = correlate </ins><span class="cx"> </span><span class="cx"> # indicates if this select statement is a subquery inside another query </span><span class="cx"> self.issubquery = False </span><span class="lines">@@ -1224,9 +1228,11 @@ </span><span class="cx"> select.is_where = self.is_where </span><span class="cx"> select.issubquery = True </span><span class="cx"> select.parens = True </span><ins>+ if not self.is_where and not select.correlate: + return </ins><span class="cx"> if getattr(select, '_correlated', None) is None: </span><span class="cx"> select._correlated = self.select._froms </span><del>- </del><ins>+ </ins><span class="cx"> def append_column(self, column): </span><span class="cx"> if _is_literal(column): </span><span class="cx"> column = ColumnClause(str(column), self) </span><span class="lines">@@ -1266,7 +1272,8 @@ </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>- </del><ins>+ if self.oid_column is None and hasattr(fromclause, 'oid_column'): + self.oid_column = fromclause.oid_column </ins><span class="cx"> fromclause.accept_visitor(self._correlator) </span><span class="cx"> fromclause._process_from_dict(self._froms, True) </span><span class="cx"> </span></span></pre></div> <a id="sqlalchemytrunklibsqlalchemyutilpy"></a> <div class="modfile"><h4>Modified: sqlalchemy/trunk/lib/sqlalchemy/util.py (1111 => 1112)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/trunk/lib/sqlalchemy/util.py 2006-03-08 01:18:26 UTC (rev 1111) +++ sqlalchemy/trunk/lib/sqlalchemy/util.py 2006-03-08 20:51:51 UTC (rev 1112) </span><span class="lines">@@ -7,6 +7,7 @@ </span><span class="cx"> __all__ = ['OrderedProperties', 'OrderedDict', 'generic_repr', 'HashSet', 'AttrProp'] </span><span class="cx"> import thread, threading, weakref, UserList, time, string, inspect, sys </span><span class="cx"> from exceptions import * </span><ins>+import __builtin__ </ins><span class="cx"> </span><span class="cx"> def to_list(x): </span><span class="cx"> if x is None: </span><span class="lines">@@ -24,6 +25,18 @@ </span><span class="cx"> else: </span><span class="cx"> return x </span><span class="cx"> </span><ins>+def reversed(seq): + try: + return __builtin__.reversed(seq) + except: + def rev(): + i = len(seq) -1 + while i >= 0: + yield seq[i] + i -= 1 + raise StopIteration() + return rev() + </ins><span class="cx"> class AttrProp(object): </span><span class="cx"> """a quick way to stick a property accessor on an object""" </span><span class="cx"> def __init__(self, key): </span></span></pre></div> <a id="sqlalchemytrunktestinheritancepy"></a> <div class="modfile"><h4>Modified: sqlalchemy/trunk/test/inheritance.py (1111 => 1112)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/trunk/test/inheritance.py 2006-03-08 01:18:26 UTC (rev 1111) +++ sqlalchemy/trunk/test/inheritance.py 2006-03-08 20:51:51 UTC (rev 1112) </span><span class="lines">@@ -2,9 +2,8 @@ </span><span class="cx"> import testbase </span><span class="cx"> import string </span><span class="cx"> import sqlalchemy.attributes as attr </span><ins>+import sys </ins><span class="cx"> </span><del>- - </del><span class="cx"> class Principal( object ): </span><span class="cx"> pass </span><span class="cx"> </span><span class="lines">@@ -54,8 +53,6 @@ </span><span class="cx"> </span><span class="cx"> ) </span><span class="cx"> </span><del>- - </del><span class="cx"> principals.create() </span><span class="cx"> users.create() </span><span class="cx"> groups.create() </span><span class="lines">@@ -65,6 +62,7 @@ </span><span class="cx"> groups.drop() </span><span class="cx"> users.drop() </span><span class="cx"> principals.drop() </span><ins>+ testbase.db.tables.clear() </ins><span class="cx"> def setUp(self): </span><span class="cx"> objectstore.clear() </span><span class="cx"> clear_mappers() </span><span class="lines">@@ -111,6 +109,7 @@ </span><span class="cx"> foo_bar.drop() </span><span class="cx"> bar.drop() </span><span class="cx"> foo.drop() </span><ins>+ testbase.db.tables.clear() </ins><span class="cx"> </span><span class="cx"> def testbasic(self): </span><span class="cx"> class Foo(object): </span><span class="lines">@@ -155,6 +154,112 @@ </span><span class="cx"> {'id':b.id, 'data':'barfoo', 'foos':(Foo, [{'id':f1.id,'data':'subfoo1'}, {'id':f2.id,'data':'subfoo2'}])}, </span><span class="cx"> ) </span><span class="cx"> </span><ins>+class InheritTest3(testbase.AssertMixin): + def setUpAll(self): + engine = testbase.db + global foo, bar, blub, bar_foo, blub_bar, blub_foo,tables + engine.engine.echo = 'debug' + # the 'data' columns are to appease SQLite which cant handle a blank INSERT + foo = Table('foo', engine, + Column('id', Integer, Sequence('foo_seq'), primary_key=True), + Column('data', String(20))) </ins><span class="cx"> </span><ins>+ bar = Table('bar', engine, + Column('id', Integer, ForeignKey('foo.id'), primary_key=True), + Column('data', String(20))) + + blub = Table('blub', engine, + Column('id', Integer, ForeignKey('bar.id'), primary_key=True), + Column('data', String(20))) + + bar_foo = Table('bar_foo', engine, + Column('bar_id', Integer, ForeignKey('bar.id')), + Column('foo_id', Integer, ForeignKey('foo.id'))) + + blub_bar = Table('bar_blub', engine, + Column('blub_id', Integer, ForeignKey('blub.id')), + Column('bar_id', Integer, ForeignKey('bar.id'))) + + blub_foo = Table('blub_foo', engine, + Column('blub_id', Integer, ForeignKey('blub.id')), + Column('foo_id', Integer, ForeignKey('foo.id'))) + + tables = [foo, bar, blub, bar_foo, blub_bar, blub_foo] + for table in tables: + table.create() + def tearDownAll(self): + for table in reversed(tables): + table.drop() + testbase.db.tables.clear() + + def testbasic(self): + class Foo(object): + def __repr__(self): + return "Foo id %d, data %s" % (self.id, self.data) + Foo.mapper = mapper(Foo, foo) + + class Bar(object): + def __repr__(self): + return "Bar id %d, data %s" % (self.id, self.data) + + Bar.mapper = mapper(Bar, bar, inherits=Foo.mapper, properties={ + 'foos' :relation(Foo.mapper, bar_foo, primaryjoin=bar.c.id==bar_foo.c.bar_id, secondaryjoin=bar_foo.c.foo_id==foo.c.id, lazy=False) + }) + + Bar.mapper.select() + + def testadvanced(self): + class Foo(object): + def __init__(self, data=None): + self.data = data + def __repr__(self): + return "Foo id %d, data %s" % (self.id, self.data) + Foo.mapper = mapper(Foo, foo) + + class Bar(Foo): + def __repr__(self): + return "Bar id %d, data %s" % (self.id, self.data) + Bar.mapper = mapper(Bar, bar, inherits=Foo.mapper) + + class Blub(Bar): + def __repr__(self): + return "Blub id %d, data %s, bars %s, foos %s" % (self.id, self.data, repr([b for b in self.bars]), repr([f for f in self.foos])) + + Blub.mapper = mapper(Blub, blub, inherits=Bar.mapper, properties={ + 'bars':relation(Bar.mapper, blub_bar, primaryjoin=blub.c.id==blub_bar.c.blub_id, secondaryjoin=blub_bar.c.bar_id==bar.c.id, lazy=False), + 'foos':relation(Foo.mapper, blub_foo, primaryjoin=blub.c.id==blub_foo.c.blub_id, secondaryjoin=blub_foo.c.foo_id==foo.c.id, lazy=False), + }) + + useobjects = True + if (useobjects): + f1 = Foo("foo #1") + b1 = Bar("bar #1") + b2 = Bar("bar #2") + bl1 = Blub("blub #1") + bl1.foos.append(f1) + bl1.bars.append(b2) + objectstore.commit() + compare = repr(bl1) + blubid = bl1.id + objectstore.clear() + else: + foo.insert().execute(data='foo #1') + foo.insert().execute(data='foo #2') + bar.insert().execute(id=1, data="bar #1") + bar.insert().execute(id=2, data="bar #2") + blub.insert().execute(id=1, data="blub #1") + blub_bar.insert().execute(blub_id=1, bar_id=2) + blub_foo.insert().execute(blub_id=1, foo_id=2) + + l = Blub.mapper.select() + for x in l: + print x + + self.assert_(repr(l[0]) == compare) + objectstore.clear() + x = Blub.mapper.get_by(id=blubid) #traceback 2 + self.assert_(repr(x) == compare) + + </ins><span class="cx"> if __name__ == "__main__": </span><span class="cx"> testbase.main() </span></span></pre></div> <a id="sqlalchemytrunktestselectpy"></a> <div class="modfile"><h4>Modified: sqlalchemy/trunk/test/select.py (1111 => 1112)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/trunk/test/select.py 2006-03-08 01:18:26 UTC (rev 1111) +++ sqlalchemy/trunk/test/select.py 2006-03-08 20:51:51 UTC (rev 1112) </span><span class="lines">@@ -418,7 +418,7 @@ </span><span class="cx"> select([s, table1]) </span><span class="cx"> ,"SELECT sq2.myid, sq2.name, sq2.description, mytable.myid, mytable.name, mytable.description FROM (SELECT ta.myid AS myid, ta.name AS name, ta.description AS description FROM mytable AS ta WHERE EXISTS (SELECT 1 FROM myothertable WHERE myothertable.otherid = ta.myid)) AS sq2, mytable") </span><span class="cx"> </span><del>- s = select([addresses.c.street], addresses.c.user_id==users.c.user_id).alias('s') </del><ins>+ s = select([addresses.c.street], addresses.c.user_id==users.c.user_id, correlate=True).alias('s') </ins><span class="cx"> self.runtest( </span><span class="cx"> select([users, s.c.street], from_obj=[s]), </span><span class="cx"> """SELECT users.user_id, users.user_name, users.password, s.street FROM users, (SELECT addresses.street AS street FROM addresses WHERE addresses.user_id = users.user_id) AS s""") </span></span></pre> </div> </div> </body> </html> |