[Sqlalchemy-commits] [1104] sqlalchemy/trunk/test: fixed bug in eager loading on a many-to-one [tick
Brought to you by:
zzzeek
From: <co...@sq...> - 2006-03-06 19:06:23
|
<!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>[1104] sqlalchemy/trunk/test: fixed bug in eager loading on a many-to-one [ticket:96], added the ticket tests as a unit test eagerload2.</title> </head> <body> <div id="msg"> <dl> <dt>Revision</dt> <dd>1104</dd> <dt>Author</dt> <dd>zzzeek</dd> <dt>Date</dt> <dd>2006-03-06 13:06:06 -0600 (Mon, 06 Mar 2006)</dd> </dl> <h3>Log Message</h3> <pre>fixed bug in eager loading on a many-to-one [ticket:96], added the ticket tests as a unit test eagerload2. got eagerload1 to be a unit test also.</pre> <h3>Modified Paths</h3> <ul> <li><a href="#sqlalchemytrunklibsqlalchemymappingpropertiespy">sqlalchemy/trunk/lib/sqlalchemy/mapping/properties.py</a></li> <li><a href="#sqlalchemytrunklibsqlalchemyutilpy">sqlalchemy/trunk/lib/sqlalchemy/util.py</a></li> <li><a href="#sqlalchemytrunktestalltestspy">sqlalchemy/trunk/test/alltests.py</a></li> <li><a href="#sqlalchemytrunktesteagertest1py">sqlalchemy/trunk/test/eagertest1.py</a></li> </ul> <h3>Added Paths</h3> <ul> <li><a href="#sqlalchemytrunktesteagertest2py">sqlalchemy/trunk/test/eagertest2.py</a></li> </ul> </div> <div id="patch"> <h3>Diff</h3> <a id="sqlalchemytrunklibsqlalchemymappingpropertiespy"></a> <div class="modfile"><h4>Modified: sqlalchemy/trunk/lib/sqlalchemy/mapping/properties.py (1103 => 1104)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/trunk/lib/sqlalchemy/mapping/properties.py 2006-03-06 17:14:50 UTC (rev 1103) +++ sqlalchemy/trunk/lib/sqlalchemy/mapping/properties.py 2006-03-06 19:06:06 UTC (rev 1104) </span><span class="lines">@@ -772,6 +772,11 @@ </span><span class="cx"> if not self.uselist: </span><span class="cx"> if isnew: </span><span class="cx"> h.setattr_clean(self._instance(row, imap)) </span><ins>+ else: + # call _instance on the row, even though the object has been created, + # so that we further descend into properties + self._instance(row, imap) + </ins><span class="cx"> return </span><span class="cx"> elif isnew: </span><span class="cx"> result_list = h </span></span></pre></div> <a id="sqlalchemytrunklibsqlalchemyutilpy"></a> <div class="modfile"><h4>Modified: sqlalchemy/trunk/lib/sqlalchemy/util.py (1103 => 1104)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/trunk/lib/sqlalchemy/util.py 2006-03-06 17:14:50 UTC (rev 1103) +++ sqlalchemy/trunk/lib/sqlalchemy/util.py 2006-03-06 19:06:06 UTC (rev 1104) </span><span class="lines">@@ -229,6 +229,8 @@ </span><span class="cx"> return dict.__getitem__(self, key) </span><span class="cx"> except KeyError: </span><span class="cx"> return self.decorate[key] </span><ins>+ def __repr__(self): + return dict.__repr__(self) + repr(self.decorate) </ins><span class="cx"> class HashSet(object): </span><span class="cx"> """implements a Set.""" </span><span class="cx"> def __init__(self, iter=None, ordered=False): </span></span></pre></div> <a id="sqlalchemytrunktestalltestspy"></a> <div class="modfile"><h4>Modified: sqlalchemy/trunk/test/alltests.py (1103 => 1104)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/trunk/test/alltests.py 2006-03-06 17:14:50 UTC (rev 1103) +++ sqlalchemy/trunk/test/alltests.py 2006-03-06 19:06:06 UTC (rev 1104) </span><span class="lines">@@ -32,6 +32,8 @@ </span><span class="cx"> </span><span class="cx"> # ORM selecting </span><span class="cx"> 'mapper', </span><ins>+ 'eagertest1', + 'eagertest2', </ins><span class="cx"> </span><span class="cx"> # ORM persistence </span><span class="cx"> 'objectstore', </span></span></pre></div> <a id="sqlalchemytrunktesteagertest1py"></a> <div class="modfile"><h4>Modified: sqlalchemy/trunk/test/eagertest1.py (1103 => 1104)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/trunk/test/eagertest1.py 2006-03-06 17:14:50 UTC (rev 1103) +++ sqlalchemy/trunk/test/eagertest1.py 2006-03-06 19:06:06 UTC (rev 1104) </span><span class="lines">@@ -1,60 +1,74 @@ </span><ins>+from testbase import PersistTest, AssertMixin +import testbase +import unittest, sys, os </ins><span class="cx"> from sqlalchemy import * </span><ins>+import datetime </ins><span class="cx"> </span><del>-class Part(object):pass -class Design(object):pass -class DesignType(object):pass -class InheritedPart(object):pass </del><ins>+class EagerTest(AssertMixin): + def setUpAll(self): + global designType, design, part, inheritedPart + + designType = Table('design_types', testbase.db, + Column('design_type_id', Integer, primary_key=True), + ) </ins><span class="cx"> </span><del>-engine = create_engine('sqlite://', echo=True) </del><ins>+ design =Table('design', testbase.db, + Column('design_id', Integer, primary_key=True), + Column('design_type_id', Integer, ForeignKey('design_types.design_type_id'))) </ins><span class="cx"> </span><del>-designType = Table('design_types', engine, - Column('design_type_id', Integer, primary_key=True), - ) </del><ins>+ part = Table('parts', testbase.db, + Column('part_id', Integer, primary_key=True), + Column('design_id', Integer, ForeignKey('design.design_id')), + Column('design_type_id', Integer, ForeignKey('design_types.design_type_id'))) </ins><span class="cx"> </span><del>-design =Table('design', engine, - Column('design_id', Integer, primary_key=True), - Column('design_type_id', Integer, ForeignKey('design_types.design_type_id'))) </del><ins>+ inheritedPart = Table('inherited_part', testbase.db, + Column('ip_id', Integer, primary_key=True), + Column('part_id', Integer, ForeignKey('parts.part_id')), + Column('design_id', Integer, ForeignKey('design.design_id')), + ) </ins><span class="cx"> </span><del>-part = Table('parts', engine, - Column('part_id', Integer, primary_key=True), - Column('design_id', Integer, ForeignKey('design.design_id')), - Column('design_type_id', Integer, ForeignKey('design_types.design_type_id'))) </del><ins>+ designType.create() + design.create() + part.create() + inheritedPart.create() + def tearDownAll(self): + inheritedPart.drop() + part.drop() + design.drop() + designType.drop() + + def testone(self): + class Part(object):pass + class Design(object):pass + class DesignType(object):pass + class InheritedPart(object):pass </ins><span class="cx"> </span><del>-inheritedPart = Table('inherited_part', engine, - Column('ip_id', Integer, primary_key=True), - Column('part_id', Integer, ForeignKey('parts.part_id')), - Column('design_id', Integer, ForeignKey('design.design_id')), - ) </del><ins>+ assign_mapper(Part, part) </ins><span class="cx"> </span><del>-designType.create() -design.create() -part.create() -inheritedPart.create() - -assign_mapper(Part, part) </del><ins>+ assign_mapper(InheritedPart, inheritedPart, properties=dict( + part=relation(Part, lazy=False) + )) </ins><span class="cx"> </span><del>-assign_mapper(InheritedPart, inheritedPart, properties=dict( - part=relation(Part, lazy=False) -)) </del><ins>+ assign_mapper(Design, design, properties=dict( + parts=relation(Part, private=True, backref="design"), + inheritedParts=relation(InheritedPart, private=True, backref="design"), + )) </ins><span class="cx"> </span><del>-assign_mapper(Design, design, properties=dict( - parts=relation(Part, private=True, backref="design"), - inheritedParts=relation(InheritedPart, private=True, backref="design"), -)) </del><ins>+ assign_mapper(DesignType, designType, properties=dict( + # designs=relation(Design, private=True, backref="type"), + )) </ins><span class="cx"> </span><del>-assign_mapper(DesignType, designType, properties=dict( -# designs=relation(Design, private=True, backref="type"), -)) </del><ins>+ Design.mapper.add_property("type", relation(DesignType, lazy=False, backref="designs")) + Part.mapper.add_property("design", relation(Design, lazy=False, backref="parts")) + #Part.mapper.add_property("designType", relation(DesignType)) </ins><span class="cx"> </span><del>-Design.mapper.add_property("type", relation(DesignType, lazy=False, backref="designs")) -Part.mapper.add_property("design", relation(Design, lazy=False, backref="parts")) -#Part.mapper.add_property("designType", relation(DesignType)) </del><ins>+ d = Design() + objectstore.commit() + objectstore.clear() + x = Design.get(1) + x.inheritedParts </ins><span class="cx"> </span><del>-d = Design() -objectstore.commit() -objectstore.clear() -print "lets go !\n\n\n" -x = Design.get(1) -x.inheritedParts </del><ins>+if __name__ == "__main__": + testbase.main() </ins><span class="cx"> </span><span class="cx"> </span></span></pre></div> <a id="sqlalchemytrunktesteagertest2py"></a> <div class="addfile"><h4>Added: sqlalchemy/trunk/test/eagertest2.py (1103 => 1104)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/trunk/test/eagertest2.py 2006-03-06 17:14:50 UTC (rev 1103) +++ sqlalchemy/trunk/test/eagertest2.py 2006-03-06 19:06:06 UTC (rev 1104) </span><span class="lines">@@ -0,0 +1,254 @@ </span><ins>+from testbase import PersistTest, AssertMixin +import testbase +import unittest, sys, os +from sqlalchemy import * +import datetime + +db = testbase.db + +class EagerTest(AssertMixin): + def setUpAll(self): + objectstore.clear() + clear_mappers() + testbase.db.tables.clear() + + global companies_table, addresses_table, invoice_table, phones_table, items_table + + companies_table = Table('companies', db, + Column('company_id', Integer, Sequence('company_id_seq', optional=True), primary_key = True), + Column('company_name', String(40)), + + ) + + addresses_table = Table('addresses', db, + Column('address_id', Integer, Sequence('address_id_seq', optional=True), primary_key = True), + Column('company_id', Integer, ForeignKey("companies.company_id")), + Column('address', String(40)), + ) + + phones_table = Table('phone_numbers', db, + Column('phone_id', Integer, Sequence('phone_id_seq', optional=True), primary_key = True), + Column('address_id', Integer, ForeignKey('addresses.address_id')), + Column('type', String(20)), + Column('number', String(10)), + ) + + invoice_table = Table('invoices', db, + Column('invoice_id', Integer, Sequence('invoice_id_seq', optional=True), primary_key = True), + Column('company_id', Integer, ForeignKey("companies.company_id")), + Column('date', DateTime), + ) + + items_table = Table('items', db, + Column('item_id', Integer, Sequence('item_id_seq', optional=True), primary_key = True), + Column('invoice_id', Integer, ForeignKey('invoices.invoice_id')), + Column('code', String(20)), + Column('qty', Integer), + ) + + companies_table.create() + addresses_table.create() + phones_table.create() + invoice_table.create() + items_table.create() + + def tearDownAll(self): + items_table.drop() + invoice_table.drop() + phones_table.drop() + addresses_table.drop() + companies_table.drop() + + def tearDown(self): + objectstore.clear() + clear_mappers() + items_table.delete().execute() + invoice_table.delete().execute() + phones_table.delete().execute() + addresses_table.delete().execute() + companies_table.delete().execute() + + def testone(self): + """tests eager load of a many-to-one attached to a one-to-many. this testcase illustrated + the bug, which is that when the single Company is loaded, no further processing of the rows + occurred in order to load the Company's second Address object.""" + class Company(object): + def __init__(self): + self.company_id = None + def __repr__(self): + return "Company:" + repr(getattr(self, 'company_id', None)) + " " + repr(getattr(self, 'company_name', None)) + " " + str([repr(addr) for addr in self.addresses]) + + class Address(object): + def __repr__(self): + return "Address: " + repr(getattr(self, 'address_id', None)) + " " + repr(getattr(self, 'company_id', None)) + " " + repr(self.address) + + class Invoice(object): + def __init__(self): + self.invoice_id = None + def __repr__(self): + return "Invoice:" + repr(getattr(self, 'invoice_id', None)) + " " + repr(getattr(self, 'date', None)) + " " + repr(self.company) + + Address.mapper = mapper(Address, addresses_table, properties={ + }) + Company.mapper = mapper(Company, companies_table, properties={ + 'addresses' : relation(Address.mapper, lazy=False), + }) + Invoice.mapper = mapper(Invoice, invoice_table, properties={ + 'company': relation(Company.mapper, lazy=False, ) + }) + + c1 = Company() + c1.company_name = 'company 1' + a1 = Address() + a1.address = 'a1 address' + c1.addresses.append(a1) + a2 = Address() + a2.address = 'a2 address' + c1.addresses.append(a2) + i1 = Invoice() + i1.date = datetime.datetime.now() + i1.company = c1 + + + objectstore.commit() + + company_id = c1.company_id + invoice_id = i1.invoice_id + + objectstore.clear() + + c = Company.mapper.get(company_id) + + objectstore.clear() + + i = Invoice.mapper.get(invoice_id) + + self.echo(repr(c)) + self.echo(repr(i.company)) + self.assert_(repr(c) == repr(i.company)) + + def testtwo(self): + """this is the original testcase that includes various complicating factors""" + class Company(object): + def __init__(self): + self.company_id = None + def __repr__(self): + return "Company:" + repr(getattr(self, 'company_id', None)) + " " + repr(getattr(self, 'company_name', None)) + " " + str([repr(addr) for addr in self.addresses]) + + class Address(object): + def __repr__(self): + return "Address: " + repr(getattr(self, 'address_id', None)) + " " + repr(getattr(self, 'company_id', None)) + " " + repr(self.address) + str([repr(ph) for ph in self.phones]) + + class Phone(object): + def __repr__(self): + return "Phone: " + repr(getattr(self, 'phone_id', None)) + " " + repr(getattr(self, 'address_id', None)) + " " + repr(self.type) + " " + repr(self.number) + + class Invoice(object): + def __init__(self): + self.invoice_id = None + def __repr__(self): + return "Invoice:" + repr(getattr(self, 'invoice_id', None)) + " " + repr(getattr(self, 'date', None)) + " " + repr(self.company) + " " + str([repr(item) for item in self.items]) + + class Item(object): + def __repr__(self): + return "Item: " + repr(getattr(self, 'item_id', None)) + " " + repr(getattr(self, 'invoice_id', None)) + " " + repr(self.code) + " " + repr(self.qty) + + Phone.mapper = mapper(Phone, phones_table, is_primary=True) + + Address.mapper = mapper(Address, addresses_table, properties={ + 'phones': relation(Phone.mapper, lazy=False, backref='address') + }) + + Company.mapper = mapper(Company, companies_table, properties={ + 'addresses' : relation(Address.mapper, lazy=False, backref='company'), + }) + + Item.mapper = mapper(Item, items_table, is_primary=True) + + Invoice.mapper = mapper(Invoice, invoice_table, properties={ + 'items': relation(Item.mapper, lazy=False, backref='invoice'), + 'company': relation(Company.mapper, lazy=False, backref='invoices') + }) + + objectstore.clear() + c1 = Company() + c1.company_name = 'company 1' + + a1 = Address() + a1.address = 'a1 address' + + p1 = Phone() + p1.type = 'home' + p1.number = '1111' + + a1.phones.append(p1) + + p2 = Phone() + p2.type = 'work' + p2.number = '22222' + a1.phones.append(p2) + + c1.addresses.append(a1) + + a2 = Address() + a2.address = 'a2 address' + + p3 = Phone() + p3.type = 'home' + p3.number = '3333' + a2.phones.append(p3) + + p4 = Phone() + p4.type = 'work' + p4.number = '44444' + a2.phones.append(p4) + + c1.addresses.append(a2) + + objectstore.commit() + + company_id = c1.company_id + + objectstore.clear() + + a = Company.mapper.get(company_id) + self.echo(repr(a)) + + # set up an invoice + i1 = Invoice() + i1.date = datetime.datetime.now() + i1.company = c1 + + item1 = Item() + item1.code = 'aaaa' + item1.qty = 1 + item1.invoice = i1 + + item2 = Item() + item2.code = 'bbbb' + item2.qty = 2 + item2.invoice = i1 + + item3 = Item() + item3.code = 'cccc' + item3.qty = 3 + item3.invoice = i1 + + objectstore.commit() + + invoice_id = i1.invoice_id + + objectstore.clear() + + c = Company.mapper.get(company_id) + self.echo(repr(c)) + + objectstore.clear() + + i = Invoice.mapper.get(invoice_id) + self.echo(repr(i)) + + self.assert_(repr(i.company) == repr(c)) + +if __name__ == "__main__": + testbase.main() </ins></span></pre> </div> </div> </body> </html> |