mixin_column to provide more context for declarative mixins?

Issue #3149 resolved
sowingsadness created an issue
# -*- coding: utf-8 -*-
__author__ = 'SowingSadness'

from sqlalchemy import Column, String, Integer, ForeignKey
from sqlalchemy import create_engine
from sqlalchemy.ext.declarative import declared_attr
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.ext.declarative import has_inherited_table
from sqlalchemy.orm import scoped_session, sessionmaker
from sqlalchemy.orm import relationship


Base = declarative_base()
DBSession = scoped_session(sessionmaker())
"""@type : sqlalchemy.orm.session.Session|sqlalchemy.orm.session.SessionTransaction"""


class VehicleModel(Base):
    __tablename__ = "vehicle_model"
    id = Column(Integer, primary_key=True)
    name = Column(String(20))


class VehicleInfo(object):
    vehicle_plate_region = Column(String(5))
    vehicle_plate = Column(String(20))

    @declared_attr
    def vehicle_model_id(cls):
        return Column(Integer, ForeignKey("vehicle_model.id"))

    @declared_attr
    def vehicle_model(cls):
        return relationship(VehicleModel, foreign_keys=[cls.vehicle_model_id])


class Vehicle(VehicleInfo, Base):
    __tablename__ = 'vehicle'
    id = Column(Integer, primary_key=True)


def main():
    engine = create_engine('postgresql+psycopg2://postgres@localhost:5432/t1')
    DBSession.configure(bind=engine)
    Base.metadata.bind = engine

    Base.metadata.drop_all()
    Base.metadata.create_all(checkfirst=True)

    vm = VehicleModel()
    vehicle = Vehicle(vehicle_model=vm)
    DBSession.add(vm)
    DBSession.add(vehicle)
    DBSession.commit()


if __name__ == '__main__':
    main()
C:\Users\Kir\Documents\Work\pyramid_p27\Scripts\pythonw.exe C:/Users/Kir/Documents/Work/sqlalchemy-test/main.py
Traceback (most recent call last):
  File "C:/Users/Kir/Documents/Work/sqlalchemy-test/main.py", line 58, in <module>
    main()
  File "C:/Users/Kir/Documents/Work/sqlalchemy-test/main.py", line 50, in main
    vm = VehicleModel()
  File "<string>", line 2, in __init__
  File "C:\Users\Kir\Documents\Work\pyramid_p27\lib\site-packages\sqlalchemy\orm\instrumentation.py", line 324, in _new_state_if_none
    state = self._state_constructor(instance, self)
  File "C:\Users\Kir\Documents\Work\pyramid_p27\lib\site-packages\sqlalchemy\util\langhelpers.py", line 725, in __get__
    obj.__dict__[self.__name__] = result = self.fget(obj)
  File "C:\Users\Kir\Documents\Work\pyramid_p27\lib\site-packages\sqlalchemy\orm\instrumentation.py", line 158, in _state_constructor
    self.dispatch.first_init(self, self.class_)
  File "C:\Users\Kir\Documents\Work\pyramid_p27\lib\site-packages\sqlalchemy\event\attr.py", line 260, in __call__
    fn(*args, **kw)
  File "C:\Users\Kir\Documents\Work\pyramid_p27\lib\site-packages\sqlalchemy\orm\mapper.py", line 2687, in _event_on_first_init
    configure_mappers()
  File "C:\Users\Kir\Documents\Work\pyramid_p27\lib\site-packages\sqlalchemy\orm\mapper.py", line 2583, in configure_mappers
    mapper._post_configure_properties()
  File "C:\Users\Kir\Documents\Work\pyramid_p27\lib\site-packages\sqlalchemy\orm\mapper.py", line 1688, in _post_configure_properties
    prop.init()
  File "C:\Users\Kir\Documents\Work\pyramid_p27\lib\site-packages\sqlalchemy\orm\interfaces.py", line 144, in init
    self.do_init()
  File "C:\Users\Kir\Documents\Work\pyramid_p27\lib\site-packages\sqlalchemy\orm\relationships.py", line 1550, in do_init
    self._setup_join_conditions()
  File "C:\Users\Kir\Documents\Work\pyramid_p27\lib\site-packages\sqlalchemy\orm\relationships.py", line 1624, in _setup_join_conditions
    can_be_synced_fn=self._columns_are_mapped
  File "C:\Users\Kir\Documents\Work\pyramid_p27\lib\site-packages\sqlalchemy\orm\relationships.py", line 1892, in __init__
    self._determine_joins()
  File "C:\Users\Kir\Documents\Work\pyramid_p27\lib\site-packages\sqlalchemy\orm\relationships.py", line 1996, in _determine_joins
    "specify a 'primaryjoin' expression." % self.prop)
sqlalchemy.exc.NoForeignKeysError: Could not determine join condition between parent/child tables on relationship Vehicle.vehicle_model - there are no foreign keys linking these tables.  Ensure that referencing columns are associated with a ForeignKey or ForeignKeyConstraint, or specify a 'primaryjoin' expression.

Process finished with exit code 1

I try to use code in https://bitbucket.org/zzzeek/sqlalchemy/issue/2471/add-example-of-declared_attr-columns but result is same.

Comments (8)

  1. Mike Bayer repo owner

    when you call this:

        @declared_attr
        def vehicle_model(cls):
            return relationship(VehicleModel, foreign_keys=[cls.vehicle_model_id])
    

    you are invoking the VehicleInfo.vehicle_model_id method again, creating a new Column() object that is unrelated to anything else and throws it off.

    Either remove it (because you have ForeignKey already):

        @declared_attr
        def vehicle_model(cls):
            return relationship(VehicleModel)
    

    or defer the call:

        @declared_attr
        def vehicle_model(cls):
            return relationship(VehicleModel, foreign_keys=lambda: [cls.vehicle_model_id])
    
  2. Mike Bayer repo owner

    see if a new declared system to complement #2952 is feasible here, a "mixin column" that has more insight into how it is used:

        class VehicleInfo(object):
            vehicle_plate_region = Column(String(5))
            vehicle_plate = Column(String(20))
    
    
    
        @mixin_column  # possibly uses __clause_element__, cls level caching, other
                                    # integrations with declarative.base
        def vehicle_model_id(cls):
            return Column(Integer, ForeignKey("vehicle_model.id"))
    
        @declared_attr
        def vehicle_model(cls):
            return relationship(VehicleModel, foreign_keys=[cls.vehicle_model_id])
    
  3. Log in to comment