2017-07-18 61 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