.. changelog::
:version: 0.9.2
+ .. change::
+ :tags: bug, orm
+ :tickets: 2918
+
+ Fixed an 0.9 regression where the automatic aliasing applied by
+ :class:`.Query` and in other situations where selects or joins
+ were aliased (such as joined table inheritance) could fail if a
+ user-defined :class:`.Column` subclass were used in the expression.
+ In this case, the subclass would fail to propagate ORM-specific
+ "annotations" along needed by the adaptation. The "expression
+ annotations" system has been corrected to account for this case.
+
.. change::
:tags: feature, orm
have been applied within this query."""
adapters = []
-
# do we adapt all expression elements or only those
# tagged as 'ORM' constructs ?
orm_only = getattr(self, '_orm_only_adapt', orm_only)
return cls
elif cls in annotated_classes:
return annotated_classes[cls]
+
+ for super_ in cls.__mro__:
+ # check if an Annotated subclass more specific than
+ # the given base_cls is already registered, such
+ # as AnnotatedColumnElement.
+ if super_ in annotated_classes:
+ base_cls = annotated_classes[super_]
+ break
+
annotated_classes[cls] = anno_cls = type(
- "Annotated%s" % cls.__name__,
- (base_cls, cls), {})
+ "Annotated%s" % cls.__name__,
+ (base_cls, cls), {})
globals()["Annotated%s" % cls.__name__] = anno_cls
return anno_cls
q.all(),
[(b, True)]
)
+
+class ColSubclassTest(fixtures.DeclarativeMappedTest, testing.AssertsCompiledSQL):
+ """Test [ticket:2918]'s test case."""
+
+ run_create_tables = None
+ __dialect__ = 'default'
+
+ @classmethod
+ def setup_classes(cls):
+ from sqlalchemy.schema import Column
+ Base = cls.DeclarativeBasic
+
+ class A(Base):
+ __tablename__ = 'a'
+
+ id = Column(Integer, primary_key=True)
+
+ class MySpecialColumn(Column):
+ pass
+
+ class B(A):
+ __tablename__ = 'b'
+
+ id = Column(ForeignKey('a.id'), primary_key=True)
+ x = MySpecialColumn(String)
+
+ def test_polymorphic_adaptation(self):
+ A, B = self.classes.A, self.classes.B
+
+ s = Session()
+ self.assert_compile(
+ s.query(A).join(B).filter(B.x == 'test'),
+ "SELECT a.id AS a_id FROM a JOIN "
+ "(a AS a_1 JOIN b AS b_1 ON a_1.id = b_1.id) "
+ "ON a.id = b_1.id WHERE b_1.x = :x_1"
+ )
annot_2 = s1._annotate({})
assert isinstance(annot_2.c.foo, Column)
+ def test_custom_construction_correct_anno_subclass(self):
+ # [ticket:2918]
+ from sqlalchemy.schema import Column
+ from sqlalchemy.sql.elements import AnnotatedColumnElement
+ class MyColumn(Column):
+ pass
+
+ assert isinstance(
+ MyColumn('x', Integer)._annotate({"foo": "bar"}),
+ AnnotatedColumnElement)
+
+ def test_custom_construction_correct_anno_expr(self):
+ # [ticket:2918]
+ from sqlalchemy.schema import Column
+ class MyColumn(Column):
+ pass
+
+ col = MyColumn('x', Integer)
+ binary_1 = col == 5
+ col_anno = MyColumn('x', Integer)._annotate({"foo": "bar"})
+ binary_2 = col_anno == 5
+ eq_(binary_2.left._annotations, {"foo": "bar"})
+
def test_annotated_corresponding_column(self):
table1 = table('table1', column("col1"))