From: Mike Bayer Date: Sun, 6 Jan 2008 20:32:45 +0000 (+0000) Subject: - fixed bug which could occur with polymorphic "union" mapper X-Git-Tag: rel_0_4_2b~4 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=d879135085d442b942253688dd5d20edc2b560ec;p=thirdparty%2Fsqlalchemy%2Fsqlalchemy.git - fixed bug which could occur with polymorphic "union" mapper which falls back to "deferred" loading of inheriting tables - the "columns" collection on a mapper/mapped class (i.e. 'c') is against the mapped table, not the select_table in the case of polymorphic "union" loading (this shouldn't be noticeable) --- diff --git a/CHANGES b/CHANGES index 872351a13d..20a39f51e1 100644 --- a/CHANGES +++ b/CHANGES @@ -18,6 +18,14 @@ CHANGES which are later added via add_property(). This commonly includes backrefs. (i.e. you can make synonyms for backrefs without worrying about the order of operations) [ticket:919] + + - fixed bug which could occur with polymorphic "union" mapper + which falls back to "deferred" loading of inheriting tables + + - the "columns" collection on a mapper/mapped class (i.e. 'c') + is against the mapped table, not the select_table in the + case of polymorphic "union" loading (this shouldn't be + noticeable) - ext - '+', '*', '+=' and '*=' support for association proxied lists. diff --git a/lib/sqlalchemy/orm/mapper.py b/lib/sqlalchemy/orm/mapper.py index e8cfc83f33..fc62c773e9 100644 --- a/lib/sqlalchemy/orm/mapper.py +++ b/lib/sqlalchemy/orm/mapper.py @@ -665,8 +665,7 @@ class Mapper(object): return if isinstance(prop, ColumnProperty): - # relate the mapper's "select table" to the given ColumnProperty - col = self.select_table.corresponding_column(prop.columns[0]) + col = self.mapped_table.corresponding_column(prop.columns[0]) # col might not be present! the selectable given to the mapper need not include "deferred" # columns (included in zblog tests) if col is None: @@ -681,9 +680,9 @@ class Mapper(object): if isinstance(prop.instrument, Mapper._CompileOnAttr): prop.instrument = object.__getattribute__(prop.instrument, 'existing_prop') if prop.map_column: - if not key in self.select_table.c: - raise exceptions.ArgumentError("Can't compile synonym '%s': no column on table '%s' named '%s'" % (prop.name, self.select_table.description, key)) - self._compile_property(prop.name, ColumnProperty(self.select_table.c[key]), init=init, setparent=setparent) + if not key in self.mapped_table.c: + raise exceptions.ArgumentError("Can't compile synonym '%s': no column on table '%s' named '%s'" % (prop.name, self.mapped_table.description, key)) + self._compile_property(prop.name, ColumnProperty(self.mapped_table.c[key]), init=init, setparent=setparent) self.__props[key] = prop if setparent: @@ -1484,6 +1483,8 @@ class Mapper(object): return None def _deferred_inheritance_condition(self, base_mapper, needs_tables): + base_mapper = base_mapper.primary_mapper() + def visit_binary(binary): leftcol = binary.left rightcol = binary.right diff --git a/test/orm/inheritance/alltests.py b/test/orm/inheritance/alltests.py index dc93ed9b38..d95de62460 100644 --- a/test/orm/inheritance/alltests.py +++ b/test/orm/inheritance/alltests.py @@ -4,6 +4,7 @@ import unittest def suite(): modules_to_test = ( 'orm.inheritance.basic', + 'orm.inheritance.query', 'orm.inheritance.manytomany', 'orm.inheritance.single', 'orm.inheritance.concrete', diff --git a/test/orm/inheritance/query.py b/test/orm/inheritance/query.py new file mode 100644 index 0000000000..ee837baed9 --- /dev/null +++ b/test/orm/inheritance/query.py @@ -0,0 +1,135 @@ +"""tests the Query object's ability to work with polymorphic selectables +and inheriting mappers.""" + +# TODO: under construction ! + +import testbase +import sets +from sqlalchemy import * +from sqlalchemy.orm import * +from testlib import * +from testlib import fixtures + +class Company(fixtures.Base): + pass + +class Person(fixtures.Base): + pass +class Engineer(Person): + pass +class Manager(Person): + pass +class Boss(Manager): + pass + +class Paperwork(fixtures.Base): + pass + +class PolymorphicQueryTest(ORMTest): + keep_data = True + keep_mappers = True + + def define_tables(self, metadata): + global companies, people, engineers, managers, boss, paperwork + + companies = Table('companies', metadata, + Column('company_id', Integer, Sequence('company_id_seq', optional=True), primary_key=True), + Column('name', String(50))) + + people = Table('people', metadata, + Column('person_id', Integer, Sequence('person_id_seq', optional=True), primary_key=True), + Column('company_id', Integer, ForeignKey('companies.company_id')), + Column('name', String(50)), + Column('type', String(30))) + + engineers = Table('engineers', metadata, + 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)), + ) + + managers = Table('managers', metadata, + Column('person_id', Integer, ForeignKey('people.person_id'), primary_key=True), + Column('status', String(30)), + Column('manager_name', String(50)) + ) + + boss = Table('boss', metadata, + Column('boss_id', Integer, ForeignKey('managers.person_id'), primary_key=True), + Column('golf_swing', String(30)), + ) + + paperwork = Table('paperwork', metadata, + Column('paperwork_id', Integer, primary_key=True), + Column('description', String(50)), + Column('person_id', Integer, ForeignKey('people.person_id'))) + + # create the most awkward polymorphic selects possible; + # the union does not include the "people" table by itself nor does it have + # "people.person_id" directly in it, and it also does not include at all + # the "boss" table + person_join = polymorphic_union( + { + 'engineer':people.join(engineers), + 'manager':people.join(managers), + }, None, 'pjoin') + + # separate join for second-level inherit + manager_join = people.join(managers).outerjoin(boss) + + mapper(Company, companies, properties={ + 'employees':relation(Person) + }) + mapper(Person, people, select_table=person_join, polymorphic_on=people.c.type, polymorphic_identity='person', order_by=person_join.c.person_id, + properties={ + 'paperwork':relation(Paperwork) + }) + mapper(Engineer, engineers, inherits=Person, polymorphic_identity='engineer') + mapper(Manager, managers, select_table=manager_join, inherits=Person, polymorphic_identity='manager') + mapper(Boss, boss, inherits=Manager, polymorphic_identity='boss') + mapper(Paperwork, paperwork) + + def insert_data(self): + c1 = Company(name="MegaCorp, Inc.") + c2 = Company(name="Elbonia, Inc.") + e1 = Engineer(name="dilbert", engineer_name="dilbert", primary_language="java", status="regular engineer", paperwork=[ + Paperwork(description="tps report #1"), + Paperwork(description="tps report #2") + ]) + e2 = Engineer(name="wally", engineer_name="wally", primary_language="c++", status="regular engineer", paperwork=[ + Paperwork(description="tps report #3"), + Paperwork(description="tps report #4") + ]) + b1 = Boss(name="pointy haired boss", golf_swing="fore", manager_name="pointy", status="da boss", paperwork=[ + Paperwork(description="review #1"), + ]) + m1 = Manager(name="dogbert", manager_name="dogbert", status="regular manager", paperwork=[ + Paperwork(description="review #2"), + Paperwork(description="review #3") + ]) + c1.employees = [e1, e2, b1, m1] + + e3 = Engineer(name="vlad", engineer_name="vlad", primary_language="cobol", status="elbonian engineer") + c2.employees = [e3] + sess = create_session() + sess.save(c1) + sess.save(c2) + sess.flush() + sess.clear() + + global all_employees, c1_employees, c2_employees + all_employees = [e1, e2, b1, m1, e3] + c1_employees = [e1, e2, b1, m1] + c2_employees = [e3] + + def test_load_all(self): + sess = create_session() + + self.assertEquals(sess.query(Person).all(), all_employees) + +if __name__ == "__main__": + testbase.main() + + + \ No newline at end of file