简体   繁体   English

Postgres用作SQLAlchemy中的列

[英]Postgres function as column in SQLAlchemy

I have two functions in Postgesql database: 我在Postgesql数据库中有两个功能:

  • id_generator , which generates id from timestamp id_generator ,它从时间戳生成id
  • timestamp_from_id(id) , which reverses id to timestamp timestamp_from_id(id) ,将id转换为timestamp

Let's assume this is my model: 假设这是我的模型:

class DatModel(object):
    id = Column(
        BigInteger,
        primary_key=True,
        server_default=text('id_generator()')
    )
    name = Column(String(50), index=True)

What I want to do is query by timestamp generated by timestamp_from_id(id) function, for example: 我想要做的就是通过查询timestamp所产生timestamp_from_id(id)功能,例如:

dbsession.query(DatModel).filter(DatModel.timestamp > datetime.now()).all()

or 要么

obj = dbsession.query(DatModel).first()
created = obj.timestamp

My question is: 我的问题是:

How to create virtual column based on postgres function? 如何基于postgres函数创建虚拟列?

Thanks in advance 提前致谢


Edit: 编辑:

Unsatisfying method #1 不满意的方法#1

As Roman Dryndik suggested possible solution is to use func . 正如Roman Dryndik所 建议的那样,可能的解决方案是使用func It's working method, but little problematic way while retrieving timestamp: 它是一种工作方法,但是在检索时间戳时几乎没有问题:

timestamp = session.execute(func.timestamp_from_id(obj.id)).fetchone()

I'd prefer to do it something like: 我更喜欢这样做:

timestamp = obj.timestamp

Unsatisfying method #2 不满意的方法2

Second possible solution is to use Compilation Extension 第二种可能的解决方案是使用编译扩展

from datetime import datetime

from sqlalchemy import DateTime
from sqlalchemy.ext.compiler import compiles
from sqlalchemy.sql.functions import FunctionElement

class Timestamp(FunctionElement):
    type = DateTime()
    name = 'timestamp'

@compiles(Timestamp)
def default_timestamp(element, compiler, **kw):
    return compiler.visit_function(element)

@compiles(Timestamp, 'postgresql')
def pg_created(element, compiler, **kw):
    arg1 = list(element.clauses)[0]
    return 'timestamp_from_id({})'.format(compiler.process(arg1))


# filter example
obj = session.query(DatModel).filter(Timestamp < datetime.now()).first()

# get timestamp attribute example
timestamp = session.execute(Timestamp(obj.id)).fetchone()

Please read Using column_property section of SQL Expressions as Mapped Attributes documentation. 请阅读“ SQL表达式用作列属性”文档的column_property部分。

Using it you should be able to do: 使用它,您应该能够:

class DatModel(Base):
    id = Column(
        Integer,
        primary_key=True,
        server_default=text('id_generator()')
    )
    name = Column(String(50), index=True)

    timestamp = column_property(func.timestamp_from_id(id))

timestamp will also be included in your query, and you can use it in the filtering and/or ordering expressions: timestamp也将包含在查询中,您可以在过滤和/或排序表达式中使用它:

q = (session.query(DatModel)
     .filter(DatModel.timestamp > datetime.now())
     .order_by(DatModel.timestamp.desc())
    )

If I correctly understand your problem you can call the stored procedure timestamp_from_id using func . 如果我正确理解了您的问题,则可以使用func调用存储过程timestamp_from_id

So you will get something like this: 因此,您将获得如下内容:

from sqlalchemy import func

# some code here

dbsession.query(DatModel).filter(func.timestamp_from_id(DataModel.id) > datetime.now()).all()

# some code here

Didn't try the code. 没有尝试代码。 Probably you should do func.timestamp_from_id(DataModel.id).scalar() as well. 也许您也应该执行func.timestamp_from_id(DataModel.id).scalar()

More info you can find here and here . 您可以在这里这里找到更多信息。

The solution is to use hybrid_property and expression ( docs ) 解决方案是使用hybrid_propertyexpressiondocs

class DatModel(Base)::
    id = Column(
        BigInteger,
        primary_key=True,
        server_default=text('id_generator()')
    )

    @hybrid_property
    def timestamp(self):
        return object_session(self). \
            scalar(select([func.timestamp_from_id(self.id)]))

    @created.expression
    def timestamp(self):
        return func.timestamp_from_id(self.id)

Example usage: 用法示例:

obj = DatModel()
with transaction.manager:
    session.add(obj)
obj = session.query(DatModel).one()

assert type(obj.timestamp) == datetime.datetime
assert obj.timestamp.date() == datetime.date.today()


objs = session.query(DatModel) \
    .filter(DatModel.timestamp < datetime.datetime.now())
assert objs.count() == 1

objs = session.query(Gateway) \
    .filter(DatModel.timestamp > datetime.datetime.now())
assert objs.count() == 0

You could achieve this using a column_property with a literal_column . 你可以使用一个实现这一column_propertyliteral_column For example: 例如:

class DatModel(object):
    id = Column(
        BigInteger,
        primary_key=True,
        server_default=text('id_generator()')
    )
    name = Column(String(50), index=True)
    timestamp = column_property(literal_column("timestamp_from_id(datmodel.id)", type_=DateTime).label('timestamp'))

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

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