2013-08-07 66 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

你是對的!感謝您修復該鏈接。 –