[Sqlalchemy-commits] [1347] sqlalchemy/branches/schema/lib/sqlalchemy/orm: polymorphic, step 1
Brought to you by:
zzzeek
From: <co...@sq...> - 2006-04-28 01:37:46
|
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head><style type="text/css"><!-- #msg dl { border: 1px #006 solid; background: #369; padding: 6px; color: #fff; } #msg dt { float: left; width: 6em; font-weight: bold; } #msg dt:after { content:':';} #msg dl, #msg dt, #msg ul, #msg li { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; } #msg dl a { font-weight: bold} #msg dl a:link { color:#fc3; } #msg dl a:active { color:#ff0; } #msg dl a:visited { color:#cc6; } h3 { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; font-weight: bold; } #msg pre { overflow: auto; background: #ffc; border: 1px #fc0 solid; padding: 6px; } #msg ul, pre { overflow: auto; } #patch { width: 100%; } #patch h4 {font-family: verdana,arial,helvetica,sans-serif;font-size:10pt;padding:8px;background:#369;color:#fff;margin:0;} #patch .propset h4, #patch .binary h4 {margin:0;} #patch pre {padding:0;line-height:1.2em;margin:0;} #patch .diff {width:100%;background:#eee;padding: 0 0 10px 0;overflow:auto;} #patch .propset .diff, #patch .binary .diff {padding:10px 0;} #patch span {display:block;padding:0 10px;} #patch .modfile, #patch .addfile, #patch .delfile, #patch .propset, #patch .binary, #patch .copfile {border:1px solid #ccc;margin:10px 0;} #patch ins {background:#dfd;text-decoration:none;display:block;padding:0 10px;} #patch del {background:#fdd;text-decoration:none;display:block;padding:0 10px;} #patch .lines, .info {color:#888;background:#fff;} --></style> <title>[1347] sqlalchemy/branches/schema/lib/sqlalchemy/orm: polymorphic, step 1</title> </head> <body> <div id="msg"> <dl> <dt>Revision</dt> <dd>1347</dd> <dt>Author</dt> <dd>zzzeek</dd> <dt>Date</dt> <dd>2006-04-27 20:37:29 -0500 (Thu, 27 Apr 2006)</dd> </dl> <h3>Log Message</h3> <pre>polymorphic, step 1</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="#sqlalchemybranchesschemalibsqlalchemyormquerypy">sqlalchemy/branches/schema/lib/sqlalchemy/orm/query.py</a></li> </ul> </div> <div id="patch"> <h3>Diff</h3> <a id="sqlalchemybranchesschemaexamplespolymorphpolymorph2py"></a> <div class="modfile"><h4>Modified: sqlalchemy/branches/schema/examples/polymorph/polymorph2.py (1346 => 1347)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/schema/examples/polymorph/polymorph2.py 2006-04-28 00:04:54 UTC (rev 1346) +++ sqlalchemy/branches/schema/examples/polymorph/polymorph2.py 2006-04-28 01:37:29 UTC (rev 1347) </span><span class="lines">@@ -34,6 +34,9 @@ </span><span class="cx"> </span><span class="cx"> # create our classes. The Engineer and Manager classes extend from Person. </span><span class="cx"> class Person(object): </span><ins>+ def __init__(self, **kwargs): + for key, value in kwargs.iteritems(): + setattr(self, key, value) </ins><span class="cx"> def __repr__(self): </span><span class="cx"> return "Ordinary person %s" % self.name </span><span class="cx"> class Engineer(Person): </span><span class="lines">@@ -43,13 +46,12 @@ </span><span class="cx"> def __repr__(self): </span><span class="cx"> return "Manager %s, status %s, manager_name %s" % (self.name, self.status, self.manager_name) </span><span class="cx"> class Company(object): </span><ins>+ def __init__(self, **kwargs): + for key, value in kwargs.iteritems(): + setattr(self, key, value) </ins><span class="cx"> def __repr__(self): </span><span class="cx"> return "Company %s" % self.name </span><span class="cx"> </span><del>-# assign plain vanilla mappers -assign_mapper(Person, people) -assign_mapper(Engineer, engineers, inherits=Person.mapper) -assign_mapper(Manager, managers, inherits=Person.mapper) </del><span class="cx"> </span><span class="cx"> # create a union that represents both types of joins. we have to use </span><span class="cx"> # nulls to pad out the disparate columns. </span><span class="lines">@@ -74,60 +76,53 @@ </span><span class="cx"> ], </span><span class="cx"> people.c.person_id==engineers.c.person_id)).alias('pjoin') </span><span class="cx"> </span><del>-print [c for c in person_join.c] </del><ins>+ +mapper(Person, people) +mapper(Engineer, engineers, inherits=Person) +mapper(Manager, managers, inherits=Person) </ins><span class="cx"> </span><del>-# 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, session, instance, row, identitykey, imap, isnew): - if row[person_join.c.type] =='engineer': - Engineer.mapper.populate_instance(session, instance, row, identitykey, imap, isnew, frommapper=mapper) - return False - elif row[person_join.c.type] =='manager': - Manager.mapper.populate_instance(session, instance, row, identitykey, imap, isnew, frommapper=mapper) - return False - else: - return sqlalchemy.mapping.EXT_PASS </del><ins>+people_mapper = mapper(Person, person_join, polymorphic_on=person_join.c.type, polymorphic_map={'engineer':Engineer, 'manager':Manager}, non_primary=True) </ins><span class="cx"> </span><del>-people_mapper = mapper(Person, person_join, polymorphic_on=person_join.c.type, polymorphic_map={'engineer':Engineer, 'manager':Manager}) </del><span class="cx"> </span><del>-assign_mapper(Company, companies, properties={ - 'employees': relation(people_mapper, lazy=False, private=True) </del><ins>+ +mapper(Company, companies, properties={ + 'employees': relation(people_mapper, lazy=False, private=True, backref='company') </ins><span class="cx"> }) </span><span class="cx"> </span><ins>+session = create_session() </ins><span class="cx"> c = Company(name='company1') </span><span class="cx"> c.employees.append(Manager(name='pointy haired boss', status='AAB', manager_name='manager1')) </span><span class="cx"> c.employees.append(Engineer(name='dilbert', status='BBA', engineer_name='engineer1', primary_language='java')) </span><span class="cx"> c.employees.append(Engineer(name='wally', status='CGG', engineer_name='engineer2', primary_language='python')) </span><span class="cx"> c.employees.append(Manager(name='jsmith', status='ABA', manager_name='manager2')) </span><del>-objectstore.commit() </del><ins>+session.save(c) +session.flush() </ins><span class="cx"> </span><del>-objectstore.clear() </del><ins>+session.clear() </ins><span class="cx"> </span><del>-c = Company.get(1) </del><ins>+c = session.query(Company).get(1) </ins><span class="cx"> for e in c.employees: </span><del>- print e, e._instance_key </del><ins>+ print e, e._instance_key, e.company </ins><span class="cx"> </span><span class="cx"> print "\n" </span><span class="cx"> </span><del>-dilbert = Engineer.mapper.get_by(name='dilbert') </del><ins>+dilbert = session.query(people_mapper).get_by(name='dilbert') +print "DILBERT1", dilbert +dilbert2 = session.query(Engineer).get_by(name='dilbert') +print "DILBERT2", dilbert2 +assert dilbert is dilbert2 + </ins><span class="cx"> dilbert.engineer_name = 'hes dibert!' </span><del>-objectstore.commit() </del><span class="cx"> </span><del>-objectstore.clear() -c = Company.get(1) </del><ins>+session.flush() +session.clear() + +c = session.query(Company).get(1) </ins><span class="cx"> for e in c.employees: </span><span class="cx"> print e, e._instance_key </span><span class="cx"> </span><del>-objectstore.delete(c) -objectstore.commit() </del><ins>+session.delete(c) +session.flush() </ins><span class="cx"> </span><span class="cx"> </span><span class="cx"> managers.drop() </span></span></pre></div> <a id="sqlalchemybranchesschemalibsqlalchemyormmapperpy"></a> <div class="modfile"><h4>Modified: sqlalchemy/branches/schema/lib/sqlalchemy/orm/mapper.py (1346 => 1347)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/schema/lib/sqlalchemy/orm/mapper.py 2006-04-28 00:04:54 UTC (rev 1346) +++ sqlalchemy/branches/schema/lib/sqlalchemy/orm/mapper.py 2006-04-28 01:37:29 UTC (rev 1347) </span><span class="lines">@@ -39,7 +39,6 @@ </span><span class="cx"> def __init__(self, </span><span class="cx"> class_, </span><span class="cx"> table, </span><del>- primarytable = None, </del><span class="cx"> properties = None, </span><span class="cx"> primary_key = None, </span><span class="cx"> is_primary = False, </span><span class="lines">@@ -51,11 +50,12 @@ </span><span class="cx"> allow_column_override = False, </span><span class="cx"> entity_name = None, </span><span class="cx"> always_refresh = False, </span><del>- version_id_col = None): </del><ins>+ version_id_col = None, + polymorphic_on=None, + polymorphic_map=None, + polymorphic_ident=None, + selectfrom=None): </ins><span class="cx"> </span><del>- if primarytable is not None: - sys.stderr.write("'primarytable' argument to mapper is deprecated\n") - </del><span class="cx"> ext = MapperExtension() </span><span class="cx"> </span><span class="cx"> for ext_class in global_extensions: </span><span class="lines">@@ -77,7 +77,9 @@ </span><span class="cx"> self.always_refresh = always_refresh </span><span class="cx"> self.version_id_col = version_id_col </span><span class="cx"> self._inheriting_mappers = sets.Set() </span><del>- </del><ins>+ self.polymorphic_on = polymorphic_on + self.polymorphic_map = polymorphic_map or {} + </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><span class="cx"> </span><span class="lines">@@ -91,7 +93,14 @@ </span><span class="cx"> else: </span><span class="cx"> self.table = table </span><span class="cx"> </span><ins>+ if selectfrom is None: + self.selectfrom = self.table + else: + self.selectfrom = selectfrom + </ins><span class="cx"> if inherits is not None: </span><ins>+ if isinstance(inherits, type): + inherits = class_mapper(inherits) </ins><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><span class="cx"> self.primarytable = inherits.primarytable </span><span class="lines">@@ -116,11 +125,15 @@ </span><span class="cx"> self._synchronizer = None </span><span class="cx"> self.inherits = inherits </span><span class="cx"> self.noninherited_table = table </span><ins>+ if polymorphic_ident is not None: + inherits.add_polymorphic_mapping(polymorphic_ident, self) </ins><span class="cx"> else: </span><span class="cx"> self.primarytable = self.table </span><span class="cx"> self.noninherited_table = self.table </span><span class="cx"> self._synchronizer = None </span><span class="cx"> self.inherits = None </span><ins>+ if polymorphic_ident is not None: + raise ArgumentError("'polymorphic_ident' argument can only be used with inherits=<somemapper>") </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><span class="lines">@@ -198,6 +211,10 @@ </span><span class="cx"> self._init_class() </span><span class="cx"> elif not non_primary: </span><span class="cx"> raise ArgumentError("Class '%s' already has a primary mapper defined. Use is_primary=True to assign a new primary mapper to the class, or use non_primary=True to create a non primary Mapper" % self.class_) </span><ins>+ + for key in self.polymorphic_map.keys(): + if isinstance(self.polymorphic_map[key], type): + self.polymorphic_map[key] = class_mapper(self.polymorphic_map[key]) </ins><span class="cx"> </span><span class="cx"> if inherits is not None: </span><span class="cx"> inherits._inheriting_mappers.add(self) </span><span class="lines">@@ -217,6 +234,11 @@ </span><span class="cx"> #for key, value in self.columntoproperty.iteritems(): </span><span class="cx"> # print key.table.name, key.key, [(v.key, v) for v in value] </span><span class="cx"> </span><ins>+ def add_polymorphic_mapping(self, key, class_or_mapper, entity_name=None): + if isinstance(class_or_mapper, type): + class_or_mapper = class_mapper(class_or_mapper, entity_name=entity_name) + self.polymorphic_map[key] = class_or_mapper + </ins><span class="cx"> def using(self, session): </span><span class="cx"> return querylib.Query(self, session=session) </span><span class="cx"> def query(self, session=None): </span><span class="lines">@@ -720,6 +742,15 @@ </span><span class="cx"> list. if the instance already exists in the given identity map, its not added. in </span><span class="cx"> either case, executes all the property loaders on the instance to also process extra </span><span class="cx"> information in the row.""" </span><ins>+ + if self.polymorphic_on is not None: + discriminator = row[self.polymorphic_on] + mapper = self.polymorphic_map[discriminator] + if mapper is not self: + row = self.translate_row(mapper, row) + return mapper._instance(session, row, imap, result=result, populate_existing=populate_existing) + + </ins><span class="cx"> # look in main identity map. if its there, we dont do anything to it, </span><span class="cx"> # including modifying any of its related items lists, as its already </span><span class="cx"> # been exposed to being modified by the application. </span></span></pre></div> <a id="sqlalchemybranchesschemalibsqlalchemyormquerypy"></a> <div class="modfile"><h4>Modified: sqlalchemy/branches/schema/lib/sqlalchemy/orm/query.py (1346 => 1347)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/schema/lib/sqlalchemy/orm/query.py 2006-04-28 00:04:54 UTC (rev 1346) +++ sqlalchemy/branches/schema/lib/sqlalchemy/orm/query.py 2006-04-28 01:37:29 UTC (rev 1347) </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.table) </del><ins>+ table = property(lambda s:s.mapper.selectfrom) </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">@@ -203,7 +203,6 @@ </span><span class="cx"> except KeyError: </span><span class="cx"> pass </span><span class="cx"> </span><del>- print "_GET!", key, ident </del><span class="cx"> if ident is None: </span><span class="cx"> ident = key[1] </span><span class="cx"> i = 0 </span></span></pre> </div> </div> </body> </html> |