【问题标题】:SQLAlchemy: Hybrid expression with relationshipSQLAlchemy:具有关系的混合表达式
【发布时间】:2013-11-15 19:21:56
【问题描述】:

我有两个具有简单一对多关系的 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()

【问题讨论】:

  • 我想知道如何在parent side 上实现这一点。

标签: python sqlalchemy flask-sqlalchemy


【解决方案1】:

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

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

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

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

【讨论】:

  • 这太简单了。非常感谢。
  • 您对select() 的看法是正确的。我的错。我对详细说明的问题添加了更新。
  • 您好,您能详细说明一下它是如何支持查询的吗?我试图在网上找到语法但未能找到有意义的结果。
  • @Curtwagner1984 它的工作方式应该与任何其他列查询相同 — Teacher.school_name == "Bob Dylan Elementary"
  • @Eevee 我明白了。我的意思是你怎么能告诉代理只签入条目的子集。例如,如果一个Teacher 有多个nicknames,并且这些昵称中只有一个是他的真实姓名,而另一个只是昵称。所以nicknames 有两个字段,nameis_primary。现在我希望 Teacher.name 给我相当于 SELECT nickname.name from nicknames join teacher on nicknames.teacher_id = teacher.id where nickname.is_primary = true limit 1
猜你喜欢
  • 1970-01-01
  • 2016-08-17
  • 2012-05-18
  • 2016-02-29
  • 1970-01-01
  • 2012-06-18
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多