简体   繁体   中英

sqlalchemy: joining to the same table multiple times using declarative and reflection

Here is a trimmed down version of my problem:

Consider that I have two tables: 'procedure' and 'role'.

role has fields: (role_uid, role_name)

procedure has fields: (procedure_uid, procedure_name, inform_role_uid, consult_role_uid)

So 'role' has two one-to-many relationships with 'procedure'.

Some code:

class Role(Base):
    __tablename__ = "role"
    __table_args__ = ({'autoload':True, 'useexisting': True})

class Procedure(Base):
    __tablename__ = "procedure"
    __table_args__ = (sqlalchemy.ForeignKeyConstraint(['consult_role_uid','inform_role_uid'],['role.role_uid', 'role.role_uid']),
        {'autoload':True, 'useexisting': True})

Procedure.consult_role = sqlalchemy.orm.relationship(Role,
 primaryjoin="Procedure.consult_role_uid==Role.role_uid", foreign_keys=Role.role_uid)
Procedure.inform_role = sqlalchemy.orm.relationship(Role,
 primaryjoin="Procedure.inform_role_uid==Role.role_uid", foreign_keys=Role.role_uid)

consult_role = sqlalchemy.orm.aliased(Role, name="consult_role")
inform_role = sqlalchemy.orm.aliased(Role, name="inform_role")

query = session.query(
    Procedure.procedure_name, 
    consult_role.role_name.label("consult_role_name"),
    inform_role.role_name.label("inform_role_name")).join(consult_role, inform_role)

This produces the following SQL:

SELECT 
  `procedure`.procedure_name AS procedure_procedure_name, 
  consult_role.role_name AS consult_role_name, 
  inform_role.role_name AS inform_role_name 
FROM 
  `procedure` 
  INNER JOIN role AS consult_role 
    ON consult_role.role_uid = `procedure`.consult_role_uid
      AND consult_role.role_uid = `procedure`.inform_role_uid 
  INNER JOIN role AS inform_role 
    ON inform_role.role_uid = `procedure`.consult_role_uid 
      AND inform_role.role_uid = `procedure`.inform_role_uid

As you can see, I had no intention of EACH of the inner joins to join on both the fields. Why does it seem to be ignoring my 'primaryjoin' argument?

This:

sqlalchemy.ForeignKeyConstraint(['consult_role_uid','inform_role_uid'],['role.role_uid', 'role.role_uid'])

Is roughly how you say that the relationship between two tables is through both attributes, as if the referant has a composite primary key. You need to specify ForeignKey twice if you want to have two foreign key references.

class Procedure(Base):
    __tablename__ = "procedure"
    __table_args__ = (
        sqlalchemy.ForeignKeyConstraint(['consult_role_uid'],['role.role_uid']),
        sqlalchemy.ForeignKeyConstraint(['inform_role_uid'],['role.role_uid']),
        {'autoload':True, 'useexisting': True})

So for completeness, here is the fixed code to the problem above. I added two ForeignKeyContstaints and I also had to specify which relationship to use in the join.

class Role(Base):
    __tablename__ = "role"
    __table_args__ = ({'autoload':True, 'useexisting': True})


class Procedure(Base):
    __tablename__ = "procedure"
    __table_args__ = (
        sqlalchemy.ForeignKeyConstraint(['consult_role_uid'], ['role.role_uid']),
        sqlalchemy.ForeignKeyConstraint(['inform_role_uid'], ['role.role_uid']),
        {'autoload':True, 'useexisting': True})

Procedure.consult_role = sqlalchemy.orm.relationship(Role,
 primaryjoin="Procedure.consult_role_uid==Role.role_uid", foreign_keys=Role.role_uid)
Procedure.inform_role = sqlalchemy.orm.relationship(Role,
 primaryjoin="Procedure.inform_role_uid==Role.role_uid", foreign_keys=Role.role_uid)

consult_role = sqlalchemy.orm.aliased(Role, name="consult_role")
inform_role = sqlalchemy.orm.aliased(Role, name="inform_role")

query = session.query(
    Procedure.procedure_name, 
    consult_role.role_name.label("consult_role_name"),
    inform_role.role_name.label("inform_role_name")).join((consult_role, Procedure.consult_role), (inform_role, Procedure.inform_role))

This produced the following correct SQL:

SELECT 
  `procedure`.procedure_name AS procedure_procedure_name, 
  consult_role.role_name AS consult_role_name, 
  inform_role.role_name AS inform_role_name 
FROM 
  `procedure` 
  INNER JOIN role AS consult_role ON `procedure`.consult_role_uid = consult_role.role_uid     
  INNER JOIN role AS inform_role ON `procedure`.inform_role_uid = inform_role.role_uid

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM