--- /dev/null
+.. change::
+ :tags: bug, orm
+ :tickets: 9537
+
+ Expanded the warning emitted when a plain :func:`_sql.column` object is
+ present in a Declarative mapping to include any arbitrary SQL expression
+ that is not declared within an appropriate property type such as
+ :func:`_orm.column_property`, :func:`_orm.deferred`, etc. These attributes
+ are otherwise not mapped at all and remain unchanged within the class
+ dictionary. As it seems likely that such an expression is usually not
+ what's intended, this case now warns for all such otherwise ignored
+ expressions, rather than just the :func:`_sql.column` case.
def _warn_for_decl_attributes(
self, cls: Type[Any], key: str, c: Any
) -> None:
- if isinstance(c, expression.ColumnClause):
+ if isinstance(c, expression.ColumnElement):
util.warn(
f"Attribute '{key}' on class {cls} appears to "
- "be a non-schema 'sqlalchemy.sql.column()' "
- "object; this won't be part of the declarative mapping"
+ "be a non-schema SQLAlchemy expression "
+ "object; this won't be part of the declarative mapping. "
+ "To map arbitrary expressions, use ``column_property()`` "
+ "or a similar function such as ``deferred()``, "
+ "``query_expression()`` etc. "
)
def _produce_column_copies(
class_mapper(Bar).get_property("some_data").columns[0] is t.c.data
)
- def test_lower_case_c_column_warning(self):
+ def test_non_sql_expression_warning_one(self):
with assertions.expect_warnings(
r"Attribute 'x' on class <class .*Foo.* appears to be a "
- r"non-schema 'sqlalchemy.sql.column\(\)' object; "
+ r"non-schema SQLAlchemy expression object; "
):
class Foo(Base):
x = sa.sql.expression.column(Integer)
y = Column(Integer)
+ def test_non_sql_expression_warning_two(self):
class MyMixin:
x = sa.sql.expression.column(Integer)
y = Column(Integer)
with assertions.expect_warnings(
r"Attribute 'x' on class <class .*MyMixin.* appears to be a "
- r"non-schema 'sqlalchemy.sql.column\(\)' object; "
+ r"non-schema SQLAlchemy expression object; "
):
class Foo2(MyMixin, Base):
id = Column(Integer, primary_key=True)
+ def test_non_sql_expression_warning_three(self):
with assertions.expect_warnings(
r"Attribute 'x' on class <class .*Foo3.* appears to be a "
- r"non-schema 'sqlalchemy.sql.column\(\)' object; "
+ r"non-schema SQLAlchemy expression object; "
):
class Foo3(Base):
y = Column(Integer)
+ def test_non_sql_expression_warning_four(self):
with assertions.expect_warnings(
r"Attribute 'x' on class <class .*Foo4.* appears to be a "
- r"non-schema 'sqlalchemy.sql.column\(\)' object; "
+ r"non-schema SQLAlchemy expression object; "
):
class MyMixin2:
id = Column(Integer, primary_key=True)
+ def test_non_sql_expression_warning_five(self):
+
+ # test for #9537
+ with assertions.expect_warnings(
+ r"Attribute 'x' on class <class .*Foo5.* appears to be a "
+ r"non-schema SQLAlchemy expression object; ",
+ r"Attribute 'y' on class <class .*Foo5.* appears to be a "
+ r"non-schema SQLAlchemy expression object; ",
+ raise_on_any_unexpected=True,
+ ):
+
+ class Foo5(Base):
+ __tablename__ = "foo5"
+
+ id = Column(Integer, primary_key=True)
+ x = Column("x", String()).collate("some collation")
+ y = Column("y", Integer) + 5
+ z = "im not a sqlalchemy thing"
+
def test_column_named_twice(self):
with expect_warnings(
"On class 'Foo', Column object 'x' named directly multiple "