From: Mike Bayer Date: Thu, 21 Jul 2011 15:44:31 +0000 (-0400) Subject: - Added an informative error message when X-Git-Tag: rel_0_7_2~18 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=c1295ce57ba7d906821b3ddd5b97aa9659700d52;p=thirdparty%2Fsqlalchemy%2Fsqlalchemy.git - Added an informative error message when ForeignKeyConstraint refers to a column name in the parent that is not found. Also in 0.6.9. - add tests for [ticket:2226], as if we hit each @declared_attr directly with obj.__get__(obj, name) instead of using getattr(cls, name). Basic inheritance mechanics are improperly used in this case, so 2226 is invalid. --- diff --git a/CHANGES b/CHANGES index 660482e606..41f624af8d 100644 --- a/CHANGES +++ b/CHANGES @@ -99,6 +99,10 @@ CHANGES loses itself. Affects [ticket:2188]. - schema + - Added an informative error message when + ForeignKeyConstraint refers to a column name in + the parent that is not found. Also in 0.6.9. + - Fixed bug whereby adaptation of old append_ddl_listener() function was passing unexpected **kw through to the Table event. Table gets no kws, the MetaData diff --git a/lib/sqlalchemy/ext/declarative.py b/lib/sqlalchemy/ext/declarative.py index 8c26edf5cd..15e7ba34f0 100755 --- a/lib/sqlalchemy/ext/declarative.py +++ b/lib/sqlalchemy/ext/declarative.py @@ -944,7 +944,6 @@ def _as_declarative(cls, classname, dict_): continue elif base is not cls: # we're a mixin. - if isinstance(obj, Column): if obj.foreign_keys: raise exc.InvalidRequestError( @@ -1042,7 +1041,6 @@ def _as_declarative(cls, classname, dict_): if key == c.key: del our_stuff[key] cols = sorted(cols, key=lambda c:c._creation_order) - table = None if '__table__' not in dict_: if tablename is not None: diff --git a/lib/sqlalchemy/schema.py b/lib/sqlalchemy/schema.py index 1763dc484d..25c7e30a20 100644 --- a/lib/sqlalchemy/schema.py +++ b/lib/sqlalchemy/schema.py @@ -2008,7 +2008,13 @@ class ForeignKeyConstraint(Constraint): # string-specified column names now get # resolved to Column objects if isinstance(col, basestring): - col = table.c[col] + try: + col = table.c[col] + except KeyError: + raise exc.ArgumentError( + "Can't create ForeignKeyConstraint " + "on table '%s': no column " + "named '%s' is present." % (table.description, col)) if not hasattr(fk, 'parent') or \ fk.parent is not col: diff --git a/test/ext/test_declarative.py b/test/ext/test_declarative.py index 1adaf6e5fa..22733223de 100644 --- a/test/ext/test_declarative.py +++ b/test/ext/test_declarative.py @@ -3176,6 +3176,41 @@ class DeclarativeMixinTest(DeclarativeTestBase): eq_(Model.__table__.c.keys(), ['col1', 'col3', 'col2', 'col4', 'id']) + def test_honor_class_mro_one(self): + class HasXMixin(object): + @declared_attr + def x(self): + return Column(Integer) + + class Parent(HasXMixin, Base): + __tablename__ = 'parent' + id = Column(Integer, primary_key=True) + + class Child(Parent): + __tablename__ = 'child' + id = Column(Integer, ForeignKey('parent.id'), primary_key=True) + + assert "x" not in Child.__table__.c + + def test_honor_class_mro_two(self): + class HasXMixin(object): + @declared_attr + def x(self): + return Column(Integer) + + class Parent(HasXMixin, Base): + __tablename__ = 'parent' + id = Column(Integer, primary_key=True) + def x(self): + return "hi" + + class C(Parent): + __tablename__ = 'c' + id = Column(Integer, ForeignKey('parent.id'), primary_key=True) + + assert C().x() == 'hi' + + class DeclarativeMixinPropertyTest(DeclarativeTestBase): def test_column_property(self): diff --git a/test/sql/test_metadata.py b/test/sql/test_metadata.py index ea18229def..406db27a7a 100644 --- a/test/sql/test_metadata.py +++ b/test/sql/test_metadata.py @@ -195,6 +195,39 @@ class MetaDataTest(fixtures.TestBase, ComparesTables): fk1 = ForeignKeyConstraint(('foo', ), ('bar', ), table=t1) assert fk1 in t1.constraints + def test_fk_no_such_parent_col_error(self): + meta = MetaData() + a = Table('a', meta, Column('a', Integer)) + b = Table('b', meta, Column('b', Integer)) + + def go(): + a.append_constraint( + ForeignKeyConstraint(['x'], ['b.b']) + ) + assert_raises_message( + exc.ArgumentError, + "Can't create ForeignKeyConstraint on " + "table 'a': no column named 'x' is present.", + go + ) + + def test_fk_no_such_target_col_error(self): + meta = MetaData() + a = Table('a', meta, Column('a', Integer)) + b = Table('b', meta, Column('b', Integer)) + a.append_constraint( + ForeignKeyConstraint(['a'], ['b.x']) + ) + + def go(): + list(a.c.a.foreign_keys)[0].column + assert_raises_message( + exc.NoReferencedColumnError, + "Could not create ForeignKey 'b.x' on " + "table 'a': table 'b' has no column named 'x'", + go + ) + @testing.exclude('mysql', '<', (4, 1, 1), 'early types are squirrely') def test_to_metadata(self): meta = MetaData()