如何在sqlalchemy中解析filter子句

时间:2017-07-18 12:04:42

标签: python sqlalchemy

如何解析作为 过滤器 方法中的参数给出的子句?

运行

  

filters_clause = Record.start> =' 2017-07-17'

     

打印(filters_clause)

给出一个字符串

  

records.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)

2 个答案:

答案 0 :(得分:1)

我认为你想要的是literal_binds

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

如果使用literal_binds,请注意SQL注入。

答案 1 :(得分:0)

我认为你不能简单地使用vanilla 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