Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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?

like image 615
Edward van Kuik Avatar asked Jul 25 '11 16:07

Edward van Kuik


1 Answers

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
like image 190
Edward van Kuik Avatar answered Nov 14 '22 21:11

Edward van Kuik