基于关系属性的 SqlAlchemy 过滤

我对 SQLAlchemy 没有多少经验,我有一个问题,我无法解决。我试过搜索,也试过很多代码。 这是我的 Class (简化为最重要的代码) :

class Patient(Base):
__tablename__ = 'patients'
id = Column(Integer, primary_key=True, nullable=False)
mother_id = Column(Integer, ForeignKey('patients.id'), index=True)
mother = relationship('Patient', primaryjoin='Patient.id==Patient.mother_id', remote_side='Patient.id', uselist=False)
phenoscore = Column(Float)

我想询问所有的病人,其母亲的酚分是(例如) == 10

我说过,我试了很多代码,但我不明白。在我看来,逻辑上的解决办法是

patients = Patient.query.filter(Patient.mother.phenoscore == 10)

因为,您可以在输出时访问每个元素的 .mother.phenoscore,但是,这段代码不这样做。

是否有可能(直接)通过关系的属性进行过滤(不需要编写 SQL 语句或额外的连接语句) ,我不止一次需要这种过滤器。

即使没有简单的解决办法,我也乐于得到所有的答案。

109672 次浏览

必须使用 join 查询关系

您将从这个 自我参照查询策略中获得示例

使用方法 has()的关系(更易读) :

patients = Patient.query.filter(Patient.mother.has(phenoscore=10))

或加入(通常更快) :

patients = Patient.query.join(Patient.mother, aliased=True)\
.filter_by(phenoscore=10)

对您来说,好消息是: 我最近制作了一个包,它使用“神奇的”字符串 就是姜戈为您进行过滤/排序,因此您现在可以编写类似

Patient.where(mother___phenoscore=10)

它要短得多尤其是对于复杂的过滤器来说,

Comment.where(post___public=True, post___user___name__like='Bi%')

希望你喜欢这个包裹

Https://github.com/absent1706/sqlalchemy-mixins#django-like-queries

我在会话中使用它,但是可以直接访问关系字段的另一种方法是

db_session.query(Patient).join(Patient.mother) \
.filter(Patient.mother.property.mapper.class_.phenoscore==10)

我还没有测试过,但我想这也可以

Patient.query.join(Patient.mother) \
.filter(Patient.mother.property.mapper.class_.phenoscore==10)

这是一个关于如何查询关系的更一般的答案。

relationship(..., lazy='dynamic', ...)

这样你就可以:

parent_obj.some_relationship.filter(ParentClass.some_attr==True).all()

对于那些希望使用声明性基础来完成此筛选的人,您可以使用 联合代理人联合代理人:

from sqlalchemy.ext.associationproxy import association_proxy


class Patient(Base):
__tablename__ = 'patients'
id = Column(Integer, primary_key=True, nullable=False)
mother_id = Column(Integer, ForeignKey('patients.id'), index=True)
mother = relationship('Patient', primaryjoin='Patient.id==Patient.mother_id',
remote_side='Patient.id', uselist=False)
phenoscore = Column(Float)


"""
Access the associated object(s) through this proxy
    

Note: Because the above relationship doesn't use a
collection (uselist=False), the associated attribute
will be a scalar. If the relationship does use a
collection (uselist=True), the associated attribute
would then be a list (or other defined collection) of values.
"""
mother_phenoscore = association_proxy('mother', 'phenoscore')

您可以直接查询子元素,而不必在关系上使用 has():

patients = Patient.query.filter(Patient.mother_phenoscore == 10)

我使用‘ any ()’函数在关系列上添加过滤查询。

class ArticleModel(db.Model, BaseModel):
__tablename__ = "articles"


id = db.Column(db.Integer, primary_key=True, autoincrement=True)
title = db.Column(db.String(120), nullable=False)
thumbnail = db.Column(db.String(240), nullable=True)
short_content = db.Column(db.String(400), nullable=False)
content = db.Column(db.String, nullable=False)
category_id = db.Column(db.Integer, db.ForeignKey("categories.id"), nullable=False)
category = db.relationship("CategoryModel", backref="articles", lazy=True)
views_count = db.Column(db.Integer, default=0, nullable=False)
comment_count = db.Column(db.Integer, default=0, nullable=False)
comments = db.relationship("CommentModel", backref="articles")
tags = db.relationship("ArticleTagModel", backref="articles", lazy=True)
seo_tags = db.Column(db.String(150), default="Software, Flask, Python, .Net Core, Web, Developer, JavaScript, React, Asp.Net, HTML5, CSS3, Web Development, Mobile, React Native", nullable=False)
seo_description = db.Column(db.String(150), default="", nullable=False)




class ArticleTagModel(db.Model, BaseModel):
__tablename__ = "article_tags"


id = db.Column(db.Integer, primary_key=True, autoincrement=True)
article_id = db.Column(db.Integer, db.ForeignKey("articles.id"), nullable=False)
tag_id = db.Column(db.Integer, db.ForeignKey("tags.id"), nullable=False)
tag = db.relationship("TagModel", backref="article_tags", lazy=True)

像这样使用

articles = ArticleModel.query.filter(ArticleModel.tags.any(tag_id=tag_id)).all()