2013-08-07 24 views
8

Albo jak mogę to sprawić?Jak zaimplementować operator koalescencji zerowej w SQLAlchemy?

mam odstępie obiektu:

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 

a zadanie:

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) 

dodać wszystkie typowe kod konfiguracji, oczywiście.

Teraz, gdy próbuję zrobić session.query(Task).filter(Task.hours_spent > 3).all()

mam NotImplementedError: <built-in function getitem> z linii sum(i.hours_spent....

Więc patrzyłem na this part dokumentacji i teorię, że może być jakiś sposób, że mogę napisać coś, co zrobi to, co chcę. This part również wygląda na to, że może być przydatny, a ja będę na niego czekał, czekając na odpowiedź tutaj;)

Odpowiedz

5

SQLAlchemy nie jest wystarczająco inteligentny, aby zbudować drzewo wyrażenia SQL z tych operandów, musisz użyć jawnego propname.expression dekorator, aby to zapewnić. Ale pojawia się kolejny problem: nie ma przenośnego sposobu na konwersję interwałów do godzin w bazie danych. Używałbyś TIMEDIFF w MySQL, EXTRACT(EPOCH FROM ...)/3600 w PostgreSQL itp. Proponuję zmienić właściwości, aby zamiast tego zwracać timedelta i porównywać jabłka z jabłkami.

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

Końcowy zapytanie jest:

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

co przekłada się (na 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

Dla prostych przykładów funkcji koalescencji sqlalchemy, mogą w tym pomóc: Handling null values in a SQLAlchemy query - equivalent of isnull, nullif or coalesce.

Oto kilka kluczowych linii kodu z tego postu:

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

Myślę ten link będzie działać lepiej: http://progblog10.blogspot.com/2014/06/handling-null-values -in-sqlalchemy.html – Kirk

+0

Masz rację! Dzięki za naprawienie tego linku. –