2017-04-20 107 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子句,然後使用和隨後的人一個布爾標誌。