以下の最小の例のように、単純な1対多の関係を持つ2つのFlask-SQLAlchemyモデルがあります。
_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')
_
次に、次のように、関係を使用するハイブリッドプロパティを教師に追加します。
_@hybrid_property
def school_name(self):
return self.school.name
_
そして、そのプロパティは、_teacher_instance.school_name
_として使用すると問題なく機能します。ただし、Teacher.query.filter(Teacher.school_name == 'x')
のようなクエリも作成したいのですが、エラーが発生します。
_`AttributeError: Neither 'InstrumentedAttribute' object nor
'Comparator' object has an attribute 'school_name'`.
_
SQLAlchemyのドキュメントに従って、次のような単純なハイブリッド式を追加しました。
_@school_name.expression
def school_name(cls):
return School.name
_
ただし、同じクエリを再試行すると、join句なしでSQLクエリが生成されるため、Teacherの外部キーに一致する行だけでなく、Schoolで使用可能なすべての行を取得します。
SQLAlchemyのドキュメントから、式は結合がすでに存在するコンテキストを想定していることがわかったので、次のようにクエリを再試行しました。
Teacher.query.join(School).filter(Teacher.school_name == 'x')
それは実際には機能しますが、それを取得するためにSchoolモデルの知識が必要な場合は、そもそも構文糖衣構文を取得しようとする目的が無効になります。その結合を式に含める方法があると思いますが、どこにも見つかりませんでした。ドキュメントには、select()
で直接作成されたサブクエリを返す式の例がありますが、それでもうまくいきませんでした。
何か案は?
[〜#〜] update [〜#〜]
以下のイーブイの回答の後、提案されたようにアソシエーションプロキシを使用しましたが、それは機能しますが、select()
サブクエリで機能するはずであるというコメントにも興味があり、何が間違っているのかを理解しようとしました。私の最初の試みは:
_@school_name.expression
def school_name(cls):
return select(School.name).where(cls.id_school == School.id).as_scalar()
_
そして、select()のリストを見逃していたため、エラーが発生していたことがわかりました。以下のコードは正常に機能します。
_@school_name.expression
def school_name(cls):
return select([School.name]).where(cls.id_school == School.id).as_scalar()
_
このような単純なケースのはるかに単純なアプローチは、 アソシエーションプロキシ :です。
_class Teacher(db.Model):
school_name = associationproxy('school', 'name')
_
これは、(少なくとも_==
_を使用した)自動的なクエリをサポートします。
ハイブリッドselect()
の例がどのように機能しなかったのか興味があります。これは、ハイブリッド内でこれを修正する最も簡単な方法だからです。また、完了のために、 transformer を使用して、サブクエリではなくクエリを直接修正することもできます。