2013-08-07 37 views
8

或者我该如何让这件事情起作用?如何在SQLAlchemy中实现空合并运算符?

我有一个时间间隔对象:

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...线。

所以我在寻找this part的文档,并推论说可能有某种方法可以写出我想要的东西。 This part也看起来像它可能是有用的,我会在这里等待一个答案时看它;)

回答

5

SQLAlchemy不够聪明,从这些操作数建立SQL表达式树,你必须使用显式propname.expression装饰者提供它。但是接下来会出现另一个问题:在数据库中没有将间隔转换为小时的便携方式。你会在MySQL中使用TIMEDIFF,在PostgreSQL中使用EXTRACT(EPOCH FROM ...)/3600等。我建议改变属性返回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 
3

对于SQLAlchemy中的聚结功能的一个简单的例子,这可能会帮助:Handling null values in a SQLAlchemy query - equivalent of isnull, nullif or coalesce

这里有一对夫妇从该职位的代码重点线路:

from sqlalchemy.sql.functions import coalesce 
my_config = session.query(Config).order_by(coalesce(Config.last_processed_at, datetime.date.min)).first() 
+2

我觉得这个链接会更好地工作:http://progblog10.blogspot.com/2014/06/handling-null-values -in-sqlalchemy.html – Kirk

+0

你是对的!感谢您修复该链接。 –