2013-08-07 13 views
8

Oder wie mache ich dieses Ding arbeiten?Wie implementiere ich einen Nullkoaleszenzoperator in SQLAlchemy?

Ich habe ein Intervall Objekt:

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 

Und eine Aufgabe:

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) 

Fügen Sie die alle typischen Setup-Code, natürlich.

Jetzt, wenn ich versuche session.query(Task).filter(Task.hours_spent > 3).all()

I NotImplementedError: <built-in function getitem> von der sum(i.hours_spent... Linie zu tun bekommen.

Also schaute ich auf this part der Dokumentation und theoretisierte, dass es eine Möglichkeit gibt, dass ich etwas schreiben kann, das tun wird, was ich will. This part sieht auch wie es von Nutzen sein kann, und ich werde es zu betrachten, während hier eine Antwort abzuwarten;)

Antwort

5

SQLAlchemy nicht intelligent genug, um SQL-Ausdruck Baum aus diesen Operanden zu bauen, müssen Sie verwenden explizite propname.expression Dekorateur, um es zu liefern. Aber dann kommt ein anderes Problem: Es gibt keinen portablen Weg, um Intervall in Stunden in Datenbank zu konvertieren. Sie würden TIMEDIFF in MySQL, EXTRACT(EPOCH FROM ...)/3600 in PostgreSQL usw. verwenden. Ich schlage vor, Eigenschaften zu ändern, um stattdessen timedelta zurückzugeben und Äpfel mit Äpfeln zu vergleichen.

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

Die letzte Frage ist:

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

, die (auf PostgreSQL-Server) übersetzt:

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

Für ein einfaches Beispiel für SQLAlchemy der coalesce Funktion, das helfen kann: Handling null values in a SQLAlchemy query - equivalent of isnull, nullif or coalesce.

Hier sind ein paar der wichtigsten Codezeilen aus diesem Beitrag:

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

ich diesen Link denken besser funktioniert: http://progblog10.blogspot.com/2014/06/handling-null-values -in-sqlalchemy.html – Kirk

+0

Sie haben Recht! Danke, dass du diesen Link verlinkt hast. –

Verwandte Themen