ich folgt ist ein minimales Beispiel für eine Viele-zu-viele-Assoziation. Mein Ziel ist es, einen einzelnen Datensatz von X
und auch laden eifrig die Instanzen von Y
, die in der ys
Liste Rekord zu laden sowie Instanzen X
die xs
Liste in einem dieser Instanz sind.Eagerly laden eine kreisförmige Assoziation
class X(db.Model):
__tablename__ = 'x'
xid = db.Column(db.Integer, primary_key=True)
ys = relationship('Z', back_populates='x', lazy='joined')
class Y(db.Model):
__tablename__ = 'y'
yid = db.Column(db.Integer, primary_key=True)
xs = relationship('Z', back_populates='y', lazy='joined')
class Z(db.Model):
__tablename__ = 'z'
xid = db.Column(db.Integer, db.ForeignKey('x.xid'), primary_key=True)
yid = db.Column(db.Integer, db.ForeignKey('y.yid'), primary_key=True)
x = relationship('X', back_populates='ys', lazy='joined')
y = relationship('Y', back_populates='xs', lazy='joined')
Mein Ziel ist es, das folgende Ergebnis zu erzeugen:
expected = [{
'xid': 1,
'ys': [
{'yid': 101, 'xs': [{'xid': 1}, {'xid': 2}, {'xid': 3}]},
{'yid': 102, 'xs': [{'xid': 1}, {'xid': 2}]},
{'yid': 104, 'xs': [{'xid': 1}, {'xid': 4}]},
],
}]
Die SQL-Anweisung, dies zu erreichen, ist recht unkompliziert:
SELECT x.xid, y.yid, x2.xid FROM x
JOIN z ON z.xid = x.xid JOIN y ON z.yid = y.yid ; Fetch Ys
JOIN z as z2 ON z2.yid = y.yid JOIN x as x2 ON z2.xid = x2.xid ; Fetch Xs (depth 2)
WHERE x.xid = 1
Mein Problem ist die Bestimmung, wie eine SQLAlchemy Abfrage erstellen Das wird entweder (a) mir erlauben, diese unformatierte Abfrage auszuführen und sie korrekt den richtigen Modellinstanzen zuzuordnen, oder (b) die Abfrage massieren (mit einer gewissen Kombination von join und contains_eager call) s), so dass es weiß, wie man eigentlich die Joins verwendet, die es generiert, so dass es nicht in n + 1 Abfragen explodiert.
Die richtige Abfrage wird durch die folgenden generiert, aber ich kann nicht schaffen, die Tiefe 2 X-Instanzen von dieser Abfrage zu laden (die Daten werden träge durch eine zweite Auswahl geladen).
a = aliased(Z)
b = aliased(X)
q = X.query.filter(X.xid==1).join(X.ys).join(Z.y).join(a, Y.xs).join(b, Z.x)
Ehrfürchtig - mein Problem war, dass ich nur den Aufruf _last_ 'contains_ager 'verwendete, den Sie im ersten Beispiel haben. Ich dachte, das wäre genug, um den Pfad zu kodieren. – efritz