22

或者我如何让这件事发挥作用?

我有一个区间对象:

class Interval(Base):
    __tablename__ = 'intervals'
    id = Column(Integer, primary_key=True)
    start = Column(DateTime)
    end = Column(DateTime, nullable=True)
    task_id = Column(Integer, ForeignKey('tasks.id'))

@hybrid_property #used to just be @property
def hours_spent(self):
    end = self.end or datetime.datetime.now()
    return (end-start).total_seconds()/60/60

还有一个任务:

class Task(Base):
    __tablename__ = 'tasks'
    id = Column(Integer, primary_key=True)
    title = Column(String)
    intervals = relationship("Interval", backref="task")

@hybrid_property  # Also used to be just @property
def hours_spent(self):
    return sum(i.hours_spent for i in self.intervals)

当然,添加所有典型的设置代码。

现在当我尝试做session.query(Task).filter(Task.hours_spent > 3).all()

NotImplementedError: <built-in function getitem>sum(i.hours_spent...线上得到。

所以我正在查看文档的这一部分,并推测可能有某种方法可以让我写出我想要的东西。这部分看起来也可能有用,我会在这里等待答案的同时查看它;)

4

3 回答 3

18

对于 SQLAlchemy 的 coalesce 函数的简单示例,这可能会有所帮助:在 SQLAlchemy 查询中处理空值 - 等效于 isnull、nullif 或 coalesce

以下是该帖子中的几行关键代码:

from sqlalchemy.sql.functions import coalesce
my_config = session.query(Config).order_by(coalesce(Config.last_processed_at, datetime.date.min)).first()
于 2014-06-06T14:37:57.533 回答
10

SQLAlchemy 不够聪明,无法从这些操作数构建 SQL 表达式树,您必须使用显式propname.expression装饰器来提供它。但随之而来的另一个问题是:没有可移植的方法将间隔转换为数据库中的小时数。您将TIMEDIFF在 MySQL、EXTRACT(EPOCH FROM ... ) / 3600PostgreSQL 等中使用。我建议将属性更改为返回timedelta,并将苹果与苹果进行比较。

from sqlalchemy import select, func


class Interval(Base):
    ...

    @hybrid_property
    def time_spent(self):
        return (self.end or datetime.now()) - self.start

    @time_spent.expression
    def time_spent(cls):
        return func.coalesce(cls.end, func.current_timestamp()) - cls.start


class Task(Base):
    ...

    @hybrid_property
    def time_spent(self):
        return sum((i.time_spent for i in self.intervals), timedelta(0))

    @time_spent.expression
    def hours_spent(cls):
        return (select([func.sum(Interval.time_spent)])
            .where(cls.id==Interval.task_id)
            .label('time_spent'))

最后的查询是:

session.query(Task).filter(Task.time_spent > timedelta(hours=3)).all()

转换为(在 PostgreSQL 后端):

SELECT task.id AS task_id, task.title AS task_title 
FROM task 
WHERE (SELECT sum(coalesce(interval."end", CURRENT_TIMESTAMP) - interval.start) AS sum_1 
FROM interval 
WHERE task.id = interval.task_id) > %(param_1)s
于 2013-08-07T12:01:22.073 回答
0

有一个类似于coalescor的 func 动作的完整示例nvl

注意它如何接受争论,并呈现一个表达式......在这种情况下,当与 Oracle 一起使用时,它是 NVL(a, b)。

http://docs.sqlalchemy.org/en/latest/core/compiler.html#subclassing-guidelines

from sqlalchemy.ext.compiler import compiles
from sqlalchemy.sql.expression import FunctionElement

class coalesce(FunctionElement):
     name = 'coalesce'

@compiles(coalesce)
def compile(element, compiler, **kw):
     return "coalesce(%s)" % compiler.process(element.clauses)

@compiles(coalesce, 'oracle')
def compile(element, compiler, **kw):
     if len(element.clauses) > 2:
          raise TypeError("coalesce only supports two arguments on Oracle")
     return "nvl(%s)" % compiler.process(element.clauses)

那么当你想使用它时...

from my_oracle_functions_sqla import coalesce
select([coalesce(A.value, '---')])  # etc

希望有帮助。

于 2018-08-02T17:29:23.717 回答