Модель запроса SQLAlchemy, когда связанной модели с заданными свойствами не существует - PullRequest
0 голосов
/ 25 апреля 2018

У меня есть три модели (обратите внимание, что это делается в Flask-SQLAlchemy, но если вы можете написать ответ только для ванильной 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? Если да, то какое волшебное слово (мольбы, похоже, не работают ...)

1 Ответ

0 голосов
/ 25 апреля 2018

Выражение подзапроса 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()))

Наконец, опция LEFT JOIN с IS NULL также является опцией:

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