SqlAlchemy - 按关系属性过滤

Posted

技术标签:

【中文标题】SqlAlchemy - 按关系属性过滤【英文标题】:SqlAlchemy - Filtering by Relationship Attribute 【发布时间】:2012-01-23 14:11:45 【问题描述】:

我没有太多使用 SQLAlchemy 的经​​验,而且我遇到了一个无法解决的问题。我尝试搜索并尝试了很多代码。 这是我的课程(简化为最重要的代码):

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)

我想查询所有患者,其母亲的 phenoscore 是(例如)== 10

如前所述,我尝试了很多代码,但我不明白。在我看来,合乎逻辑的解决方案是

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

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

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

即使没有简单的解决方案,我也很高兴得到所有答案。

【问题讨论】:

【参考方案1】:

关系的使用方法has()(更具可读性):

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

或加入(通常更快):

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

【讨论】:

患者 = Patient.query.filter(Patient.mother.has(Patient.phenoscore==10)) @user1105851 has() 支持条件表达式作为未命名参数和filter_by 风格的关键字参数。后者对我来说似乎更具可读性。 @DenisOtkidach 正确,但它会是 phenoscore = 10filter_by 只接受相等关键字(因为它只是对它们做 **kwargs) @aruisdante 你是对的,这是对答案的错误编辑。 使用 any 代替:patients = Patient.query.filter(Patient.mother.any(phenoscore=10))【参考方案2】:

你必须用join查询关系

你会从这个Self-Referential Query Strategies得到例子

【讨论】:

【参考方案3】:

对你来说好消息:我最近制作了一个包,它可以让你使用“神奇”字符串as in Django 进行过滤/排序,所以你现在可以编写类似的东西

Patient.where(mother___phenoscore=10)

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

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

希望你会喜欢这个包

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

【讨论】:

【参考方案4】:

我将它与会话一起使用,但您可以直接访问关系字段的另一种方式是

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)

【讨论】:

【参考方案5】:

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

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

这允许您:

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

【讨论】:

【参考方案6】:

对于那些希望使用声明性基础来完成此过滤器的人,您可以使用association proxy:

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(),可以直接查询child:

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

【讨论】:

【参考方案7】:

我使用 '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()

【讨论】:

以上是关于SqlAlchemy - 按关系属性过滤的主要内容,如果未能解决你的问题,请参考以下文章

您如何设置 sqlalchemy 关系ihp,以便子查询负载将根据属性过滤我们的结果

SQLAlchemy如何在多对多中按孩子过滤

嵌套的一对多关系 sqlalchemy 过滤

Sqlalchemy:过滤关系中应该有多少个实例

SQLAlchemy:如何按两个字段分组并按日期过滤

如何将自动过滤器添加到与 SQLAlchemy 的关系中?