[Sqlalchemy-tickets] Issue #4215: AttributeError for `Mutable.associate_with` when using a relation
Brought to you by:
zzzeek
From: David M. <iss...@bi...> - 2018-03-12 15:42:30
|
New issue 4215: AttributeError for `Mutable.associate_with` when using a relationship to non primary mapper https://bitbucket.org/zzzeek/sqlalchemy/issues/4215/attributeerror-for-mutableassociate_with David McDonald: ### Outline of the problem ### I have several models that have JSON fields. I also have a relationship to non primary mapper as following the SQLAlchemy docs. I tried to introduce mutability tracking to my JSON fields using https://github.com/edelooff/sqlalchemy-json. I ran into a problem with using `associate_with`. I've stripped back my code to remove the use of `sqlalchemy-json` and the problem still remains just when using `sqlalchemy.ext.mutable.Mutable`. `listen_for_type` seems to assume that all `mapper.column_attrs` are attributes existing on the original class. I believe this is a bug but feel free to point out if I am doing something incorrect. ### Requirements ### ``` Python 3.6.4 Postgres 9.5.3 SQLAlchemy 1.2.5 psycopg2 2.7.4 ``` ### Code example ### A full working example is available on Github for easy running - https://github.com/idavidmcdonald/sqlalchemy-bug-example ``` from sqlalchemy import Column, Integer, ForeignKey, join, create_engine from sqlalchemy.dialects.postgresql import JSON from sqlalchemy.ext.declarative import declarative_base from sqlalchemy.ext.mutable import Mutable from sqlalchemy.orm import mapper, relationship # The following example is taken from the docs regarding a non primary relationship mapper # http://docs.sqlalchemy.org/en/latest/orm/join_conditions.html#relationship-to-non-primary-mapper Base = declarative_base() class A(Base): __tablename__ = 'a' id = Column(Integer, primary_key=True) b_id = Column(ForeignKey('b.id')) a_json = Column(JSON) class B(Base): __tablename__ = 'b' id = Column(Integer, primary_key=True) b_json = Column(JSON) class C(Base): __tablename__ = 'c' id = Column(Integer, primary_key=True) a_id = Column(ForeignKey('a.id')) c_json = Column(JSON) class D(Base): __tablename__ = 'd' id = Column(Integer, primary_key=True) c_id = Column(ForeignKey('c.id')) b_id = Column(ForeignKey('b.id')) d_json = Column(JSON) # 1. set up the join() as a variable, so we can refer # to it in the mapping multiple times. j = join(B, D, D.b_id == B.id).join(C, C.id == D.c_id) # 2. Create a new mapper() to B, with non_primary=True. # Columns in the join with the same name must be # disambiguated within the mapping, using named properties. B_viacd = mapper(B, j, non_primary=True, properties={ "b_id": [j.c.b_id, j.c.d_b_id], "d_id": j.c.d_id }) A.b = relationship(B_viacd, primaryjoin=A.b_id == B_viacd.c.b_id) # Create DB engine = create_engine('postgresql://localhost/test_db') Base.metadata.create_all(engine) # If we associate `Mutable` with `JSON` then `A` can not be instantiated succesfully Mutable.associate_with(JSON) item1 = A() ``` ### Stack trace ### ``` Traceback (most recent call last): File "example.py", line 62, in <module> item1 = A() File "<string>", line 2, in __init__ File "/Users/davidmcdonald/sqlalchemy-bug-example/venv/lib/python3.6/site-packages/sqlalchemy/orm/instrumentation.py", line 391, in _new_state_if_none state = self._state_constructor(instance, self) File "/Users/davidmcdonald/sqlalchemy-bug-example/venv/lib/python3.6/site-packages/sqlalchemy/util/langhelpers.py", line 767, in __get__ obj.__dict__[self.__name__] = result = self.fget(obj) File "/Users/davidmcdonald/sqlalchemy-bug-example/venv/lib/python3.6/site-packages/sqlalchemy/orm/instrumentation.py", line 221, in _state_constructor self.dispatch.first_init(self, self.class_) File "/Users/davidmcdonald/sqlalchemy-bug-example/venv/lib/python3.6/site-packages/sqlalchemy/event/attr.py", line 284, in __call__ fn(*args, **kw) File "/Users/davidmcdonald/sqlalchemy-bug-example/venv/lib/python3.6/site-packages/sqlalchemy/orm/mapper.py", line 3139, in _event_on_first_init configure_mappers() File "/Users/davidmcdonald/sqlalchemy-bug-example/venv/lib/python3.6/site-packages/sqlalchemy/orm/mapper.py", line 3032, in configure_mappers mapper, mapper.class_) File "/Users/davidmcdonald/sqlalchemy-bug-example/venv/lib/python3.6/site-packages/sqlalchemy/event/attr.py", line 246, in __call__ fn(*args, **kw) File "/Users/davidmcdonald/sqlalchemy-bug-example/venv/lib/python3.6/site-packages/sqlalchemy/orm/events.py", line 621, in wrap fn(*arg, **kw) File "/Users/davidmcdonald/sqlalchemy-bug-example/venv/lib/python3.6/site-packages/sqlalchemy/ext/mutable.py", line 571, in listen_for_type cls.associate_with_attribute(getattr(class_, prop.key)) AttributeError: type object 'B' has no attribute 'd_json' ``` |