2017-07-18 62 views
0

如何解析子句,作爲篩選器中的參數方法?如何解析sqlalchemy中的filter子句

運行的

filters_clause = Record.start> = '2017年7月17日'

打印(filters_clause)

給出了一個串

記錄.start> =:start_1

這是必要的實際值而不是:start_1和該值必須已通過process_bind_param函數。我必須使用哪種方法來獲得這樣的字符串:records.start> ='1500321600.0'

#!/usr/bin/env python 
# coding=utf-8 

from __future__ import (division, absolute_import, 
         print_function, unicode_literals) 

import time 

from sqlalchemy.ext.declarative import declarative_base 
from sqlalchemy.orm import scoped_session, sessionmaker, attributes 
from sqlalchemy.types import TypeDecorator 
from sqlalchemy import create_engine, MetaData, Column, Integer, String 


Base = declarative_base() 


class EpochTime(TypeDecorator): 
    impl = Integer 

    def process_bind_param(self, value, dialect): 
     if isinstance(value, unicode): 
      if value.isdigit(): 
       return value 

      if len(value) == 10: 
       value = time.mktime(time.strptime(value, "%Y-%m-%d")) 
      elif len(value) == 13: 
       value = time.mktime(time.strptime(value, "%Y-%m-%d %H")) 
      elif len(value) == 16: 
       value = time.mktime(time.strptime(value, "%Y-%m-%d %H:%M")) 

     return value 

    def process_result_value(self, value, dialect): 
     return time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(value)) 


class Record(Base): 
    __tablename__ = 'records' 

    id = Column(Integer, primary_key=True) 
    name = Column(String) 
    start = Column(EpochTime) 


engine = create_engine('sqlite://') 
session = scoped_session(sessionmaker()) 
session.configure(bind=engine) 
Base.metadata.create_all(engine) 

record = Record(name = 'Record 1', start = '2017-07-16') 
session.add(record) 
record = Record(name = 'Record 2', start = '2017-07-17') 
session.add(record) 
record = Record(name = 'Record 3', start = '2017-07-18') 
session.add(record) 

session.commit() 


filters_clause = Record.start>='2017-07-17' 
s = session.query(Record).filter(filters_clause) 
res = s.all() 
names = [i.name for i in Record.__table__.c] 
rows = [[row.__dict__.get(i) for i in names] for row in res] 

for row in rows: 
    print(row) 

print(0, s) 
print(1, filters_clause) 
+0

你在標題(「解析」)中比在正文中提出了另一個問題(「編譯爲'records.start> ='1500321600.0''」)。你能澄清你的問題嗎? – univerio

+0

英語不是我的母語,請原諒我的錯誤。我認爲'編譯'方法是我所期待的。但是使用這種方法並沒有給我預期的結果。但是,也許我做錯了什麼。無論如何,我認爲你的評論是準確的,只是需要編輯條款值。 – Stan

回答

1

我想你想要的是literal_binds

print(filters_clause.compile(engine, compile_kwargs={"literal_binds": True})) 
# records.start >= 1500274800.0 

警惕SQL注入的,如果你使用literal_binds,雖然。

+0

非常感謝!這正是我想要的!我猜想它一定很容易,但找不到解決方案)) – Stan

0

我不認爲你可以簡單地用香草sqlalchemy來實現這一點。 您可以嘗試使用自定義功能,如:

import re 

def represent_filter(expression): 
    expression = expression.compile() 
    params = expression.params 
    prefixed_names = [':' + param for param in params] 
    pattern = re.compile('(%s)' % '|'.join(prefixed_names)) 

    def substitute_param_value(match): 
     return params[match.group()[1:]] 

    return pattern.sub(substitute_param_value, expression.string) 

然後用它調用represent_filter(filters_clause),它將返回records.start >= 2017-07-17

您可以強制represent_filter函數引用替換值,使用類似repr(str(params[match.group()[1:]]))而不是params[match.group()[1:]]

另一種方法是遍歷給定的表達式(在您的案例中,filters_clauseBinaryExpression,也有一元表達式),檢查左側和右側並構建您自己的表示。如果您只想恢復該值(您提供過濾的日期),則它位於filters_clause.right.effective_value

+0

謝謝你的回答。這是毫無疑問的獲得records.start> ='2017-07-17'。這很容易。有一個問題,通過process_bind_param方法將值'2017-07-17'編譯爲'1500321600.0',特別爲自定義類型的SA創建。當我們執行session.query(Record).filter(filters_clause)時,該方法會自動調用,我不知道如何在不執行整個查詢的情況下爲一個參數(start)調用該方法。 – Stan