2013-11-05 12 views
20

Ich habe zwei Fläschchen-SQLAlchemy Modelle mit einer einfachen Eins-zu-viele-Beziehung, wie das minimal Beispiel unten:SQLAlchemy: Hybrid-Expression mit Beziehung

class School(db.Model): 
    id = db.Column(db.Integer, primary_key=True) 
    name = db.Column(db.String(30)) 
    address = db.Column(db.String(30)) 

class Teacher(db.Model): 
    id = db.Column(db.Integer, primary_key=True) 
    name = db.Column(db.String(30)) 
    id_school = db.Column(db.Integer, db.ForeignKey(School.id)) 

    school = relationship('School', backref='teachers') 

Dann füge ich eine Hybrid-Eigenschaft auf Lehrer, die verwendet Beziehung, wie:

@hybrid_property 
def school_name(self): 
    return self.school.name 

Und diese Eigenschaft funktioniert gut, wenn ich es als teacher_instance.school_name verwenden. Allerdings würde Ich mag auch Anfragen wie Teacher.query.filter(Teacher.school_name == 'x') machen, aber das gibt mir eine Fehlermeldung:

`AttributeError: Neither 'InstrumentedAttribute' object nor 
'Comparator' object has an attribute 'school_name'`. 

Nach SQLAlchemy Dokumentation, habe ich einen einfachen Hybrid Ausdruck, wie die folgenden:

@school_name.expression 
def school_name(cls): 
    return School.name 

jedoch Wenn ich dieselbe Abfrage erneut ausprobiere, wird eine SQL-Abfrage ohne die Join-Klausel generiert, sodass ich alle verfügbaren Zeilen in der Schule erhalte, nicht nur diejenigen, die mit dem Fremdschlüssel in Teacher übereinstimmen.

Von SQLAlchemy Dokumentation wurde mir klar, dass der Ausdruck einen Zusammenhang erwartet, wo die Verbindung bereits vorhanden ist, so habe ich versucht, die Abfrage erneut als:

Teacher.query.join(School).filter(Teacher.school_name == 'x')

Und das tatsächlich funktioniert, aber es Niederlagen der Zweck der Ich versuche, den syntaktischen Zucker erst dort zu bekommen, wenn ich das Schulmodell kennen muss, um das zu bekommen. Ich nehme an, dass es einen Weg gibt, diesen Ausdruck zu verbinden, aber ich konnte ihn nirgends finden. Die Dokumentation hat ein Beispiel mit dem Ausdruck, der eine Unterabfrage zurückgibt, die direkt mit der select() erstellt wurde, aber selbst das hat bei mir nicht funktioniert.

Irgendwelche Ideen?

UPDATE

Nach Eevee Antwort unten, ich den Verein Proxy wie vorgeschlagen verwendet und es funktioniert, aber ich habe auch mit der Bemerkung merkwürdig, dass es mit der select() Unterabfrage arbeiten soll und versucht, herauszufinden, was ich falsch gemacht. Mein ursprünglicher Versuch war:

@school_name.expression 
def school_name(cls): 
    return select(School.name).where(cls.id_school == School.id).as_scalar() 

Und es stellt sich heraus, dass gab mir einen Fehler, weil ich die Liste in select() verpasst habe. Der folgende Code funktioniert:

@school_name.expression 
def school_name(cls): 
    return select([School.name]).where(cls.id_school == School.id).as_scalar() 

Antwort

12

Ein wesentlich einfacherer Ansatz für einen einfachen Fall wie diesem ist ein association proxy:

class Teacher(db.Model): 
    school_name = associationproxy('school', 'name') 

Dies unterstützt (zumindest mit ==) Abfrage automatisch.

Ich bin gespannt, wie das hybride select() Beispiel für Sie nicht funktioniert, da das der einfachste Weg ist, dies in einem Hybrid zu beheben. Aus Gründen der Fertigstellung könnten Sie auch eine transformer verwenden, um die Abfrage direkt und nicht durch Unterquery zu ändern.

+1

Das ist lächerlich einfach. Danke vielmals. –

+1

Sie hatten Recht mit dem 'select()'. Mein Fehler. Ich habe ein Update zu der Frage hinzugefügt, die es genau beschreibt. –

Verwandte Themen