2013-08-07 33 views
8

Hoặc làm cách nào để làm cho tính năng này hoạt động?Làm cách nào để triển khai toán tử kết hợp rỗng trong SQLAlchemy?

tôi có một đối tượng Interval:

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 

Và một nhiệm vụ:

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) 

Thêm tất cả các mã cài đặt điển hình, tất nhiên.

Bây giờ khi tôi cố gắng làm session.query(Task).filter(Task.hours_spent > 3).all()

tôi nhận được NotImplementedError: <built-in function getitem> từ dòng sum(i.hours_spent....

Vì vậy, tôi đã xem this part tài liệu và được giả thuyết rằng có thể có một số cách để tôi có thể viết một cái gì đó sẽ làm những gì tôi muốn. This part cũng giống như nó có thể được sử dụng, và tôi sẽ xem xét nó trong khi chờ đợi một câu trả lời ở đây;)

Trả lời

5

SQLAlchemy không đủ thông minh để xây dựng cây biểu thức SQL từ các toán hạng này, bạn phải sử dụng rõ ràng propname.expression trang trí để cung cấp nó. Nhưng sau đó đến một vấn đề khác: không có cách di động để chuyển đổi khoảng thời gian để giờ trong cơ sở dữ liệu. Bạn sẽ sử dụng TIMEDIFF trong MySQL, EXTRACT(EPOCH FROM ...)/3600 trong PostgreSQL v.v. Tôi đề xuất thay đổi thuộc tính để trả lại timedelta thay thế và so sánh táo với táo.

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')) 

Truy vấn cuối cùng là:

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

mà dịch để (trên PostgreSQL backend):

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

Đối với một ví dụ đơn giản của chức năng liên hiệp SQLAlchemy, điều này có thể giúp: Handling null values in a SQLAlchemy query - equivalent of isnull, nullif or coalesce.

Dưới đây là một vài dòng chính của mã từ bài rằng:

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

Tôi nghĩ rằng liên kết này sẽ làm việc tốt hơn: http://progblog10.blogspot.com/2014/06/handling-null-values -in-sqlalchemy.html – Kirk

+0

Bạn nói đúng! Cảm ơn bạn đã sửa liên kết đó. –

Các vấn đề liên quan