首页 > 解决方案 > 不存在具有给定属性的相关模型时的 SQLAlchemy 查询模型

问题描述

我有三个模型(请注意,这是在 Flask-SQLAlchemy 中完成的,但如果你只能为 vanilla SQLAlchemy 写一个答案,那对我来说很好。)为了清楚起见,删除了不相关的字段。

class KPI(db.Model):
    __tablename__ = 'kpis'

    id = db.Column(db.Integer, primary_key=True)
    identifier = db.Column(db.String(length=50))


class Report(db.Model):
    __tablename__ = 'reports'

    id = db.Column(db.Integer, primary_key=True)


class ReportKPI(db.Model):
    report_id = db.Column(db.Integer, db.ForeignKey('reports.id'), primary_key=True)
    kpi_id = db.Column(db.Integer, db.ForeignKey('kpis.id'), primary_key=True)

    report = db.relationship('Report', backref=db.backref('values'))
    kpi = db.relationship('KPI')

我的目标是找到所有Report不测量特定值的对象KPI(即,没有ReportKPI对象的KPI关系已identifier设置为特定值)。

我的一个尝试看起来像

Report.query \
      .join(ReportKPI) \
      .join(KPI) \
      .filter(KPI.identifier != 'reflection')

但这会返回更多Report实际存在的对象(我想我会得到一个ReportKPI除了KPI“反射”之外的任何东西。)

我想用 SQLAlchemy 实现的目标真的可行吗?如果是这样,神奇的词是什么(请似乎不起作用……)

标签: pythonsqlalchemyflask-sqlalchemy

解决方案


EXISTS 子查询表达式非常适合您的目标。编写此类查询的一种速记方法是:

Report.query.\
    filter(db.not_(Report.values.any(
        ReportKPI.kpi.has(identifier='reflection'))))

但这会产生 2 个嵌套的 EXISTS 表达式,尽管 EXISTS 中的连接也可以:

Report.query.\
    filter(db.not_(
        ReportKPI.query.
            filter_by(report_id=Report.id).
            join(KPI).
            filter_by(identifier='reflection').
            exists()))

最后,带有 IS NULL 的 LEFT JOIN 也是一种选择:

Report.query.\
    outerjoin(db.join(ReportKPI, KPI),
              db.and_(ReportKPI.report_id == Report.id,
                      KPI.identifier == 'reflection')).\
    filter(KPI.id.is_(None))

推荐阅读