简体   繁体   English

如何将多表 case 语句添加到 @hybrid_property 表达式?

[英]How do I add a multi-table case statement to a @hybrid_property expression?

I'm trying to create a @hybrid_property in a 'parent' table with a case statement that outputs a datetime value from a 'child' table if one exists.我正在尝试在“父”表中创建一个@hybrid_property ,其中包含一个 case 语句,该语句从“子”表中输出datetime时间值(如果存在)。 If it doesn't then it should output the datetime value from the original table.如果不是,那么它应该是原始表中的datetime时间值 output。 As the relationship between the two tables will be one-to-one then I'm following the guide here .由于这两个表之间的关系是一对一的,因此我将按照此处的指南进行操作。

I've put together this MRE:我把这个 MRE 放在一起:

from sqlalchemy import Integer, Column, DateTime, case, create_engine, ForeignKey
from sqlalchemy.orm import relationship, sessionmaker
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.ext.hybrid import hybrid_property
import datetime as dt

Base = declarative_base()

class TblParent(Base):

    __tablename__ = "tbl_parent"
    __table_args__ = {"schema": "test"}

    id_ = Column(Integer, primary_key=True)
    date_time = Column(DateTime)

    tbl_child_rel = relationship("TblChild", back_populates="tbl_parent_rel", uselist=False)

    @hybrid_property
    def date_time_hybrid(self):
        return

    @date_time_hybrid.expression
    def date_time_hybrid(cls):
        return case(
            [
                (cls.tbl_child_rel.date_time.__ne__(None), cls.tbl_child_rel.date_time),
            ],
            else_=cls.date_time,
        )


class TblChild(Base):

    __tablename__ = "tbl_child"
    __table_args__ = {"schema": "test"}

    id_ = Column(Integer, primary_key=True)
    date_time = Column(DateTime)
    tbl_parent_id = Column(Integer, ForeignKey("test.tbl_parent.id_"), unique=True)

    tbl_parent_rel = relationship("TblParent", back_populates="tbl_child_rel")


engine = create_engine(cn.CONN_STRING)
Session = sessionmaker(bind=engine)
session = Session()
Base.metadata.drop_all(engine)
Base.metadata.create_all(engine)
session.add(TblParent(id_=1, date_time=dt.datetime(2000, 1, 1)))
session.add(TblChild(id_=1, date_time=dt.datetime(2000, 1, 1), tbl_parent_id=1))
session.commit()
qry = session.query(TblParent.date_time_hybrid)

When I run it I get the error:当我运行它时出现错误:

AttributeError: Neither 'InstrumentedAttribute' object nor 'Comparator' object associated with TblParent.tbl_child has an attribute 'date_time'

Not sure where I'm going wrong?不确定我哪里出错了? Thanks in advance!提前致谢!

I'm using python 3.9.6 and sqlalchemy 1.4.23.我正在使用 python 3.9.6 和 sqlalchemy 1.4.23。

Below should work:下面应该工作:

@hybrid_property
def date_time_hybrid(self):
    return self.tbl_child_rel and self.tbl_child_rel.date_time or self.date_time

@date_time_hybrid.expression
def date_time_hybrid(cls):
    subq = (
        select([TblChild.date_time.label("date_time")])
        .where(TblChild.tbl_parent_id == cls.id_)
        .scalar_subquery()
    )
    return func.ifnull(subq, cls.date_time).label("date_time_hybrid")

Fully contained working example below:下面是完全包含的工作示例:

import datetime as dt

from sqlalchemy import (
    Column,
    DateTime,
    ForeignKey,
    Integer,
    create_engine,
    func,
    select,
)
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.ext.hybrid import hybrid_property
from sqlalchemy.orm import relationship, sessionmaker

Base = declarative_base()


class TblParent(Base):

    __tablename__ = "tbl_parent"
    # __table_args__ = {"schema": "test"}

    id_ = Column(Integer, primary_key=True)
    date_time = Column(DateTime)

    tbl_child_rel = relationship(
        "TblChild", back_populates="tbl_parent_rel", uselist=False
    )

    @hybrid_property
    def date_time_hybrid(self):
        return self.tbl_child_rel and self.tbl_child_rel.date_time or self.date_time

    @date_time_hybrid.expression
    def date_time_hybrid(cls):
        subq = (
            select([TblChild.date_time.label("date_time")])
            .where(TblChild.tbl_parent_id == cls.id_)
            .scalar_subquery()
        )
        return func.ifnull(subq, cls.date_time).label("date_time_hybrid")


class TblChild(Base):

    __tablename__ = "tbl_child"
    # __table_args__ = {"schema": "test"}

    id_ = Column(Integer, primary_key=True)
    date_time = Column(DateTime)
    tbl_parent_id = Column(Integer, ForeignKey("tbl_parent.id_"), unique=True)

    tbl_parent_rel = relationship("TblParent", back_populates="tbl_child_rel")


CONN_STRING = "sqlite:///:memory:"
engine = create_engine(CONN_STRING, echo=True)
Session = sessionmaker(bind=engine)
session = Session()
Base.metadata.drop_all(engine)
Base.metadata.create_all(engine)

parents = (p_no_child_no_dt, p_no_child_dt, p_child_no_dt, p_child_dt,) = (
    TblParent(id_=1, date_time=None),
    TblParent(id_=2, date_time=dt.datetime(2000, 1, 1)),
    TblParent(id_=3, date_time=dt.datetime(2000, 1, 2), tbl_child_rel=TblChild()),
    TblParent(
        id_=4,
        date_time=dt.datetime(2000, 1, 3),
        tbl_child_rel=TblChild(date_time=dt.datetime(2000, 1, 4)),
    ),
)
session.add_all(parents)
session.commit()
# qry = session.query(TblParent.date_time_hybrid)

# in-memory
session.expunge_all()
qry = session.query(TblParent)
for p in qry.all():
    print(p.id_, p.date_time_hybrid)

print("-" * 80)

# query
session.expunge_all()
qry = session.query(TblParent.id_, TblParent.date_time_hybrid)
for p in qry:
    print(p)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM