Skip to content

mixin_column to provide more context for declarative mixins? #3149

Closed
@sqlalchemy-bot

Description

@sqlalchemy-bot
Collaborator

Migrated issue, originally created by sowingsadness (@SowingSadness)

# -*- 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.

Activity

sqlalchemy-bot

sqlalchemy-bot commented on Jul 31, 2014

@sqlalchemy-bot
CollaboratorAuthor

Michael Bayer (@zzzeek) wrote:

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])

sqlalchemy-bot

sqlalchemy-bot commented on Jul 31, 2014

@sqlalchemy-bot
CollaboratorAuthor

Changes by Michael Bayer (@zzzeek):

  • changed status to closed
sqlalchemy-bot

sqlalchemy-bot commented on Jul 31, 2014

@sqlalchemy-bot
CollaboratorAuthor

Michael Bayer (@zzzeek) wrote:

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])

sqlalchemy-bot

sqlalchemy-bot commented on Jul 31, 2014

@sqlalchemy-bot
CollaboratorAuthor

Changes by Michael Bayer (@zzzeek):

  • removed labels: bug
  • added labels: feature
sqlalchemy-bot

sqlalchemy-bot commented on Jul 31, 2014

@sqlalchemy-bot
CollaboratorAuthor

Changes by Michael Bayer (@zzzeek):

  • set milestone to "1.0"
sqlalchemy-bot

sqlalchemy-bot commented on Jul 31, 2014

@sqlalchemy-bot
CollaboratorAuthor

Changes by Michael Bayer (@zzzeek):

  • changed title from "declared_attr do not work with mixin relationship " to "mixin_column to provide more context for declarati"
sqlalchemy-bot

sqlalchemy-bot commented on Jul 31, 2014

@sqlalchemy-bot
CollaboratorAuthor

Changes by Michael Bayer (@zzzeek):

  • changed status to reopened
sqlalchemy-bot

sqlalchemy-bot commented on Jul 31, 2014

@sqlalchemy-bot
CollaboratorAuthor

Michael Bayer (@zzzeek) wrote:

Duplicate of #3150.

sqlalchemy-bot

sqlalchemy-bot commented on Jul 31, 2014

@sqlalchemy-bot
CollaboratorAuthor

Changes by Michael Bayer (@zzzeek):

  • added labels: duplicate
  • changed status to closed
sqlalchemy-bot

sqlalchemy-bot commented on Jul 31, 2014

@sqlalchemy-bot
CollaboratorAuthor

Michael Bayer (@zzzeek) wrote:

thanks for pushing me on this i think #3150 will be a great addition that meets many unsolved use cases

sqlalchemy-bot

sqlalchemy-bot commented on Aug 6, 2014

@sqlalchemy-bot
CollaboratorAuthor

sowingsadness (@SowingSadness) wrote:

Excelent. Because existing behavior of declared_attr is so hard and not obviously.

sqlalchemy-bot

sqlalchemy-bot commented on Sep 26, 2014

@sqlalchemy-bot
CollaboratorAuthor
sqlalchemy-bot

sqlalchemy-bot commented on Sep 26, 2014

@sqlalchemy-bot
CollaboratorAuthor

Changes by Michael Bayer (@zzzeek):

  • removed labels: duplicate

3 remaining items

Loading
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    declarativehas to do with the declarative API, scanning classes and mixins for attributes to be mappedfeature

    Type

    No type

    Projects

    No projects

    Relationships

    None yet

      Development

      No branches or pull requests

        Participants

        @sqlalchemy-bot

        Issue actions

          mixin_column to provide more context for declarative mixins? · Issue #3149 · sqlalchemy/sqlalchemy