SQLAlchemy: Hybrydowe wyrażenie z relacją
Mam dwa modele Flask-SQLAlchemy z prostą relacją jeden-do-wielu, jak w poniższym przykładzie minimalnym:
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')
Następnie dodaję właściwość hybrydową do nauczyciela, który używa relacji, jak:
@hybrid_property
def school_name(self):
return self.school.name
A ta właściwość działa dobrze, gdy używam jej jakoteacher_instance.school_name
. Chciałbym jednak również zadać takie pytaniaTeacher.query.filter(Teacher.school_name == 'x')
, ale to daje mi błąd:
`AttributeError: Neither 'InstrumentedAttribute' object nor
'Comparator' object has an attribute 'school_name'`.
Po dokumentacji SQLAlchemy dodałem proste wyrażenie hybrydowe, jak poniżej:
@school_name.expression
def school_name(cls):
return School.name
Jednak, gdy spróbuję ponownie tego samego zapytania, generuje zapytanie SQL bez klauzuli join, więc dostaję wszystkie dostępne wiersze w School, a nie tylko te, które pasują do klucza obcego w Teacher.
Z dokumentacji SQLAlchemy zdałem sobie sprawę, że wyrażenie oczekuje kontekstu, w którym złączenie jest już obecne, więc spróbowałem ponownie wykonać zapytanie jako:
Teacher.query.join(School).filter(Teacher.school_name == 'x')
I to rzeczywiście działa, ale pokonuje cel, jakim jest uzyskanie cukru syntaktycznego w pierwszej kolejności, jeśli potrzebuję wiedzy o modelu szkoły, aby to uzyskać. Spodziewam się, że istnieje sposób, aby dołączyć do tego wyrażenia, ale nie mogłem go nigdzie znaleźć. Dokumentacja ma przykład z wyrażeniem zwracającym podzapytanie zbudowane bezpośrednio za pomocąselect()
, ale nawet to nie zadziałało dla mnie.
Jakieś pomysły?
AKTUALIZACJA
Po odpowiedzi Eevee poniżej, użyłem proxy asocjacji zgodnie z sugestią i działa, ale ciekawi mnie też komentarz, że powinien działać zselect()
podzapytanie i próbowałem dowiedzieć się, co zrobiłem źle. Moja pierwotna próba była:
@school_name.expression
def school_name(cls):
return select(School.name).where(cls.id_school == School.id).as_scalar()
I okazuje się, że dawał mi błąd, ponieważ przegapiłem listę w select (). Poniższy kod działa poprawnie:
@school_name.expression
def school_name(cls):
return select([School.name]).where(cls.id_school == School.id).as_scalar()