SQLAlchemy:具有关系的混合表达式

时间:2013-11-05 00:57:37

标签: python sqlalchemy flask-sqlalchemy

我有两个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')

这实际上是有效的,但是如果我需要学校模型的知识来实现​​这一目标,它就会首先尝试在那里获得语法糖。我希望有一种方法可以在表达式中加入,但我无法在任何地方找到它。该文档有一个示例,表达式返回直接使用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()

1 个答案:

答案 0 :(得分:14)

对于像这样的简单案例,更简单的方法是association proxy

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

这支持自动查询(至少使用==)。

我很好奇混合select()示例如何不适合您,因为这是在混合中修复此问题的最简单方法。为了完成,您还可以使用transformer直接修改查询而不是子查询。