Как загрузить выражение SQL как сопоставленный атрибут, имеющий отношение к SQLAlchemy? - PullRequest
2 голосов
/ 12 апреля 2020

Давайте создадим две модели SQLAlchemy:

class Child(Base):
    __tablename__ = "child"
    id = Column(String, primary_key=True)
    parent_id = Column(String, ForeignKey("parent.id"), nullable=False)

class Parent(Base):
    __tablename__ = "parent"
    id = Column(String, primary_key=True)
    children = relationship("Child", lazy="joined")

То, чего я пытаюсь добиться, - это наличие логического атрибута (назовем его has_deps) для Parent, который будет True, когда есть дети для данного родителя.

У меня также есть некоторые ограничения:

  • Он должен быть доступен при преобразовании объекта в dict
  • Он не должен генерировать коррелированный подзапрос

Я пробовал разные методы:

  • Использование column_property с exists дало мне коррелированный подзапрос
Parent.has_deps = column_property(
    exists([Child.parent_id]).where(Child.parent_id == Parent.id)
)

результаты в:

SELECT parent.id AS parent_id, EXISTS (SELECT child.parent_id FROM child WHERE child.parent_id = parent.id) AS anon_1, parent.id AS id_1, child_1.id AS child_1_id, child_1.parent_id AS child_1_parent_id 
FROM parent LEFT OUTER JOIN child AS child_1 ON parent.id = child_1.parent_id
  • Использование hybrid_property не загружается во время запроса и не доступно с помощью dict и не имеет для этого цели
@has_deps.expression
def has_deps(cls):
    return case([(Child.parent_id != None, True)], else_=False)

(не будет даже быть привязанным к левому внешнему соединению псевдонимов, сгенерированному отношением)

Например, это запрос, который я хочу, чтобы ORM выполнил, просто выполнив session.query(Parent).all():

SELECT parent.id AS parent_id, CASE WHEN child_1.parent_id IS NOT NULL THEN true ELSE false END AS has_deps, parent.id AS id_1, child_1.id AS child_1_id, child_1.parent_id AS child_1_parent_id 
FROM parent LEFT OUTER JOIN child AS child_1 ON parent.id = child_1.parent_id
...