[Sqlalchemy-commits] [1349] sqlalchemy/branches/schema/test: an overhaul to mapper table attributes
Brought to you by:
zzzeek
From: <co...@sq...> - 2006-04-28 22:11: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>[1349] sqlalchemy/branches/schema/test: an overhaul to mapper table attributes to allow more flexible breaking up of mapped_table vs.</title> </head> <body> <div id="msg"> <dl> <dt>Revision</dt> <dd>1349</dd> <dt>Author</dt> <dd>zzzeek</dd> <dt>Date</dt> <dd>2006-04-28 17:11:28 -0500 (Fri, 28 Apr 2006)</dd> </dl> <h3>Log Message</h3> <pre>an overhaul to mapper table attributes to allow more flexible breaking up of mapped_table vs. select_table; the real polymorphic approach seems to work now</pre> <h3>Modified Paths</h3> <ul> <li><a href="#sqlalchemybranchesschemaexamplespolymorphpolymorph2py">sqlalchemy/branches/schema/examples/polymorph/polymorph2.py</a></li> <li><a href="#sqlalchemybranchesschemalibsqlalchemyormmapperpy">sqlalchemy/branches/schema/lib/sqlalchemy/orm/mapper.py</a></li> <li><a href="#sqlalchemybranchesschemalibsqlalchemyormpropertiespy">sqlalchemy/branches/schema/lib/sqlalchemy/orm/properties.py</a></li> <li><a href="#sqlalchemybranchesschemalibsqlalchemyormquerypy">sqlalchemy/branches/schema/lib/sqlalchemy/orm/query.py</a></li> <li><a href="#sqlalchemybranchesschemalibsqlalchemyormsessionpy">sqlalchemy/branches/schema/lib/sqlalchemy/orm/session.py</a></li> <li><a href="#sqlalchemybranchesschematestinheritancepy">sqlalchemy/branches/schema/test/inheritance.py</a></li> <li><a href="#sqlalchemybranchesschematestmapperpy">sqlalchemy/branches/schema/test/mapper.py</a></li> <li><a href="#sqlalchemybranchesschematestselectpy">sqlalchemy/branches/schema/test/select.py</a></li> </ul> <h3>Removed Paths</h3> <ul> <li><a href="#sqlalchemybranchesschemaexamplespolymorphpolymorphpy">sqlalchemy/branches/schema/examples/polymorph/polymorph.py</a></li> </ul> </div> <div id="patch"> <h3>Diff</h3> <a id="sqlalchemybranchesschemaexamplespolymorphpolymorphpy"></a> <div class="delfile"><h4>Deleted: sqlalchemy/branches/schema/examples/polymorph/polymorph.py (1348 => 1349)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/schema/examples/polymorph/polymorph.py 2006-04-28 15:02:49 UTC (rev 1348) +++ sqlalchemy/branches/schema/examples/polymorph/polymorph.py 2006-04-28 22:11:28 UTC (rev 1349) </span><span class="lines">@@ -1,156 +0,0 @@ </span><del>-from sqlalchemy import * -import sys - -# this example illustrates how to create a relationship to a list of objects, -# where each object in the list has a different type. The typed objects will -# extend from a common base class, although this same approach can be used -# with - -db = create_engine('sqlite://', echo=True, echo_uow=False) -#db = create_engine('postgres://user=scott&password=tiger&host=127.0.0.1&database=test', echo=True, echo_uow=False) - -# a table to store companies -companies = Table('companies', db, - Column('company_id', Integer, primary_key=True), - Column('name', String(50))).create() - -# we will define an inheritance relationship between the table "people" and "engineers", -# and a second inheritance relationship between the table "people" and "managers" -people = Table('people', db, - Column('person_id', Integer, primary_key=True), - Column('company_id', Integer, ForeignKey('companies.company_id')), - Column('name', String(50))).create() - -engineers = Table('engineers', db, - Column('person_id', Integer, ForeignKey('people.person_id'), primary_key=True), - Column('special_description', String(50))).create() - -managers = Table('managers', db, - Column('person_id', Integer, ForeignKey('people.person_id'), primary_key=True), - Column('description', String(50))).create() - - -# create our classes. The Engineer and Manager classes extend from Person. -class Person(object): - def __init__(self, **kwargs): - for key, value in kwargs.iteritems(): - setattr(self, key, value) - def __repr__(self): - return "Ordinary person %s" % self.name -class Engineer(Person): - def __repr__(self): - return "Engineer %s, description %s" % (self.name, self.special_description) -class Manager(Person): - def __repr__(self): - return "Manager %s, description %s" % (self.name, self.description) -class Company(object): - def __init__(self, **kwargs): - for key, value in kwargs.iteritems(): - setattr(self, key, value) - def __repr__(self): - return "Company %s" % self.name - -# next we assign Person mappers. Since these are the first mappers we are -# creating for these classes, they automatically become the "primary mappers", which -# define the dependency relationships between the classes, so we do a straight -# inheritance setup, i.e. no modifications to how objects are loaded or anything like that. -mapper(Person, people) -mapper(Engineer, engineers, inherits=class_mapper(Person)) -mapper(Manager, managers, inherits=class_mapper(Person)) - -# next, we define a query that is going to load Managers and Engineers in one shot. -# we will use a UNION ALL with an extra hardcoded column to indicate the type of object. -# this can also be done via several LEFT OUTER JOINS but a UNION is more appropriate -# since they are distinct result sets. -# The select() statement is also given an alias 'pjoin', since the mapper requires -# that all Selectables have a name. -# -# TECHNIQUE - when you want to load a certain set of objects from a in one query, all the -# columns in the Selectable must have unique names. Dont worry about mappers at this point, -# just worry about making a query where if you were to view the results, you could tell everything -# you need to know from each row how to construct an object instance from it. this is the -# essence of "resultset-based-mapping", which is the core ideology of SQLAlchemy. -# -person_join = select( - [people, managers.c.description,column("'manager'").label('type')], - people.c.person_id==managers.c.person_id).union_all( - select( - [people, engineers.c.special_description.label('description'), column("'engineer'").label('type')], - people.c.person_id==engineers.c.person_id)).alias('pjoin') - - -# lets print out what this Selectable looks like. The mapper is going to take the selectable and -# Select off of it, with the flag "use_labels" which indicates to prefix column names with the table -# name. So here is what our mapper will see: -print "Person selectable:", str(person_join.select(use_labels=True)), "\n" - - -# MapperExtension object. -class PersonLoader(MapperExtension): - def create_instance(self, session, mapper, row, imap, class_): - if row['pjoin_type'] =='engineer': - e = Engineer() - e.special_description = row['pjoin_description'] - return e - elif row['pjoin_type'] =='manager': - return Manager() - else: - return Person() -ext = PersonLoader() - -# set up the polymorphic mapper, which maps the person_join we set up to -# the Person class, using an instance of PersonLoader. -people_mapper = mapper(Person, person_join, extension=ext, non_primary=True) - -# TODO: polymorphic options, i.e.: -# people_mapper = mapper(Person, person_join, polymorphic_on=person_join.c.type, polymorphic_map={'engineer':Engineer, 'manager':Manager}) - -# create a mapper for Company. the 'employees' relationship points to -# our new people_mapper. -# -# the dependency relationships which take effect on commit (i.e. the order of -# inserts/deletes) will be established against the Person class's primary -# mapper, and when the Engineer and -# Manager objects are found in the 'employees' list, the primary mappers -# for those subclasses will register -# themselves as dependent on the Person mapper's save operations. -# (translation: it'll work) -# TODO: get the eager loading to work (the compound select alias doesnt like being aliased itself) -mapper(Company, companies, properties={ - 'employees': relation(people_mapper, lazy=False, cascade="save-update,delete,delete-orphan", backref=backref("company")) -}) - -session = create_session() -c = Company(name='company1') -c.employees.append(Manager(name='pointy haired boss', description='manager1')) -c.employees.append(Engineer(name='dilbert', special_description='engineer1')) -c.employees.append(Engineer(name='wally', special_description='engineer2')) -c.employees.append(Manager(name='jsmith', description='manager2')) -session.save(c) -session.flush() - -session.clear() - -c = session.query(Company).get(1) -for e in c.employees: - print e, e._instance_key - -print "\n" - -dilbert = session.query(Engineer).get_by(name='dilbert') -dilbert.special_description = 'hes dibert!' -session.flush() - -session.clear() -c = session.query(Company).get(1) -for e in c.employees: - print e, e._instance_key, e.company - -session.delete(c) -session.flush() - - -managers.drop() -engineers.drop() -people.drop() -companies.drop() </del></span></pre></div> <a id="sqlalchemybranchesschemaexamplespolymorphpolymorph2py"></a> <div class="modfile"><h4>Modified: sqlalchemy/branches/schema/examples/polymorph/polymorph2.py (1348 => 1349)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/schema/examples/polymorph/polymorph2.py 2006-04-28 15:02:49 UTC (rev 1348) +++ sqlalchemy/branches/schema/examples/polymorph/polymorph2.py 2006-04-28 22:11:28 UTC (rev 1349) </span><span class="lines">@@ -77,16 +77,12 @@ </span><span class="cx"> people.c.person_id==engineers.c.person_id)).alias('pjoin') </span><span class="cx"> </span><span class="cx"> </span><del>-mapper(Person, people) -mapper(Engineer, engineers, inherits=Person) -mapper(Manager, managers, inherits=Person) - -people_mapper = mapper(Person, person_join, polymorphic_on=person_join.c.type, polymorphic_map={'engineer':Engineer, 'manager':Manager}, non_primary=True) </del><ins>+person_mapper = mapper(Person, people, select_table=person_join, polymorphic_on=person_join.c.type) +mapper(Engineer, engineers, inherits=person_mapper, polymorphic_ident='engineer') +mapper(Manager, managers, inherits=person_mapper, polymorphic_ident='manager') </ins><span class="cx"> </span><del>- - </del><span class="cx"> mapper(Company, companies, properties={ </span><del>- 'employees': relation(people_mapper, lazy=False, private=True, backref='company') </del><ins>+ 'employees': relation(Person, lazy=True, private=True, backref='company') </ins><span class="cx"> }) </span><span class="cx"> </span><span class="cx"> session = create_session() </span><span class="lines">@@ -106,10 +102,8 @@ </span><span class="cx"> </span><span class="cx"> print "\n" </span><span class="cx"> </span><del>-dilbert = session.query(people_mapper).get_by(name='dilbert') -print "DILBERT1", dilbert </del><ins>+dilbert = session.query(Person).get_by(name='dilbert') </ins><span class="cx"> dilbert2 = session.query(Engineer).get_by(name='dilbert') </span><del>-print "DILBERT2", dilbert2 </del><span class="cx"> assert dilbert is dilbert2 </span><span class="cx"> </span><span class="cx"> dilbert.engineer_name = 'hes dibert!' </span></span></pre></div> <a id="sqlalchemybranchesschemalibsqlalchemyormmapperpy"></a> <div class="modfile"><h4>Modified: sqlalchemy/branches/schema/lib/sqlalchemy/orm/mapper.py (1348 => 1349)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/schema/lib/sqlalchemy/orm/mapper.py 2006-04-28 15:02:49 UTC (rev 1348) +++ sqlalchemy/branches/schema/lib/sqlalchemy/orm/mapper.py 2006-04-28 22:11:28 UTC (rev 1349) </span><span class="lines">@@ -38,7 +38,7 @@ </span><span class="cx"> relation() function.""" </span><span class="cx"> def __init__(self, </span><span class="cx"> class_, </span><del>- table, </del><ins>+ local_table, </ins><span class="cx"> properties = None, </span><span class="cx"> primary_key = None, </span><span class="cx"> is_primary = False, </span><span class="lines">@@ -54,7 +54,7 @@ </span><span class="cx"> polymorphic_on=None, </span><span class="cx"> polymorphic_map=None, </span><span class="cx"> polymorphic_ident=None, </span><del>- selectfrom=None): </del><ins>+ select_table=None): </ins><span class="cx"> </span><span class="cx"> ext = MapperExtension() </span><span class="cx"> </span><span class="lines">@@ -78,77 +78,89 @@ </span><span class="cx"> self.version_id_col = version_id_col </span><span class="cx"> self._inheriting_mappers = sets.Set() </span><span class="cx"> self.polymorphic_on = polymorphic_on </span><del>- self.polymorphic_map = polymorphic_map or {} - </del><ins>+ if polymorphic_map is None: + self.polymorphic_map = {} + else: + self.polymorphic_map = polymorphic_map + </ins><span class="cx"> if not issubclass(class_, object): </span><span class="cx"> raise ArgumentError("Class '%s' is not a new-style class" % class_.__name__) </span><del>- - if isinstance(table, sql.Select): - # some db's, noteably postgres, dont want to select from a select - # without an alias. also if we make our own alias internally, then - # the configured properties on the mapper are not matched against the alias - # we make, theres workarounds but it starts to get really crazy (its crazy enough - # the SQL that gets generated) so just require an alias - raise ArgumentError("Mapping against a Select object requires that it has a name. Use an alias to give it a name, i.e. s = select(...).alias('myselect')") - else: - self.table = table </del><span class="cx"> </span><del>- if selectfrom is None: - self.selectfrom = self.table - else: - self.selectfrom = selectfrom - </del><ins>+ # set up various Selectable units: + + # mapped_table - the Selectable that represents a join of the underlying Tables to be saved (or just the Table) + # local_table - the Selectable that was passed to this Mapper's constructor, if any + # select_table - the Selectable that will be used during queries. if this is specified + # as a constructor keyword argument, it takes precendence over mapped_table, otherwise its mapped_table + # unjoined_table - our Selectable, minus any joins constructed against the inherits table. + # this is either select_table if it was given explicitly, or in the case of a mapper that inherits + # its local_table + # tables - a collection of underlying Table objects pulled from mapped_table + + for table in (local_table, select_table): + if table is not None and isinstance(local_table, sql.Select): + # some db's, noteably postgres, dont want to select from a select + # without an alias. also if we make our own alias internally, then + # the configured properties on the mapper are not matched against the alias + # we make, theres workarounds but it starts to get really crazy (its crazy enough + # the SQL that gets generated) so just require an alias + raise ArgumentError("Mapping against a Select object requires that it has a name. Use an alias to give it a name, i.e. s = select(...).alias('myselect')") + + self.local_table = local_table + </ins><span class="cx"> if inherits is not None: </span><span class="cx"> if isinstance(inherits, type): </span><span class="cx"> inherits = class_mapper(inherits) </span><span class="cx"> if self.class_.__mro__[1] != inherits.class_: </span><span class="cx"> raise ArgumentError("Class '%s' does not inherit from '%s'" % (self.class_.__name__, inherits.class_.__name__)) </span><del>- self.primarytable = inherits.primarytable </del><span class="cx"> # inherit_condition is optional. </span><del>- if not table is inherits.noninherited_table: </del><ins>+ if not local_table is inherits.local_table: </ins><span class="cx"> if inherit_condition is None: </span><span class="cx"> # figure out inherit condition from our table to the immediate table </span><span class="cx"> # of the inherited mapper, not its full table which could pull in other </span><span class="cx"> # stuff we dont want (allows test/inheritance.InheritTest4 to pass) </span><del>- inherit_condition = sql.join(inherits.noninherited_table, table).onclause - self.table = sql.join(inherits.table, table, inherit_condition) </del><ins>+ inherit_condition = sql.join(inherits.local_table, self.local_table).onclause + self.mapped_table = sql.join(inherits.mapped_table, self.local_table, inherit_condition) </ins><span class="cx"> #print "inherit condition", str(self.table.onclause) </span><span class="cx"> </span><span class="cx"> # generate sync rules. similarly to creating the on clause, specify a </span><span class="cx"> # stricter set of tables to create "sync rules" by,based on the immediate </span><span class="cx"> # inherited table, rather than all inherited tables </span><span class="cx"> self._synchronizer = sync.ClauseSynchronizer(self, self, sync.ONETOMANY) </span><del>- self._synchronizer.compile(self.table.onclause, util.HashSet([inherits.noninherited_table]), sqlutil.TableFinder(table)) - # the old rule - #self._synchronizer.compile(self.table.onclause, inherits.tables, TableFinder(table)) </del><ins>+ self._synchronizer.compile(self.mapped_table.onclause, util.HashSet([inherits.local_table]), sqlutil.TableFinder(self.local_table)) </ins><span class="cx"> else: </span><span class="cx"> self._synchronizer = None </span><span class="cx"> self.inherits = inherits </span><del>- self.noninherited_table = table </del><span class="cx"> if polymorphic_ident is not None: </span><span class="cx"> inherits.add_polymorphic_mapping(polymorphic_ident, self) </span><span class="cx"> else: </span><del>- self.primarytable = self.table - self.noninherited_table = self.table </del><span class="cx"> self._synchronizer = None </span><span class="cx"> self.inherits = None </span><ins>+ self.mapped_table = self.local_table </ins><span class="cx"> if polymorphic_ident is not None: </span><span class="cx"> raise ArgumentError("'polymorphic_ident' argument can only be used with inherits=<somemapper>") </span><ins>+ + if select_table is not None: + self.select_table = select_table + self.unjoined_table = self.select_table + else: + self.select_table = self.mapped_table + self.unjoined_table = self.local_table </ins><span class="cx"> </span><span class="cx"> # locate all tables contained within the "table" passed in, which </span><span class="cx"> # may be a join or other construct </span><del>- self.tables = sqlutil.TableFinder(self.table) </del><ins>+ self.tables = sqlutil.TableFinder(self.mapped_table) </ins><span class="cx"> </span><span class="cx"> # determine primary key columns, either passed in, or get them from our set of tables </span><span class="cx"> self.pks_by_table = {} </span><span class="cx"> if primary_key is not None: </span><span class="cx"> for k in primary_key: </span><span class="cx"> self.pks_by_table.setdefault(k.table, util.HashSet(ordered=True)).append(k) </span><del>- if k.table != self.table: </del><ins>+ if k.table != self.mapped_table: </ins><span class="cx"> # associate pk cols from subtables to the "main" table </span><del>- self.pks_by_table.setdefault(self.table, util.HashSet(ordered=True)).append(k) </del><ins>+ self.pks_by_table.setdefault(self.mapped_table, util.HashSet(ordered=True)).append(k) </ins><span class="cx"> else: </span><del>- for t in self.tables + [self.table]: </del><ins>+ for t in self.tables + [self.mapped_table, self.select_table]: </ins><span class="cx"> try: </span><span class="cx"> l = self.pks_by_table[t] </span><span class="cx"> except KeyError: </span><span class="lines">@@ -177,7 +189,7 @@ </span><span class="cx"> </span><span class="cx"> # load properties from the main table object, </span><span class="cx"> # not overriding those set up in the 'properties' argument </span><del>- for column in self.table.columns: </del><ins>+ for column in self.select_table.columns: </ins><span class="cx"> if not self.columns.has_key(column.key): </span><span class="cx"> self.columns[column.key] = column </span><span class="cx"> </span><span class="lines">@@ -206,7 +218,7 @@ </span><span class="cx"> proplist = self.columntoproperty.setdefault(column.original, []) </span><span class="cx"> proplist.append(prop) </span><span class="cx"> </span><del>- if not mapper_registry.has_key(self.class_key) or self.is_primary or (inherits is not None and inherits._is_primary_mapper()): </del><ins>+ if not non_primary and (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"> sessionlib.global_attributes.reset_class_managed(self.class_) </span><span class="cx"> self._init_class() </span><span class="cx"> elif not non_primary: </span><span class="lines">@@ -215,7 +227,7 @@ </span><span class="cx"> for key in self.polymorphic_map.keys(): </span><span class="cx"> if isinstance(self.polymorphic_map[key], type): </span><span class="cx"> self.polymorphic_map[key] = class_mapper(self.polymorphic_map[key]) </span><del>- </del><ins>+ </ins><span class="cx"> if inherits is not None: </span><span class="cx"> inherits._inheriting_mappers.add(self) </span><span class="cx"> for key, prop in inherits.props.iteritems(): </span><span class="lines">@@ -228,6 +240,7 @@ </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>+ </ins><span class="cx"> # this prints a summary of the object attributes and how they </span><span class="cx"> # will be mapped to table columns </span><span class="cx"> #print "mapper %s, columntoproperty:" % (self.class_.__name__) </span><span class="lines">@@ -323,14 +336,14 @@ </span><span class="cx"> </span><span class="cx"> if sql.is_column(prop): </span><span class="cx"> try: </span><del>- prop = self.table._get_col_by_original(prop) </del><ins>+ prop = self.select_table._get_col_by_original(prop) </ins><span class="cx"> except KeyError: </span><span class="cx"> raise ArgumentError("Column '%s' is not represented in mapper's table" % prop._label) </span><span class="cx"> self.columns[key] = prop </span><span class="cx"> prop = ColumnProperty(prop) </span><span class="cx"> elif isinstance(prop, list) and sql.is_column(prop[0]): </span><span class="cx"> try: </span><del>- prop = [self.table._get_col_by_original(p) for p in prop] </del><ins>+ prop = [self.select_table._get_col_by_original(p) for p in prop] </ins><span class="cx"> except KeyError, e: </span><span class="cx"> raise ArgumentError("Column '%s' is not represented in mapper's table" % e.args[0]) </span><span class="cx"> self.columns[key] = prop[0] </span><span class="lines">@@ -350,7 +363,7 @@ </span><span class="cx"> mapper.add_property(key, p, init=False) </span><span class="cx"> </span><span class="cx"> def __str__(self): </span><del>- return "Mapper|" + self.class_.__name__ + "|" + (self.entity_name is not None and "/%s" % self.entity_name or "") + self.primarytable.name </del><ins>+ return "Mapper|" + self.class_.__name__ + "|" + (self.entity_name is not None and "/%s" % self.entity_name or "") + self.select_table.name </ins><span class="cx"> </span><span class="cx"> def _is_primary_mapper(self): </span><span class="cx"> """returns True if this mapper is the primary mapper for its class key (class + entity_name)""" </span><span class="lines">@@ -456,7 +469,7 @@ </span><span class="cx"> </span><span class="cx"> def identity(self, instance): </span><span class="cx"> """returns the identity (list of primary key values) for the given instance. The list of values can be fed directly into the get() method as mapper.get(*key).""" </span><del>- return [self._getattrbycolumn(instance, column) for column in self.pks_by_table[self.table]] </del><ins>+ return [self._getattrbycolumn(instance, column) for column in self.pks_by_table[self.select_table]] </ins><span class="cx"> </span><span class="cx"> def compile(self, whereclause = None, **options): </span><span class="cx"> """works like select, except returns the SQL statement object without </span><span class="lines">@@ -489,6 +502,8 @@ </span><span class="cx"> if (key.startswith('select_by_') or key.startswith('get_by_')): </span><span class="cx"> return getattr(self.query(), key) </span><span class="cx"> else: </span><ins>+ if key == 'table': + raise "what!? " + key #AttributeError(key) </ins><span class="cx"> raise AttributeError(key) </span><span class="cx"> </span><span class="cx"> def _getpropbycolumn(self, column, raiseerror=True): </span><span class="lines">@@ -735,7 +750,7 @@ </span><span class="cx"> yield c </span><span class="cx"> </span><span class="cx"> def _identity_key(self, row): </span><del>- return sessionlib.get_row_key(row, self.class_, self.pks_by_table[self.table], self.entity_name) </del><ins>+ return sessionlib.get_row_key(row, self.class_, self.pks_by_table[self.select_table], self.entity_name) </ins><span class="cx"> </span><span class="cx"> def _instance(self, session, 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">@@ -745,6 +760,7 @@ </span><span class="cx"> </span><span class="cx"> if self.polymorphic_on is not None: </span><span class="cx"> discriminator = row[self.polymorphic_on] </span><ins>+ print self.polymorphic_map </ins><span class="cx"> mapper = self.polymorphic_map[discriminator] </span><span class="cx"> if mapper is not self: </span><span class="cx"> row = self.translate_row(mapper, row) </span><span class="lines">@@ -776,7 +792,7 @@ </span><span class="cx"> if not exists: </span><span class="cx"> # check if primary key cols in the result are None - this indicates </span><span class="cx"> # an instance of the object is not present in the row </span><del>- for col in self.pks_by_table[self.table]: </del><ins>+ for col in self.pks_by_table[self.select_table]: </ins><span class="cx"> if row[col] is None: </span><span class="cx"> return None </span><span class="cx"> # plugin point </span><span class="lines">@@ -820,9 +836,9 @@ </span><span class="cx"> bare keynames to accomplish this. So far this works for the various polymorphic </span><span class="cx"> examples.""" </span><span class="cx"> newrow = util.DictDecorator(row) </span><del>- for c in self.table.c: </del><ins>+ for c in self.select_table.c: </ins><span class="cx"> newrow[c.name] = row[c] </span><del>- for c in tomapper.table.c: </del><ins>+ for c in tomapper.select_table.c: </ins><span class="cx"> newrow[c] = newrow[c.name] </span><span class="cx"> return newrow </span><span class="cx"> </span></span></pre></div> <a id="sqlalchemybranchesschemalibsqlalchemyormpropertiespy"></a> <div class="modfile"><h4>Modified: sqlalchemy/branches/schema/lib/sqlalchemy/orm/properties.py (1348 => 1349)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/schema/lib/sqlalchemy/orm/properties.py 2006-04-28 15:02:49 UTC (rev 1348) +++ sqlalchemy/branches/schema/lib/sqlalchemy/orm/properties.py 2006-04-28 22:11:28 UTC (rev 1349) </span><span class="lines">@@ -195,7 +195,7 @@ </span><span class="cx"> if isinstance(self.association, type): </span><span class="cx"> self.association = sqlalchemy.orm.class_mapper(self.association) </span><span class="cx"> </span><del>- self.target = self.mapper.table </del><ins>+ self.target = self.mapper.select_table </ins><span class="cx"> self.key = key </span><span class="cx"> self.parent = parent </span><span class="cx"> </span><span class="lines">@@ -204,12 +204,13 @@ </span><span class="cx"> # if join conditions were not specified, figure them out based on foreign keys </span><span class="cx"> if self.secondary is not None: </span><span class="cx"> if self.secondaryjoin is None: </span><del>- self.secondaryjoin = sql.join(self.mapper.noninherited_table, self.secondary).onclause </del><ins>+ self.secondaryjoin = sql.join(self.mapper.unjoined_table, self.secondary).onclause </ins><span class="cx"> if self.primaryjoin is None: </span><del>- self.primaryjoin = sql.join(parent.noninherited_table, self.secondary).onclause </del><ins>+ self.primaryjoin = sql.join(parent.unjoined_table, self.secondary).onclause </ins><span class="cx"> else: </span><span class="cx"> if self.primaryjoin is None: </span><del>- self.primaryjoin = sql.join(parent.noninherited_table, self.target).onclause </del><ins>+ self.primaryjoin = sql.join(parent.unjoined_table, self.target).onclause + print "PJ", self.primaryjoin </ins><span class="cx"> # if the foreign key wasnt specified and theres no assocaition table, try to figure </span><span class="cx"> # out who is dependent on who. we dont need all the foreign keys represented in the join, </span><span class="cx"> # just one of them. </span><span class="lines">@@ -255,18 +256,18 @@ </span><span class="cx"> </span><span class="cx"> def _get_direction(self): </span><span class="cx"> """determines our 'direction', i.e. do we represent one to many, many to many, etc.""" </span><del>- #print self.key, repr(self.parent.table.name), repr(self.parent.primarytable.name), repr(self.foreignkey.table.name), repr(self.target), repr(self.foreigntable.name) </del><ins>+ #print self.key, repr(self.parent.mapped_table.name), repr(self.parent.primarytable.name), repr(self.foreignkey.table.name), repr(self.target), repr(self.foreigntable.name) </ins><span class="cx"> </span><span class="cx"> if self.secondaryjoin is not None: </span><span class="cx"> return sync.MANYTOMANY </span><del>- elif self.parent.table is self.target: </del><ins>+ elif self.parent.mapped_table is self.target: </ins><span class="cx"> if self.foreignkey.primary_key: </span><span class="cx"> return sync.MANYTOONE </span><span class="cx"> else: </span><span class="cx"> return sync.ONETOMANY </span><del>- elif self.foreigntable == self.mapper.noninherited_table: </del><ins>+ elif self.foreigntable == self.mapper.unjoined_table: </ins><span class="cx"> return sync.ONETOMANY </span><del>- elif self.foreigntable == self.parent.noninherited_table: </del><ins>+ elif self.foreigntable == self.parent.unjoined_table: </ins><span class="cx"> return sync.MANYTOONE </span><span class="cx"> else: </span><span class="cx"> raise ArgumentError("Cant determine relation direction") </span><span class="lines">@@ -316,11 +317,11 @@ </span><span class="cx"> else: </span><span class="cx"> c = (self.mapper.props[key].columns[0]==value) & self.primaryjoin </span><span class="cx"> return c.copy_container() </span><del>- elif self.mapper.table.c.has_key(key): </del><ins>+ elif self.mapper.mapped_table.c.has_key(key): </ins><span class="cx"> if self.secondaryjoin is not None: </span><del>- c = (self.mapper.table.c[key].columns[0]==value) & self.primaryjoin & self.secondaryjoin </del><ins>+ c = (self.mapper.mapped_table.c[key].columns[0]==value) & self.primaryjoin & self.secondaryjoin </ins><span class="cx"> else: </span><del>- c = (self.mapper.table.c[key].columns[0]==value) & self.primaryjoin </del><ins>+ c = (self.mapper.mapped_table.c[key].columns[0]==value) & self.primaryjoin </ins><span class="cx"> return c.copy_container() </span><span class="cx"> elif self.association is not None: </span><span class="cx"> c = query._get_criterion(self.mapper, key, value) & self.primaryjoin </span><span class="lines">@@ -343,8 +344,8 @@ </span><span class="cx"> </span><span class="cx"> The list of rules is used within commits by the _synchronize() method when dependent </span><span class="cx"> objects are processed.""" </span><del>- parent_tables = util.HashSet(self.parent.tables + [self.parent.primarytable]) - target_tables = util.HashSet(self.mapper.tables + [self.mapper.primarytable]) </del><ins>+ parent_tables = util.HashSet(self.parent.tables + [self.parent.mapped_table, self.parent.select_table]) + target_tables = util.HashSet(self.mapper.tables + [self.mapper.mapped_table, self.mapper.select_table]) </ins><span class="cx"> </span><span class="cx"> self.syncrules = sync.ClauseSynchronizer(self.parent, self.mapper, self.direction) </span><span class="cx"> if self.direction == sync.MANYTOMANY: </span><span class="lines">@@ -356,7 +357,7 @@ </span><span class="cx"> </span><span class="cx"> class LazyLoader(PropertyLoader): </span><span class="cx"> def do_init_subclass(self, key, parent): </span><del>- (self.lazywhere, self.lazybinds, self.lazyreverse) = create_lazy_clause(self.parent.noninherited_table, self.primaryjoin, self.secondaryjoin, self.foreignkey) </del><ins>+ (self.lazywhere, self.lazybinds, self.lazyreverse) = create_lazy_clause(self.parent.unjoined_table, self.primaryjoin, self.secondaryjoin, self.foreignkey) </ins><span class="cx"> # determine if our "lazywhere" clause is the same as the mapper's </span><span class="cx"> # get() clause. then we can just use mapper.get() </span><span class="cx"> self.use_get = not self.uselist and self.mapper.query()._get_clause.compare(self.lazywhere) </span><span class="lines">@@ -387,7 +388,7 @@ </span><span class="cx"> # to possibly save a DB round trip </span><span class="cx"> if self.use_get: </span><span class="cx"> ident = [] </span><del>- for primary_key in self.mapper.pks_by_table[self.mapper.table]: </del><ins>+ for primary_key in self.mapper.pks_by_table[self.mapper.select_table]: </ins><span class="cx"> bind = self.lazyreverse[primary_key] </span><span class="cx"> ident.append(params[bind.key]) </span><span class="cx"> return self.mapper.using(session).get(*ident) </span><span class="lines">@@ -500,7 +501,7 @@ </span><span class="cx"> if isinstance(prop, EagerLoader): </span><span class="cx"> eagerprops.append(prop) </span><span class="cx"> if len(eagerprops): </span><del>- recursion_stack[self.parent.table] = True </del><ins>+ recursion_stack[self.parent.mapped_table] = True </ins><span class="cx"> self.mapper = self.mapper.copy() </span><span class="cx"> try: </span><span class="cx"> for prop in eagerprops: </span><span class="lines">@@ -510,16 +511,16 @@ </span><span class="cx"> continue </span><span class="cx"> p = prop.copy() </span><span class="cx"> self.mapper.props[prop.key] = p </span><del>-# print "we are:", id(self), self.target.name, (self.secondary and self.secondary.name or "None"), self.parent.table.name -# print "prop is",id(prop), prop.target.name, (prop.secondary and prop.secondary.name or "None"), prop.parent.table.name </del><ins>+# print "we are:", id(self), self.target.name, (self.secondary and self.secondary.name or "None"), self.parent.mapped_table.name +# print "prop is",id(prop), prop.target.name, (prop.secondary and prop.secondary.name or "None"), prop.parent.mapped_table.name </ins><span class="cx"> p.do_init_subclass(prop.key, prop.parent, recursion_stack) </span><span class="cx"> p._create_eager_chain(in_chain=True, recursion_stack=recursion_stack) </span><span class="cx"> p.eagerprimary = p.eagerprimary.copy_container() </span><del>-# aliasizer = Aliasizer(p.parent.table, aliases={p.parent.table:self.eagertarget}) </del><ins>+# aliasizer = Aliasizer(p.parent.mapped_table, aliases={p.parent.mapped_table:self.eagertarget}) </ins><span class="cx"> p.eagerprimary.accept_visitor(self.aliasizer) </span><del>- #print "new eagertqarget", p.eagertarget.name, (p.secondary and p.secondary.name or "none"), p.parent.table.name </del><ins>+ #print "new eagertqarget", p.eagertarget.name, (p.secondary and p.secondary.name or "none"), p.parent.mapped_table.name </ins><span class="cx"> finally: </span><del>- del recursion_stack[self.parent.table] </del><ins>+ del recursion_stack[self.parent.mapped_table] </ins><span class="cx"> </span><span class="cx"> self._row_decorator = self._create_decorator_row() </span><span class="cx"> </span><span class="lines">@@ -546,7 +547,7 @@ </span><span class="cx"> if hasattr(statement, '_outerjoin'): </span><span class="cx"> towrap = statement._outerjoin </span><span class="cx"> else: </span><del>- towrap = self.parent.table </del><ins>+ towrap = self.parent.mapped_table </ins><span class="cx"> </span><span class="cx"> # print "hello, towrap", str(towrap) </span><span class="cx"> if self.secondaryjoin is not None: </span></span></pre></div> <a id="sqlalchemybranchesschemalibsqlalchemyormquerypy"></a> <div class="modfile"><h4>Modified: sqlalchemy/branches/schema/lib/sqlalchemy/orm/query.py (1348 => 1349)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/schema/lib/sqlalchemy/orm/query.py 2006-04-28 15:02:49 UTC (rev 1348) +++ sqlalchemy/branches/schema/lib/sqlalchemy/orm/query.py 2006-04-28 22:11:28 UTC (rev 1349) </span><span class="lines">@@ -31,7 +31,7 @@ </span><span class="cx"> return sessionlib.get_session() </span><span class="cx"> else: </span><span class="cx"> return self._session </span><del>- table = property(lambda s:s.mapper.selectfrom) </del><ins>+ table = property(lambda s:s.mapper.select_table) </ins><span class="cx"> props = property(lambda s:s.mapper.props) </span><span class="cx"> session = property(_get_session) </span><span class="cx"> </span><span class="lines">@@ -274,8 +274,8 @@ </span><span class="cx"> list of relations.""" </span><span class="cx"> if mapper.props.has_key(key): </span><span class="cx"> return mapper.props[key].columns[0] == value </span><del>- elif mapper.table.c.has_key(key): - return mapper.table.c[key] == value </del><ins>+ elif mapper.select_table.c.has_key(key): + return mapper.select_table.c[key] == value </ins><span class="cx"> else: </span><span class="cx"> for prop in mapper.props.values(): </span><span class="cx"> c = prop.get_criterion(self, key, value) </span></span></pre></div> <a id="sqlalchemybranchesschemalibsqlalchemyormsessionpy"></a> <div class="modfile"><h4>Modified: sqlalchemy/branches/schema/lib/sqlalchemy/orm/session.py (1348 => 1349)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/schema/lib/sqlalchemy/orm/session.py 2006-04-28 15:02:49 UTC (rev 1348) +++ sqlalchemy/branches/schema/lib/sqlalchemy/orm/session.py 2006-04-28 22:11:28 UTC (rev 1349) </span><span class="lines">@@ -147,12 +147,12 @@ </span><span class="cx"> return self.bind_to </span><span class="cx"> elif self.binds.has_key(mapper): </span><span class="cx"> return self.binds[mapper] </span><del>- elif self.binds.has_key(mapper.table): - return self.binds[mapper.table] </del><ins>+ elif self.binds.has_key(mapper.select_table): + return self.binds[mapper.select_table] </ins><span class="cx"> elif self.bind_to is not None: </span><span class="cx"> return self.bind_to </span><span class="cx"> else: </span><del>- return mapper.table.engine </del><ins>+ return mapper.select_table.engine </ins><span class="cx"> def query(self, mapper_or_class, entity_name=None): </span><span class="cx"> """given a mapper or Class, returns a new Query object corresponding to this Session and the mapper, or the classes' primary mapper.""" </span><span class="cx"> if isinstance(mapper_or_class, type): </span></span></pre></div> <a id="sqlalchemybranchesschematestinheritancepy"></a> <div class="modfile"><h4>Modified: sqlalchemy/branches/schema/test/inheritance.py (1348 => 1349)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/schema/test/inheritance.py 2006-04-28 15:02:49 UTC (rev 1348) +++ sqlalchemy/branches/schema/test/inheritance.py 2006-04-28 22:11:28 UTC (rev 1349) </span><span class="lines">@@ -1,5 +1,5 @@ </span><ins>+import testbase </ins><span class="cx"> from sqlalchemy import * </span><del>-import testbase </del><span class="cx"> import string </span><span class="cx"> import sqlalchemy.attributes as attr </span><span class="cx"> import sys </span><span class="lines">@@ -374,7 +374,7 @@ </span><span class="cx"> def testbasic(self): </span><span class="cx"> class ContentType(object): pass </span><span class="cx"> class Content(object): pass </span><del>- class Product(object): pass </del><ins>+ class Product(Content): pass </ins><span class="cx"> </span><span class="cx"> content_types = mapper(ContentType, content_type) </span><span class="cx"> contents = mapper(Content, content, properties={ </span><span class="lines">@@ -410,6 +410,7 @@ </span><span class="cx"> foos = mapper(Foo, foo) </span><span class="cx"> bars = mapper(Bar, bar, inherits=foos) </span><span class="cx"> bars.add_property('lazy', relation(foos, bar_foo, lazy=True)) </span><ins>+ print bars.props['lazy'].primaryjoin, bars.props['lazy'].secondaryjoin </ins><span class="cx"> bars.add_property('eager', relation(foos, bar_foo, lazy=False)) </span><span class="cx"> </span><span class="cx"> foo.insert().execute(data='foo1') </span></span></pre></div> <a id="sqlalchemybranchesschematestmapperpy"></a> <div class="modfile"><h4>Modified: sqlalchemy/branches/schema/test/mapper.py (1348 => 1349)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/schema/test/mapper.py 2006-04-28 15:02:49 UTC (rev 1348) +++ sqlalchemy/branches/schema/test/mapper.py 2006-04-28 22:11:28 UTC (rev 1349) </span><span class="lines">@@ -570,8 +570,8 @@ </span><span class="cx"> closedorders = alias(orders, 'closedorders') </span><span class="cx"> m = mapper(User, users, properties = dict( </span><span class="cx"> addresses = relation(mapper(Address, addresses), lazy = False), </span><del>- open_orders = relation(mapper(Order, openorders, non_primary=True), primaryjoin = and_(openorders.c.isopen == 1, users.c.user_id==openorders.c.user_id), lazy = True), - closed_orders = relation(mapper(Order, closedorders,non_primary=True), primaryjoin = and_(closedorders.c.isopen == 0, users.c.user_id==closedorders.c.user_id), lazy = True) </del><ins>+ open_orders = relation(mapper(Order, openorders, entity_name='open'), primaryjoin = and_(openorders.c.isopen == 1, users.c.user_id==openorders.c.user_id), lazy = True), + closed_orders = relation(mapper(Order, closedorders,entity_name='closed'), primaryjoin = and_(closedorders.c.isopen == 0, users.c.user_id==closedorders.c.user_id), lazy = True) </ins><span class="cx"> )) </span><span class="cx"> l = m.select() </span><span class="cx"> self.assert_result(l, User, </span></span></pre></div> <a id="sqlalchemybranchesschematestselectpy"></a> <div class="modfile"><h4>Modified: sqlalchemy/branches/schema/test/select.py (1348 => 1349)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/schema/test/select.py 2006-04-28 15:02:49 UTC (rev 1348) +++ sqlalchemy/branches/schema/test/select.py 2006-04-28 22:11:28 UTC (rev 1349) </span><span class="lines">@@ -460,7 +460,14 @@ </span><span class="cx"> FROM mytable UNION SELECT myothertable.otherid, myothertable.othername \ </span><span class="cx"> FROM myothertable UNION SELECT thirdtable.userid, thirdtable.otherstuff FROM thirdtable") </span><span class="cx"> </span><ins>+ u = union( + select([table1]), + select([table2]), + select([table3]) + ) + assert u._get_col_by_original(table2.c.otherid) is u.c.otherid </ins><span class="cx"> </span><ins>+ </ins><span class="cx"> def testouterjoin(self): </span><span class="cx"> # test an outer join. the oracle module should take the ON clause of the join and </span><span class="cx"> # move it up to the WHERE clause of its parent select, and append (+) to all right-hand-side columns </span></span></pre> </div> </div> </body> </html> |