[Sqlalchemy-commits] [1191] sqlalchemy/trunk/lib/sqlalchemy: some more tweaks to get more advanced p
Brought to you by:
zzzeek
From: <co...@sq...> - 2006-03-24 06:28:40
|
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head><style type="text/css"><!-- #msg dl { border: 1px #006 solid; background: #369; padding: 6px; color: #fff; } #msg dt { float: left; width: 6em; font-weight: bold; } #msg dt:after { content:':';} #msg dl, #msg dt, #msg ul, #msg li { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; } #msg dl a { font-weight: bold} #msg dl a:link { color:#fc3; } #msg dl a:active { color:#ff0; } #msg dl a:visited { color:#cc6; } h3 { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; font-weight: bold; } #msg pre { overflow: auto; background: #ffc; border: 1px #fc0 solid; padding: 6px; } #msg ul, pre { overflow: auto; } #patch { width: 100%; } #patch h4 {font-family: verdana,arial,helvetica,sans-serif;font-size:10pt;padding:8px;background:#369;color:#fff;margin:0;} #patch .propset h4, #patch .binary h4 {margin:0;} #patch pre {padding:0;line-height:1.2em;margin:0;} #patch .diff {width:100%;background:#eee;padding: 0 0 10px 0;overflow:auto;} #patch .propset .diff, #patch .binary .diff {padding:10px 0;} #patch span {display:block;padding:0 10px;} #patch .modfile, #patch .addfile, #patch .delfile, #patch .propset, #patch .binary, #patch .copfile {border:1px solid #ccc;margin:10px 0;} #patch ins {background:#dfd;text-decoration:none;display:block;padding:0 10px;} #patch del {background:#fdd;text-decoration:none;display:block;padding:0 10px;} #patch .lines, .info {color:#888;background:#fff;} --></style> <title>[1191] sqlalchemy/trunk/lib/sqlalchemy: some more tweaks to get more advanced polymorphic stuff to work</title> </head> <body> <div id="msg"> <dl> <dt>Revision</dt> <dd>1191</dd> <dt>Author</dt> <dd>zzzeek</dd> <dt>Date</dt> <dd>2006-03-24 00:28:27 -0600 (Fri, 24 Mar 2006)</dd> </dl> <h3>Log Message</h3> <pre>some more tweaks to get more advanced polymorphic stuff to work</pre> <h3>Modified Paths</h3> <ul> <li><a href="#sqlalchemytrunkexamplespolymorphpolymorphpy">sqlalchemy/trunk/examples/polymorph/polymorph.py</a></li> <li><a href="#sqlalchemytrunklibsqlalchemymappingmapperpy">sqlalchemy/trunk/lib/sqlalchemy/mapping/mapper.py</a></li> <li><a href="#sqlalchemytrunklibsqlalchemymappingpropertiespy">sqlalchemy/trunk/lib/sqlalchemy/mapping/properties.py</a></li> <li><a href="#sqlalchemytrunklibsqlalchemysqlpy">sqlalchemy/trunk/lib/sqlalchemy/sql.py</a></li> </ul> <h3>Added Paths</h3> <ul> <li><a href="#sqlalchemytrunkexamplespolymorphpolymorph2py">sqlalchemy/trunk/examples/polymorph/polymorph2.py</a></li> </ul> </div> <div id="patch"> <h3>Diff</h3> <a id="sqlalchemytrunkexamplespolymorphpolymorphpy"></a> <div class="modfile"><h4>Modified: sqlalchemy/trunk/examples/polymorph/polymorph.py (1190 => 1191)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/trunk/examples/polymorph/polymorph.py 2006-03-23 07:38:28 UTC (rev 1190) +++ sqlalchemy/trunk/examples/polymorph/polymorph.py 2006-03-24 06:28:27 UTC (rev 1191) </span><span class="lines">@@ -23,7 +23,7 @@ </span><span class="cx"> </span><span class="cx"> engineers = Table('engineers', db, </span><span class="cx"> Column('person_id', Integer, ForeignKey('people.person_id'), primary_key=True), </span><del>- Column('description', String(50))).create() </del><ins>+ Column('special_description', String(50))).create() </ins><span class="cx"> </span><span class="cx"> managers = Table('managers', db, </span><span class="cx"> Column('person_id', Integer, ForeignKey('people.person_id'), primary_key=True), </span><span class="lines">@@ -36,7 +36,7 @@ </span><span class="cx"> return "Ordinary person %s" % self.name </span><span class="cx"> class Engineer(Person): </span><span class="cx"> def __repr__(self): </span><del>- return "Engineer %s, description %s" % (self.name, self.description) </del><ins>+ return "Engineer %s, description %s" % (self.name, self.special_description) </ins><span class="cx"> class Manager(Person): </span><span class="cx"> def __repr__(self): </span><span class="cx"> return "Manager %s, description %s" % (self.name, self.description) </span><span class="lines">@@ -69,7 +69,7 @@ </span><span class="cx"> [people, managers.c.description,column("'manager'").label('type')], </span><span class="cx"> people.c.person_id==managers.c.person_id).union_all( </span><span class="cx"> select( </span><del>- [people, engineers.c.description, column("'engineer'").label('type')], </del><ins>+ [people, engineers.c.special_description.label('description'), column("'engineer'").label('type')], </ins><span class="cx"> people.c.person_id==engineers.c.person_id)).alias('pjoin') </span><span class="cx"> </span><span class="cx"> </span><span class="lines">@@ -83,7 +83,9 @@ </span><span class="cx"> class PersonLoader(MapperExtension): </span><span class="cx"> def create_instance(self, mapper, row, imap, class_): </span><span class="cx"> if row['pjoin_type'] =='engineer': </span><del>- return Engineer() </del><ins>+ e = Engineer() + e.special_description = row['pjoin_description'] + return e </ins><span class="cx"> elif row['pjoin_type'] =='manager': </span><span class="cx"> return Manager() </span><span class="cx"> else: </span><span class="lines">@@ -111,8 +113,8 @@ </span><span class="cx"> </span><span class="cx"> c = Company(name='company1') </span><span class="cx"> c.employees.append(Manager(name='pointy haired boss', description='manager1')) </span><del>-c.employees.append(Engineer(name='dilbert', description='engineer1')) -c.employees.append(Engineer(name='wally', description='engineer2')) </del><ins>+c.employees.append(Engineer(name='dilbert', special_description='engineer1')) +c.employees.append(Engineer(name='wally', special_description='engineer2')) </ins><span class="cx"> c.employees.append(Manager(name='jsmith', description='manager2')) </span><span class="cx"> objectstore.commit() </span><span class="cx"> </span><span class="lines">@@ -125,7 +127,7 @@ </span><span class="cx"> print "\n" </span><span class="cx"> </span><span class="cx"> dilbert = Engineer.mapper.get_by(name='dilbert') </span><del>-dilbert.description = 'hes dibert!' </del><ins>+dilbert.special_description = 'hes dibert!' </ins><span class="cx"> objectstore.commit() </span><span class="cx"> </span><span class="cx"> objectstore.clear() </span></span></pre></div> <a id="sqlalchemytrunkexamplespolymorphpolymorph2py"></a> <div class="addfile"><h4>Added: sqlalchemy/trunk/examples/polymorph/polymorph2.py (1190 => 1191)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/trunk/examples/polymorph/polymorph2.py 2006-03-23 07:38:28 UTC (rev 1190) +++ sqlalchemy/trunk/examples/polymorph/polymorph2.py 2006-03-24 06:28:27 UTC (rev 1191) </span><span class="lines">@@ -0,0 +1,137 @@ </span><ins>+from sqlalchemy import * +import sys + +# this example illustrates a polymorphic load of two classes, where each class has a very +# different set of properties + +db = create_engine('sqlite://', echo=True, echo_uow=False) + +# a table to store companies +companies = Table('companies', db, + Column('company_id', Integer, primary_key=True), + Column('name', String(50))).create() + +# we will define an inheritance relationship between the table "people" and "engineers", +# and a second inheritance relationship between the table "people" and "managers" +people = Table('people', db, + Column('person_id', Integer, primary_key=True), + Column('company_id', Integer, ForeignKey('companies.company_id')), + Column('name', String(50))).create() + +engineers = Table('engineers', db, + Column('person_id', Integer, ForeignKey('people.person_id'), primary_key=True), + Column('status', String(30)), + Column('engineer_name', String(50)), + Column('primary_language', String(50)), + ).create() + +managers = Table('managers', db, + Column('person_id', Integer, ForeignKey('people.person_id'), primary_key=True), + Column('status', String(30)), + Column('manager_name', String(50)) + ).create() + + +# create our classes. The Engineer and Manager classes extend from Person. +class Person(object): + def __repr__(self): + return "Ordinary person %s" % self.name +class Engineer(Person): + def __repr__(self): + return "Engineer %s, status %s, engineer_name %s, primary_language %s" % (self.name, self.status, self.engineer_name, self.primary_language) +class Manager(Person): + def __repr__(self): + return "Manager %s, status %s, manager_name %s" % (self.name, self.status, self.manager_name) +class Company(object): + def __repr__(self): + return "Company %s" % self.name + +# assign plain vanilla mappers +assign_mapper(Person, people) +assign_mapper(Engineer, engineers, inherits=Person.mapper) +assign_mapper(Manager, managers, inherits=Person.mapper) + +# create a union that represents both types of joins. we have to use +# nulls to pad out the disparate columns. +person_join = select( + [ + people, + managers.c.status, + managers.c.manager_name, + null().label('engineer_name'), + null().label('primary_language'), + column("'manager'").label('type') + ], + people.c.person_id==managers.c.person_id).union_all( + select( + [ + people, + engineers.c.status, + null().label('').label('manager_name'), + engineers.c.engineer_name, + engineers.c.primary_language, + column("'engineer'").label('type') + ], + people.c.person_id==engineers.c.person_id)).alias('pjoin') + + +# MapperExtension object. +class PersonLoader(MapperExtension): + def create_instance(self, mapper, row, imap, class_): + if row[person_join.c.type] =='engineer': + return Engineer() + elif row[person_join.c.type] =='manager': + return Manager() + else: + return Person() + + def populate_instance(self, mapper, instance, row, identitykey, imap, isnew): + if row[person_join.c.type] =='engineer': + Engineer.mapper.populate_instance(instance, row, identitykey, imap, isnew) + return False + elif row[person_join.c.type] =='manager': + Manager.mapper.populate_instance(instance, row, identitykey, imap, isnew) + return False + else: + return True + + + +people_mapper = mapper(Person, person_join, extension=PersonLoader()) + +assign_mapper(Company, companies, properties={ + 'employees': relation(people_mapper, lazy=False, private=True) +}) + +c = Company(name='company1') +c.employees.append(Manager(name='pointy haired boss', status='AAB', manager_name='manager1')) +c.employees.append(Engineer(name='dilbert', status='BBA', engineer_name='engineer1', primary_language='java')) +c.employees.append(Engineer(name='wally', status='CGG', engineer_name='engineer2', primary_language='python')) +c.employees.append(Manager(name='jsmith', status='ABA', manager_name='manager2')) +objectstore.commit() + +objectstore.clear() + +c = Company.get(1) +for e in c.employees: + print e, e._instance_key + +print "\n" + +dilbert = Engineer.mapper.get_by(name='dilbert') +dilbert.engineer_name = 'hes dibert!' +objectstore.commit() + +objectstore.clear() +c = Company.get(1) +for e in c.employees: + print e, e._instance_key + +objectstore.delete(c) +objectstore.commit() + + +managers.drop() +engineers.drop() +people.drop() +companies.drop() </ins></span></pre></div> <a id="sqlalchemytrunklibsqlalchemymappingmapperpy"></a> <div class="modfile"><h4>Modified: sqlalchemy/trunk/lib/sqlalchemy/mapping/mapper.py (1190 => 1191)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/trunk/lib/sqlalchemy/mapping/mapper.py 2006-03-23 07:38:28 UTC (rev 1190) +++ sqlalchemy/trunk/lib/sqlalchemy/mapping/mapper.py 2006-03-24 06:28:27 UTC (rev 1191) </span><span class="lines">@@ -291,7 +291,7 @@ </span><span class="cx"> objectstore.get_session().register_clean(value) </span><span class="cx"> </span><span class="cx"> if mappers: </span><del>- result.extend(otherresults) </del><ins>+ result = [result] + otherresults </ins><span class="cx"> return result </span><span class="cx"> </span><span class="cx"> def get(self, *ident): </span><span class="lines">@@ -837,8 +837,8 @@ </span><span class="cx"> </span><span class="cx"> # call further mapper properties on the row, to pull further </span><span class="cx"> # instances from the row and possibly populate this item. </span><del>- for prop in self.props.values(): - prop.execute(instance, row, identitykey, imap, isnew) </del><ins>+ if self.extension.populate_instance(self, instance, row, identitykey, imap, isnew): + self.populate_instance(instance, row, identitykey, imap, isnew, translate=False) </ins><span class="cx"> </span><span class="cx"> if self.extension.append_result(self, row, imap, result, instance, isnew, populate_existing=populate_existing): </span><span class="cx"> if result is not None: </span><span class="lines">@@ -846,6 +846,17 @@ </span><span class="cx"> </span><span class="cx"> return instance </span><span class="cx"> </span><ins>+ def populate_instance(self, instance, row, identitykey, imap, isnew, translate=True): + if translate: + newrow = {} + for table in self.tables: + for c in table.c: + newrow[c] = row[c.key] + row = newrow + + for prop in self.props.values(): + prop.execute(instance, row, identitykey, imap, isnew) + </ins><span class="cx"> class MapperProperty(object): </span><span class="cx"> """an element attached to a Mapper that describes and assists in the loading and saving </span><span class="cx"> of an attribute on an object instance.""" </span><span class="lines">@@ -930,7 +941,8 @@ </span><span class="cx"> def append_result(self, mapper, row, imap, result, instance, isnew, populate_existing=False): </span><span class="cx"> """called when an object instance is being appended to a result list. </span><span class="cx"> </span><del>- If it returns True, it is assumed that this method handled the appending itself. </del><ins>+ If this method returns True, it is assumed that the mapper should do the appending, else + if this method returns False, it is assumed that the append was handled by this method. </ins><span class="cx"> </span><span class="cx"> mapper - the mapper doing the operation </span><span class="cx"> </span><span class="lines">@@ -956,6 +968,22 @@ </span><span class="cx"> return True </span><span class="cx"> else: </span><span class="cx"> return self.next.append_result(mapper, row, imap, result, instance, isnew, populate_existing) </span><ins>+ def populate_instance(self, mapper, instance, row, identitykey, imap, isnew): + """called right before the mapper, after creating an instance from a row, passes the row + to its MapperProperty objects which are responsible for populating the object's attributes. + If this method returns True, it is assumed that the mapper should do the appending, else + if this method returns False, it is assumed that the append was handled by this method. + + Essentially, this method is used to have a different mapper populate the object: + + def populate_instance(self, mapper, *args): + othermapper.populate_instance(*args) + return False + """ + if self.next is None: + return True + else: + return self.next.populate_instance(row, imap, result, instance, isnew) </ins><span class="cx"> def before_insert(self, mapper, instance): </span><span class="cx"> """called before an object instance is INSERTed into its table. </span><span class="cx"> </span></span></pre></div> <a id="sqlalchemytrunklibsqlalchemymappingpropertiespy"></a> <div class="modfile"><h4>Modified: sqlalchemy/trunk/lib/sqlalchemy/mapping/properties.py (1190 => 1191)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/trunk/lib/sqlalchemy/mapping/properties.py 2006-03-23 07:38:28 UTC (rev 1190) +++ sqlalchemy/trunk/lib/sqlalchemy/mapping/properties.py 2006-03-24 06:28:27 UTC (rev 1191) </span><span class="lines">@@ -807,11 +807,14 @@ </span><span class="cx"> if map.has_key(key): </span><span class="cx"> key = map[key] </span><span class="cx"> return self.row[key] </span><ins>+ def keys(self): + return map.keys() </ins><span class="cx"> map = {} </span><span class="cx"> for c in self.eagertarget.c: </span><span class="cx"> parent = self.target._get_col_by_original(c.original) </span><span class="cx"> map[parent] = c </span><span class="cx"> map[parent._label] = c </span><ins>+ map[parent.name] = c </ins><span class="cx"> return DecoratorDict </span><span class="cx"> </span><span class="cx"> def _instance(self, row, imap, result_list=None): </span></span></pre></div> <a id="sqlalchemytrunklibsqlalchemysqlpy"></a> <div class="modfile"><h4>Modified: sqlalchemy/trunk/lib/sqlalchemy/sql.py (1190 => 1191)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/trunk/lib/sqlalchemy/sql.py 2006-03-23 07:38:28 UTC (rev 1190) +++ sqlalchemy/trunk/lib/sqlalchemy/sql.py 2006-03-24 06:28:27 UTC (rev 1191) </span><span class="lines">@@ -13,7 +13,7 @@ </span><span class="cx"> import string, re, random </span><span class="cx"> types = __import__('types') </span><span class="cx"> </span><del>-__all__ = ['text', 'table', 'column', 'func', 'select', 'update', 'insert', 'delete', 'join', 'and_', 'or_', 'not_', 'union', 'union_all', 'desc', 'asc', 'outerjoin', 'alias', 'subquery', 'literal', 'bindparam', 'exists'] </del><ins>+__all__ = ['text', 'table', 'column', 'func', 'select', 'update', 'insert', 'delete', 'join', 'and_', 'or_', 'not_', 'union', 'union_all', 'null', 'desc', 'asc', 'outerjoin', 'alias', 'subquery', 'literal', 'bindparam', 'exists'] </ins><span class="cx"> </span><span class="cx"> def desc(column): </span><span class="cx"> """returns a descending ORDER BY clause element, e.g.: </span><span class="lines">@@ -705,9 +705,11 @@ </span><span class="cx"> def _get_from_objects(self): </span><span class="cx"> return [] </span><span class="cx"> </span><del>-class Null(ClauseElement): </del><ins>+class Null(ColumnElement): </ins><span class="cx"> """represents the NULL keyword in a SQL statement. public contstructor is the </span><span class="cx"> null() function.""" </span><ins>+ def __init__(self): + self.type = sqltypes.NULLTYPE </ins><span class="cx"> def accept_visitor(self, visitor): </span><span class="cx"> visitor.visit_null(self) </span><span class="cx"> def _get_from_objects(self): </span></span></pre> </div> </div> </body> </html> |