我有两个具有简单的一对多关系的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查询,因此我得到了School中所有可用的行,而不仅仅是匹配Teacher中外键的行。
从SQLAlchemy文档中,我意识到该表达式需要一个已经存在联接的上下文,因此我再次尝试查询该查询,如下所示:
Teacher.query.join(School).filter(Teacher.school_name == 'x')
这确实有效,但是如果我需要了解School模型来获取语法糖,那么它首先会挫败尝试在其中获取语法糖的目的。我希望有一种方法可以使该表达式加入表达式,但是我在任何地方都找不到它。文档中有一个示例,该表达式返回直接使用构建的子查询select()
,但即使这样对我也不起作用。
有任何想法吗?
更新
在下面的Eevee回答之后,我按建议使用了关联代理,它可以工作,但是我也对它应该与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()
示例对您不起作用,因为这是在混合中解决此问题的最简单方法。并且为了完成起见,您还可以使用转换器直接修改查询,而不是子查询。