summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMike Bayer <mike_mp@zzzcomputing.com>2008-09-04 17:44:48 +0000
committerMike Bayer <mike_mp@zzzcomputing.com>2008-09-04 17:44:48 +0000
commit94c32b19a08182aa5403a893ffd037994199ca44 (patch)
tree15e08f101e7292cbf4a636bccbeea97f28f74bcd
parentcd8a3390e6992a1a969af47ee6268456d7890aa8 (diff)
downloadsqlalchemy-94c32b19a08182aa5403a893ffd037994199ca44.tar.gz
- Fixed bug whereby mapper couldn't initialize if a composite
primary key referenced another table that was not defined yet [ticket:1161]
-rw-r--r--CHANGES5
-rw-r--r--lib/sqlalchemy/orm/mapper.py6
-rw-r--r--lib/sqlalchemy/sql/util.py16
-rw-r--r--test/ext/declarative.py16
4 files changed, 38 insertions, 5 deletions
diff --git a/CHANGES b/CHANGES
index 1f7151823..b395f6edd 100644
--- a/CHANGES
+++ b/CHANGES
@@ -112,6 +112,11 @@ CHANGES
- The `echo_uow` flag on `Session` is deprecated, and unit-of-work
logging is now application-level only, not per-session level.
+
+- declarative
+ - Fixed bug whereby mapper couldn't initialize if a composite
+ primary key referenced another table that was not defined
+ yet [ticket:1161]
- schema
- Added "sorted_tables" accessor to MetaData, which returns
diff --git a/lib/sqlalchemy/orm/mapper.py b/lib/sqlalchemy/orm/mapper.py
index cfea61e26..d6dbc2634 100644
--- a/lib/sqlalchemy/orm/mapper.py
+++ b/lib/sqlalchemy/orm/mapper.py
@@ -545,11 +545,11 @@ class Mapper(object):
# determine primary key from argument or mapped_table pks - reduce to the minimal set of columns
if self.primary_key_argument:
primary_key = sqlutil.reduce_columns(
- self.mapped_table.corresponding_column(c)
- for c in self.primary_key_argument)
+ [self.mapped_table.corresponding_column(c) for c in self.primary_key_argument],
+ ignore_nonexistent_tables=True)
else:
primary_key = sqlutil.reduce_columns(
- self._pks_by_table[self.mapped_table])
+ self._pks_by_table[self.mapped_table], ignore_nonexistent_tables=True)
if len(primary_key) == 0:
raise sa_exc.ArgumentError("Mapper %s could not assemble any primary key columns for mapped table '%s'" % (self, self.mapped_table.description))
diff --git a/lib/sqlalchemy/sql/util.py b/lib/sqlalchemy/sql/util.py
index e1636ccf9..d9c3ed899 100644
--- a/lib/sqlalchemy/sql/util.py
+++ b/lib/sqlalchemy/sql/util.py
@@ -190,7 +190,7 @@ def splice_joins(left, right, stop_on=None):
return ret
-def reduce_columns(columns, *clauses):
+def reduce_columns(columns, *clauses, **kw):
"""given a list of columns, return a 'reduced' set based on natural equivalents.
the set is reduced to the smallest list of columns which have no natural
@@ -200,12 +200,17 @@ def reduce_columns(columns, *clauses):
\*clauses is an optional list of join clauses which will be traversed
to further identify columns that are "equivalent".
+ \**kw may specify 'ignore_nonexistent_tables' to ignore foreign keys
+ whose tables are not yet configured.
+
This function is primarily used to determine the most minimal "primary key"
from a selectable, by reducing the set of primary key columns present
in the the selectable to just those that are not repeated.
"""
+ ignore_nonexistent_tables = kw.pop('ignore_nonexistent_tables', False)
+
columns = util.OrderedSet(columns)
omit = set()
@@ -214,7 +219,14 @@ def reduce_columns(columns, *clauses):
for c in columns:
if c is col:
continue
- if fk.column.shares_lineage(c):
+ try:
+ fk_col = fk.column
+ except exc.NoReferencedTableError:
+ if ignore_nonexistent_tables:
+ continue
+ else:
+ raise
+ if fk_col.shares_lineage(c):
omit.add(col)
break
diff --git a/test/ext/declarative.py b/test/ext/declarative.py
index 6b226cc9f..3121f959f 100644
--- a/test/ext/declarative.py
+++ b/test/ext/declarative.py
@@ -679,6 +679,22 @@ class DeclarativeTest(testing.TestBase, testing.AssertsExecutionResults):
Address(email='one'),
Address(email='two')])])
+ def test_pk_with_fk_init(self):
+ class Bar(Base):
+ __tablename__ = 'bar'
+
+ id = sa.Column(sa.Integer, sa.ForeignKey("foo.id"), primary_key=True)
+ ex = sa.Column(sa.Integer, primary_key=True)
+
+ class Foo(Base):
+ __tablename__ = 'foo'
+
+ id = sa.Column(sa.Integer, primary_key=True)
+ bars = sa.orm.relation(Bar)
+
+ assert Bar.__mapper__.primary_key[0] is Bar.__table__.c.id
+ assert Bar.__mapper__.primary_key[1] is Bar.__table__.c.ex
+
def test_single_inheritance(self):
class Company(Base, ComparableEntity):
__tablename__ = 'companies'