2017-04-20 115 views
0

假设我有一个SQLAlchemy的表看起来像:动态添加过滤器SQLAlchemy的TextClause

class Country: 
    name = VARCHAR 
    population = INTEGER 
    continent = VARCHAR 
    num_states = INTEGER 

我的应用程序允许看到的名称和人口的所有国家。所以我有一个TextClause,它看起来像

"select name, population from Country" 

我允许我的应用程序的原始查询,所以我没有选项来改变这选择。

在运行时,我想允许我的用户选择一个字段名称并将一个字段值放在我想允许过滤的位置上。例如:用户可以说我只想看看欧洲大陆是亚洲的国家的名字和人口。所以,我要动态添加的过滤器

.where(Country.c.continent == 'Asia') 

但我不能添加.whereTextClause

同样,我的用户可以选择看名字和人口的国家,num_states大于10所以我要动态添加的过滤器

.where(Country.c.num_states > 10) 

但我又不能添加.whereTextClause

我有什么选择来解决这个问题?

子查询可以以任何方式帮助吗?

+0

你受到什么限制?你可以修改'TextClause'吗?你知道'TextClause'的结构吗? – univerio

+0

我无法修改TextClause。我不知道TextClause的结构。该程序只告诉用户表的名称。在用户输入TextClause之后,用户还需要定义需要与TextClause结合使用的另一列(例如:num_states)和值(例如:小于10)。 –

回答

0

请根据情况添加过滤条件。过滤器用于在sqlalchemy中添加条件。

Country.query.filter(Country.num_states > 10).all() 

你也可以这样做:

query = Country.query.filter(Country.continent == 'Asia') 
if user_input == 'states': 
    query = query.filter(Country.num_states > 10) 
query = query.all() 
+0

你在哪里使用TextClause? –

1

这不是一般意义上是可行的,而不解析查询。在关系代数术语中,用户将投影和选择操作应用于表格,并且您想要对其应用选择操作。由于用户可以应用任意投影(例如,用户供应品SELECT id FROM table),因此您无法始终将滤镜应用于顶部,因此您必须在用户使用之前应用滤镜。这意味着您需要将其重写为SELECT id FROM (some subquery),这需要解析用户的查询。

但是,我们可以根据您使用的数据库进行排序,方法是让数据库引擎为您解析。实现这一目标的方式是使用CTE,通过CTE基本上映射表名称。

使用你的例子,它看起来像下面这样。用户提供查询

SELECT name, population FROM country; 

您的CTE阴影country

WITH country AS (
    SELECT * FROM country 
    WHERE continent = 'Asia' 
) SELECT name, population FROM country; 

不幸的是,由于方式的SQLAlchemy的CTE支持的作品,这是很难得到它产生的CTE为TextClause。解决的办法是自己基本上生成的字符串,使用自定义编译扩展,这样的事情:

class WrappedQuery(Executable, ClauseElement): 
    def __init__(self, name, outer, inner): 
     self.name = name 
     self.outer = outer 
     self.inner = inner 

@compiles(WrappedQuery) 
def compile_wrapped_query(element, compiler, **kwargs): 
    return "WITH {} AS ({}) {}".format(
     element.name, 
     compiler.process(element.outer), 
     compiler.process(element.inner)) 

c = Country.__table__ 
cte = select(["*"]).select_from(c).where(c.c.continent == "Asia") 
query = WrappedQuery("country", cte, text("SELECT name, population FROM country")) 
session.execute(query) 

从我的测试中,PostgreSQL中这仅适用。 SQLite和SQL Server都将它视为递归而不是阴影,而MySQL不支持CTE。

0

在这篇文档中我找不到任何漂亮的东西。我最终采取了几乎只是字符串处理....但至少它的作品!

from sqlalchemy.sql import text 

query = """select name, population from Country""" 

if continent is not None: 
    additional_clause = """WHERE continent = {continent};""" 
    query = query + additional_clause 
    text_clause = text(
     query.format(
      continent=continent, 
     ), 
    ) 
else: 
    text_clause = text(query) 

with sql_connection() as conn: 
    results = conn.execute(text_clause) 

你也可以连锁这个逻辑有更多的条款,但你必须创建第一个WHERE子句,然后使用和随后的人一个布尔标志。