<p dir="auto"><strong>Migrated issue, originally created by MartinH (<a href="https://github.com/dwt">@dwt</a>)</strong></p>
<p dir="auto">I've found that the documentation for join at <a href="http://docs.sqlalchemy.org/en/rel_0_8/orm/query.html#sqlalchemy.orm.query.Query.join" rel="nofollow">http://docs.sqlalchemy.org/en/rel_0_8/orm/query.html#sqlalchemy.orm.query.Query.join</a> was lacking the information that the direction of the join, i.e. what is the left side and what is the righthand side of a join can be decided by the way you write the argument to the join call.</p>
<p dir="auto">So it makes a difference if you write <code class="notranslate">DBSession.query(User).join(Track.user)</code> - that will <code class="notranslate">select * from Track join User</code> or if you write <code class="notranslate">DBSession.query(User).join(User.tracks)</code> which will <code class="notranslate">select * from User join Track</code>.</p>
<p dir="auto">As far as my understanding of sql is that shouldn't make a difference, but when you switch from join to <code class="notranslate">outerjoin()</code>, suddenly this distinction becomes very important as it defines the lefthand side of the left outer join and someting you cannot override even with <code class="notranslate">select_from()</code>.</p>
<p dir="auto">And that is what cost us quite some time today to figure out - so a clearer mention of this in the documentation would be really helpfull.</p>
<p dir="auto">Hope this clarifies what I mean,</p>
<p dir="auto">Best Regards,<br>
Martin</p>
<p dir="auto"><strong>Migrated issue, originally created by Michael Bayer (<a href="https://github.com/zzzeek">@zzzeek</a>)</strong></p>
<div class="snippet-clipboard-content notranslate position-relative overflow-auto" data-snippet-clipboard-copy-content="from sqlalchemy import *
from sqlalchemy.orm import *
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.ext.declarative import declared_attr

Base = declarative_base()


class A(Base):
    __tablename__ = 'a'

    id = Column(Integer, primary_key=True)
    data = Column(String)
    bs = relationship(&quot;B&quot;)


class B(A):
    __tablename__ = 'b'

    id = Column(Integer, ForeignKey('a.id'), primary_key=True)


class C(A):
    __tablename__ = 'c'

    c_id = Column(Integer, ForeignKey('a.id'), primary_key=True)

from sqlalchemy.sql.elements import ClauseList

# ClauseList / Bundle are equivalent
print(ClauseList(A.id, C.c_id))
print(Bundle(&quot;pk&quot;, A.id, C.c_id).__clause_element__())

# w/ same name, they are not because it creates the clause list using *c without using exprs
print(ClauseList(A.id, B.id))
print(Bundle(&quot;pk&quot;, A.id, B.id).__clause_element__())
"><pre class="notranslate"><code class="notranslate">from sqlalchemy import *
from sqlalchemy.orm import *
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.ext.declarative import declared_attr

Base = declarative_base()


class A(Base):
    __tablename__ = 'a'

    id = Column(Integer, primary_key=True)
    data = Column(String)
    bs = relationship("B")


class B(A):
    __tablename__ = 'b'

    id = Column(Integer, ForeignKey('a.id'), primary_key=True)


class C(A):
    __tablename__ = 'c'

    c_id = Column(Integer, ForeignKey('a.id'), primary_key=True)

from sqlalchemy.sql.elements import ClauseList

# ClauseList / Bundle are equivalent
print(ClauseList(A.id, C.c_id))
print(Bundle("pk", A.id, C.c_id).__clause_element__())

# w/ same name, they are not because it creates the clause list using *c without using exprs
print(ClauseList(A.id, B.id))
print(Bundle("pk", A.id, B.id).__clause_element__())

</code></pre></div>
<p dir="auto">output:</p>
<div class="snippet-clipboard-content notranslate position-relative overflow-auto" data-snippet-clipboard-copy-content="a.id, c.c_id
a.id, c.c_id
a.id, b.id
b.id
"><pre class="notranslate"><code class="notranslate">a.id, c.c_id
a.id, c.c_id
a.id, b.id
b.id

</code></pre></div>
<p dir="auto">fix:</p>
<div class="snippet-clipboard-content notranslate position-relative overflow-auto" data-snippet-clipboard-copy-content="diff --git a/lib/sqlalchemy/orm/query.py b/lib/sqlalchemy/orm/query.py
index 98747c680..0b6b32a07 100644
--- a/lib/sqlalchemy/orm/query.py
+++ b/lib/sqlalchemy/orm/query.py
@@ -1738,7 +1738,7 @@ class Query(object):
                 self._group_by = False
                 return
 
-        criterion = list(chain(*[_orm_columns(c) for c in criterion]))
+        criterion = c = list(chain(*[_orm_columns(c) for c in criterion]))
         criterion = self._adapt_col_list(criterion)
 
         if self._group_by is False:
@@ -3964,7 +3964,7 @@ class Bundle(InspectionAttr):
         return cloned
 
     def __clause_element__(self):
-        return expression.ClauseList(group=False, *self.c)
+        return expression.ClauseList(group=False, *self.exprs)
 
     @property
     def clauses(self):
"><pre class="notranslate"><code class="notranslate">diff --git a/lib/sqlalchemy/orm/query.py b/lib/sqlalchemy/orm/query.py
index 98747c680..0b6b32a07 100644
--- a/lib/sqlalchemy/orm/query.py
+++ b/lib/sqlalchemy/orm/query.py
@@ -1738,7 +1738,7 @@ class Query(object):
                 self._group_by = False
                 return
 
-        criterion = list(chain(*[_orm_columns(c) for c in criterion]))
+        criterion = c = list(chain(*[_orm_columns(c) for c in criterion]))
         criterion = self._adapt_col_list(criterion)
 
         if self._group_by is False:
@@ -3964,7 +3964,7 @@ class Bundle(InspectionAttr):
         return cloned
 
     def __clause_element__(self):
-        return expression.ClauseList(group=False, *self.c)
+        return expression.ClauseList(group=False, *self.exprs)
 
     @property
     def clauses(self):

</code></pre></div>