it's clear that the deferredreflection sees the word "join" as the name of a 
table to reflect from the "secondary" argument. Also, your test reproduces the 
error in all versions of SQLAlchemy, I can't find any version that has 
DeferredReflection which does not behave identically; 1.2, 1.1, 1.0, 0.9, they 
all do the same thing. So if you saw something break between 1.2 and 1.3, it's 
something else, this happens every time.

The test case can be made to work by using a lambda instead of a string, which 
means SQLAlchemy can do less guessing as to what the name of the "secondary" 
table might be (in this case nothing):

from sqlalchemy import and_, join

# ...

class ...

 d = relationship(
 "D",
 secondary=lambda: join(B, D, B.d_id == D.id).join(C, C.d_id == D.id),
 primaryjoin=lambda: and_(A.b_id == B.id, A.id == C.a_id),
 secondaryjoin=lambda: D.id == B.d_id,
 uselist=True,
 )



On Wed, Oct 30, 2019, at 6:35 PM, Cory Virok wrote:
> I recently upgraded from SQLAlchemy 1.2 to 1.3 and one of the relationships I 
> have no longer works. I'm trying to reflect an existing schema and provide a 
> base class that provides some helper methods to all of my ORM classes. I'm 
> also trying to defer reflection to after the ORM classes are prepared.
> 
> The error I'm seeing is:
> 
> File "scratch.py", line 98, in <module>
>  a = session.query(A)
> File "lib/python2.7/site-packages/sqlalchemy/orm/session.py", line 1544, in 
> query
> return self._query_cls(entities, self, **kwargs)
> File "lib/python2.7/site-packages/sqlalchemy/orm/query.py", line 168, in 
> __init__
> self._set_entities(entities)
> File "lib/python2.7/site-packages/sqlalchemy/orm/query.py", line 200, in 
> _set_entities
> self._set_entity_selectables(self._entities)
> File "lib/python2.7/site-packages/sqlalchemy/orm/query.py", line 231, in 
> _set_entity_selectables
>  ent.setup_entity(*d[entity])
> File "lib/python2.7/site-packages/sqlalchemy/orm/query.py", line 4121, in 
> setup_entity
> self._with_polymorphic = ext_info.with_polymorphic_mappers
> File "lib/python2.7/site-packages/sqlalchemy/util/langhelpers.py", line 855, 
> in __get__
>  obj.__dict__[self.__name__] = result = self.fget(obj)
> File "lib/python2.7/site-packages/sqlalchemy/orm/mapper.py", line 2135, in 
> _with_polymorphic_mappers
>  configure_mappers()
> File "lib/python2.7/site-packages/sqlalchemy/orm/mapper.py", line 3248, in 
> configure_mappers
>  mapper._post_configure_properties()
> File "lib/python2.7/site-packages/sqlalchemy/orm/mapper.py", line 1947, in 
> _post_configure_properties
>  prop.init()
> File "lib/python2.7/site-packages/sqlalchemy/orm/interfaces.py", line 196, in 
> init
> self.do_init()
> File "lib/python2.7/site-packages/sqlalchemy/orm/relationships.py", line 
> 1860, in do_init
> self._process_dependent_arguments()
> File "lib/python2.7/site-packages/sqlalchemy/orm/relationships.py", line 
> 1889, in _process_dependent_arguments
>  setattr(self, attr, attr_value())
> File "lib/python2.7/site-packages/sqlalchemy/ext/declarative/clsregistry.py", 
> line 294, in __call__
>  x = eval(self.arg, globals(), self._dict)
> File "<string>", line 1, in <module>
> File "lib/python2.7/site-packages/sqlalchemy/util/_collections.py", line 734, 
> in __missing__
> self[key] = val = self.creator(key)
> File "lib/python2.7/site-packages/sqlalchemy/ext/declarative/clsregistry.py", 
> line 286, in _access_cls
>  value = resolv(key)
> File "lib/python2.7/site-packages/sqlalchemy/ext/declarative/api.py", line 
> 778, in _resolve
>  cls._reflect_table(t1, engine)
> File "lib/python2.7/site-packages/sqlalchemy/ext/declarative/api.py", line 
> 811, in _reflect_table
>  schema=table.schema,
> File "<string>", line 2, in __new__
> File "lib/python2.7/site-packages/sqlalchemy/util/deprecations.py", line 128, 
> in warned
> return fn(*args, **kwargs)
> File "lib/python2.7/site-packages/sqlalchemy/sql/schema.py", line 492, in 
> __new__
>  table._init_existing(*args, **kw)
> File "lib/python2.7/site-packages/sqlalchemy/sql/schema.py", line 708, in 
> _init_existing
>  _extend_on=_extend_on,
> File "lib/python2.7/site-packages/sqlalchemy/sql/schema.py", line 619, in 
> _autoload
>  _extend_on=_extend_on,
> File "lib/python2.7/site-packages/sqlalchemy/engine/base.py", line 2160, in 
> run_callable
> return conn.run_callable(callable_, *args, **kwargs)
> File "lib/python2.7/site-packages/sqlalchemy/engine/base.py", line 1612, in 
> run_callable
> return callable_(self, *args, **kwargs)
> File "lib/python2.7/site-packages/sqlalchemy/engine/default.py", line 459, in 
> reflecttable
>  table, include_columns, exclude_columns, resolve_fks, **opts
> File "lib/python2.7/site-packages/sqlalchemy/engine/reflection.py", line 629, 
> in reflecttable
>  table_name, schema, **table.dialect_kwargs
> File "lib/python2.7/site-packages/sqlalchemy/engine/reflection.py", line 314, 
> in get_table_options
> self.bind, table_name, schema, info_cache=self.info_cache, **kw
> File "<string>", line 2, in get_table_options
> File "lib/python2.7/site-packages/sqlalchemy/engine/reflection.py", line 56, 
> in cache
>  ret = fn(self, con, *args, **kw)
> File "lib/python2.7/site-packages/sqlalchemy/dialects/mysql/base.py", line 
> 2499, in get_table_options
>  connection, table_name, schema, **kw
> File "lib/python2.7/site-packages/sqlalchemy/dialects/mysql/base.py", line 
> 2745, in _parsed_state_or_create
>  info_cache=kw.get("info_cache", None),
> File "<string>", line 2, in _setup_parser
> File "lib/python2.7/site-packages/sqlalchemy/engine/reflection.py", line 56, 
> in cache
>  ret = fn(self, con, *args, **kw)
> File "lib/python2.7/site-packages/sqlalchemy/dialects/mysql/base.py", line 
> 2773, in _setup_parser
>  connection, None, charset, full_name=full_name
> File "lib/python2.7/site-packages/sqlalchemy/dialects/mysql/base.py", line 
> 2876, in _show_create_table
> raise exc.NoSuchTableError(full_name)
> sqlalchemy.exc.NoSuchTableError: `join`
> 
> 
> Here's a repro based on the example from the docs 
> (https://docs.sqlalchemy.org/en/13/orm/join_conditions.html?highlight=secondary%20composite#composite-secondary-joins)
> 
> from sqlalchemy import Column, Integer, ForeignKey, create_engine
> from sqlalchemy.ext.declarative import declarative_base, DeferredReflection
> from sqlalchemy.orm import relationship, Session
> 
> _Base = declarative_base()
> 
> 
> class Base(_Base, DeferredReflection):
>  __abstract__ = True
> 
> 
> class A(Base):
>  __tablename__ = 'a'
> 
>  id = Column(Integer, primary_key=True)
>  b_id = Column(ForeignKey('b.id'))
> 
>  d = relationship("D",
>  secondary="join(B, D, B.d_id == D.id)."
>  "join(C, C.d_id == D.id)",
>  primaryjoin="and_(A.b_id == B.id, A.id == C.a_id)",
>  secondaryjoin="D.id == B.d_id",
>  uselist=True
>  )
> 
> 
> class B(Base):
>  __tablename__ = 'b'
> 
>  id = Column(Integer, primary_key=True)
>  d_id = Column(ForeignKey('d.id'))
> 
> 
> class C(Base):
>  __tablename__ = 'c'
> 
>  id = Column(Integer, primary_key=True)
>  a_id = Column(ForeignKey('a.id'))
>  d_id = Column(ForeignKey('d.id'))
> 
> 
> class D(Base):
>  __tablename__ = 'd'
> 
>  id = Column(Integer, primary_key=True)
> 
> 
> engine = 
> create_engine("mysql+mysqldb://[email protected]:3306/testing?charset=utf8mb4&binary_prefix=true",
>  echo=True)
> engine.execute("""
> DROP DATABASE testing;
> CREATE DATABASE testing;
> USE testing;
> 
> CREATE TABLE d (
>  id int unsigned auto_increment,
>  primary key (id)
> ) engine=innodb;
> 
> CREATE TABLE b (
>  id int unsigned auto_increment,
>  d_id int unsigned default null,
>  primary key (id),
>  foreign key (d_id) references `d` (id)
> ) engine=innodb;
> 
> CREATE TABLE a (
>  id int unsigned auto_increment,
>  b_id int unsigned default null,
>  primary key (id),
>  foreign key (b_id) references `b` (id)
> ) engine=innodb;
> 
> CREATE TABLE c (
>  id int unsigned auto_increment,
>  a_id int unsigned default null,
>  d_id int unsigned default null,
>  primary key (id),
>  foreign key (a_id) references `a` (id),
>  foreign key (d_id) references `d` (id)
> ) engine=innodb;
> 
> INSERT INTO d (id) values (1), (2), (3);
> INSERT INTO b (d_id) values (1), (1), (2);
> INSERT INTO a (b_id) values (2), (3), (1);
> INSERT INTO c (a_id, d_id) values (1, 1), (1, 2), (2, 3);
> """)
> 
> Base.metadata.reflect(bind=engine)
> 
> A.prepare(engine)
> B.prepare(engine)
> C.prepare(engine)
> D.prepare(engine)
> 
> engine = 
> create_engine("mysql+mysqldb://[email protected]:3306/testing?charset=utf8mb4&binary_prefix=true",
>  echo=True)
> session = Session(engine)
> 
> a = session.query(A)
> for _a in a:
>  print(a)
>  if _a.d:
>  print(list(_a.d))
> 
> I've verified that the error only happens when I defer reflection. I.e. I 
> remove the DefferedReflection base class from _Base and I comment out the 
> A.prepare(engine)... lines.
> 
> Has anyone else run into this?
> 
> Thanks!
> 

> --
>  SQLAlchemy - 
>  The Python SQL Toolkit and Object Relational Mapper
> 
> http://www.sqlalchemy.org/
> 
>  To post example code, please provide an MCVE: Minimal, Complete, and 
> Verifiable Example. See http://stackoverflow.com/help/mcve for a full 
> description.
>  --- 
>  You received this message because you are subscribed to the Google Groups 
> "sqlalchemy" group.
>  To unsubscribe from this group and stop receiving emails from it, send an 
> email to [email protected].
>  To view this discussion on the web visit 
> https://groups.google.com/d/msgid/sqlalchemy/b6e3e875-3d17-49c0-b5f6-70d7215ea3f0%40googlegroups.com
>  
> <https://groups.google.com/d/msgid/sqlalchemy/b6e3e875-3d17-49c0-b5f6-70d7215ea3f0%40googlegroups.com?utm_medium=email&utm_source=footer>.

-- 
SQLAlchemy - 
The Python SQL Toolkit and Object Relational Mapper

http://www.sqlalchemy.org/

To post example code, please provide an MCVE: Minimal, Complete, and Verifiable 
Example.  See  http://stackoverflow.com/help/mcve for a full description.
--- 
You received this message because you are subscribed to the Google Groups 
"sqlalchemy" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to [email protected].
To view this discussion on the web visit 
https://groups.google.com/d/msgid/sqlalchemy/877f6b3b-9b14-4014-a736-1de61c74b9c8%40www.fastmail.com.

Reply via email to