SQLAlchemy se une a claves externas compuestas (con matraz-sqlalchemy)

Estoy tratando de entender cómo hacer uniones con claves externas compuestas en SQLAlchemy y mis bashs de hacer esto están fallando.

Tengo las siguientes clases de modelos en mi modelo de juguete (estoy usando Flask-SQLAlchemy, pero no estoy seguro de que esto tenga algo que ver con el problema):

# coding=utf-8 from flask import Flask from flask.ext.sqlalchemy import SQLAlchemy app = Flask(__name__) app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:////tmp/test.db' db = SQLAlchemy(app) class Asset(db.Model): __tablename__ = 'asset' user = db.Column('usuario', db.Integer, primary_key=True) profile = db.Column('perfil', db.Integer, primary_key=True) name = db.Column('nome', db.Unicode(255)) def __str__(self): return u"Asset({}, {}, {})".format(self.user, self.profile, self.name).encode('utf-8') class Zabumba(db.Model): __tablename__ = 'zabumba' db.ForeignKeyConstraint( ['asset.user', 'asset.profile'], ['zabumba.user', 'zabumba.profile'] ) user = db.Column('usuario', db.Integer, primary_key=True) profile = db.Column('perfil', db.Integer, primary_key=True) count = db.Column('qtdade', db.Integer) def __str__(self): return u"Zabumba({}, {}, {})".format(self.user, self.profile, self.count).encode('utf-8') 

Luego llené la base de datos con algunos datos falsos:

 db.drop_all() db.create_all() db.session.add(Asset(user=1, profile=1, name=u"Pafúncio")) db.session.add(Asset(user=1, profile=2, name=u"Skavurska")) db.session.add(Asset(user=2, profile=1, name=u"Ermengarda")) db.session.add(Zabumba(user=1, profile=1, count=10)) db.session.add(Zabumba(user=1, profile=2, count=11)) db.session.add(Zabumba(user=2, profile=1, count=12)) db.session.commit() 

Y probé la siguiente consulta:

 > for asset, zabumba in db.session.query(Zabumba).join(Asset).all(): > print "{:25}\t\t{:25}".format(asset, zabumba) 

Pero SQLAlchemy me dice que no puede encontrar las claves externas adecuadas para esta combinación:

 Traceback (most recent call last): File "sqlalchemy_join.py", line 65, in  for asset, zabumba in db.session.query(Zabumba).join(Asset).all(): File "/home/calsaverini/.virtualenvs/recsys/local/lib/python2.7/site-packages/sqlalchemy/orm/query.py", line 1724, in join from_joinpoint=from_joinpoint) File "", line 2, in _join File "/home/calsaverini/.virtualenvs/recsys/local/lib/python2.7/site-packages/sqlalchemy/orm/base.py", line 191, in generate fn(self, *args[1:], **kw) File "/home/calsaverini/.virtualenvs/recsys/local/lib/python2.7/site-packages/sqlalchemy/orm/query.py", line 1858, in _join outerjoin, create_aliases, prop) File "/home/calsaverini/.virtualenvs/recsys/local/lib/python2.7/site-packages/sqlalchemy/orm/query.py", line 1928, in _join_left_to_right self._join_to_left(l_info, left, right, onclause, outerjoin) File "/home/calsaverini/.virtualenvs/recsys/local/lib/python2.7/site-packages/sqlalchemy/orm/query.py", line 2056, in _join_to_left "Tried joining to %s, but got: %s" % (right, ae)) sqlalchemy.exc.InvalidRequestError: Could not find a FROM clause to join from. Tried joining to , but got: Can't find any foreign key relationships between 'zabumba' and 'asset'. 

db.session.query(Asset).join(Zabumba).all() varias otras cosas como: declarar ForeignKeyConstraint en ambas tablas o invertir la consulta a db.session.query(Asset).join(Zabumba).all() .

¿Qué estoy haciendo mal?

Gracias.


PD: en mi código de aplicación real, el problema es en realidad un poco más complicado porque esas tablas están en diferentes esquemas y usaré el enlace:

 app.config['SQLALCHEMY_BINDS'] = { 'assets': 'mysql+mysqldb://fooserver/assets', 'zabumbas': 'mysql+mysqldb://fooserver/zabumbas', } 

Y luego declararé los diferentes enlaces en las tablas. ¿Cómo debo declarar ForeignKeyConstraint entonces?

Su código tiene pocos errores tipográficos , corrigiendo lo que hará que todo el código funcione.

Defina correctamente la ForeignKeyConstraint :

  • no es solo definirlo, hay que agregarlo a __table_args__
  • Se invierte la definición de los parámetros de columnas y refcolumns (ver documentación )
  • los nombres de las columnas deben ser nombres en la base de datos, y no el nombre de los atributos ORM

como se muestra en el siguiente código:

 class Zabumba(db.Model): __tablename__ = 'zabumba' __table_args__ = ( db.ForeignKeyConstraint( ['usuario', 'perfil'], ['asset.usuario', 'asset.perfil'], ), ) 

construya correctamente la consulta teniendo ambas clases en la cláusula de consulta:

  for asset, zabumba in db.session.query(Asset, Zabumba).join(Zabumba).all(): print "{:25}\t<---->\t{:25}".format(asset, zabumba)