将 PostgreSQL 多范围函数与 SQLAlchemy 结合使用

问题描述 投票:0回答:1

我在模型中使用 NUMMULTIRANGE 字段。 PostgreSQL 在docs中描述了这种类型的函数。但我在 SQLAlchemy 中找不到此功能的实现。我已阅读文档的这part。 Range 类型有很多有用的属性,但 Multirange 没有。

我的模型看起来像:

...
from sqlalchemy.dialects.postgresql import Range
from sqlalchemy.dialects.postgresql import NUMMULTIRANGE

class Condition(Base):
    range: Mapped[list[Range[Decimal]]] = mapped_column(NUMMULTIRANGE)

有没有办法使用 PostgreSQL 内置函数进行多范围?例如,我想找到多范围的下限和上限,或者使用函数 lower_inc、lower_inf。在这种情况下,似乎我必须迭代我的范围列表。也许您可以建议更有效的方法,或者我错过了文档中的某些内容。

我正在使用 SQLAlchemy 2.0.28 和 PostgreSQL 15。

还有一个问题:PostgreSQL中的Range类型是否有可能定义不包含一个值的Range,相当于

!=
表达式?它允许我拒绝使用多范围,因为我必须使用多范围
x != 6
 指定像 
'{(,6),(6,)}'

这样的表达式
python postgresql sqlalchemy
1个回答
0
投票

您所要求的几乎已使用

func
完成。我添加了一些功能,我相信这些功能应该足以让其他人工作。

我已经展示了一些断言语句,这些语句在空表上将是正确的。

from decimal import Decimal
from sqlalchemy import DECIMAL, cast, create_engine, func, select
from sqlalchemy.dialects.postgresql import NUMMULTIRANGE, Range
from sqlalchemy.dialects.postgresql.ranges import MultiRange
from sqlalchemy.orm import DeclarativeBase, Mapped, Session, mapped_column

class Base(DeclarativeBase):
    pass

class Condition(Base):
    __tablename__ = "condition"
    id: Mapped[int] = mapped_column(primary_key=True)
    range: Mapped[MultiRange[Decimal]] = mapped_column(NUMMULTIRANGE)

engine = create_engine("postgresql+psycopg://username:password@localhost/database")
Base.metadata.create_all(engine)

with Session(engine) as session:
    session.add(Condition(range=[Range(Decimal(7), Decimal(10)), Range(Decimal(-1), Decimal(6))]))
    session.add(Condition(range=[Range()]))
    session.add(Condition(range=[Range(empty=True)]))
    session.commit()

with Session(engine) as session:
    upper = session.scalar(select(func.upper(Condition.range)).where(Condition.id == 1))
    assert upper == 10

    lower = session.scalar(select(func.lower_inf(Condition.range)).where(Condition.id == 2))
    assert lower is True

    is_empty = session.scalar(select(func.isempty(Condition.range)).where(Condition.id == 3))
    assert is_empty is True

    count = session.scalar(select(func.count()).where(Condition.range.op("@>")(MultiRange([Range(Decimal(7), Decimal(10))]))))
    assert count == 2

    count = session.scalar(select(func.count()).where(cast(0, DECIMAL).op("<@")(Condition.range)))
    assert count == 2

对于你的“附加问题”,我不是 Postgres 专家,我无法获得任何关于如何做到这一点的可靠线索。当然,这并不意味着不可能。

© www.soinside.com 2019 - 2024. All rights reserved.