summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMike Bayer <mike_mp@zzzcomputing.com>2008-01-06 20:32:45 +0000
committerMike Bayer <mike_mp@zzzcomputing.com>2008-01-06 20:32:45 +0000
commitd879135085d442b942253688dd5d20edc2b560ec (patch)
treed4261ec338dfd242eb06915d17435819a4da712b
parenta78914942ccc26bfad91e367eaaae2dbd29e4439 (diff)
downloadsqlalchemy-d879135085d442b942253688dd5d20edc2b560ec.tar.gz
- 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)
-rw-r--r--CHANGES8
-rw-r--r--lib/sqlalchemy/orm/mapper.py11
-rw-r--r--test/orm/inheritance/alltests.py1
-rw-r--r--test/orm/inheritance/query.py135
4 files changed, 150 insertions, 5 deletions
diff --git a/CHANGES b/CHANGES
index 872351a13..20a39f51e 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 e8cfc83f3..fc62c773e 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 dc93ed9b3..d95de6246 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 000000000..ee837baed
--- /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