我想在我的web页面上创建一个搜索function,该页面链接到5个表的数据库。

想法是用户可以通过select表搜索并输入搜索词,它将返回结果的list。

显然我可以通过使用5 if-statements来做到这一点,但似乎应该有一个更简单的方法。

这段代码是我试过的,但出于某种原因,当我将itemtable变量放入查询而不是实际的列名时,它不起作用。它只适用于我在tabletable.item中键入特定术语。

   if 'form.submitted' in request.params:
        #search term
        search = request.params['body']
        #table to be searched
        table = request.params['table']
        list = eval(table).__table__.columns._data.keys()

        results = {}

        for item in list:
          try:

            searchdb = request.dbsession.query(table).filter(table.item==search).all()                                     

            results[item]=searchdb

          except:

              continue

        return (results)

我想知道这是否可行,或者我应该放弃并写下5 if语句

分析解答

请不要使用eval()它主要是邪恶的。您无法在用户输入上安全地使用它,如"Eval really is dangerous"所示。想象一下,如果一个对手正在调用你的API,而不是使用你的表单,那么只是向你发送一些东西

body=byebye&table=__import__("os").system("rm -rf /")

根据您的设置,您可能会丢失整个系统或容器,或者您拥有的东西。这不是他们唯一能做的事情。他们可以使用整个Python表达式。

在您的情况下,处理用户选择的表(models)的正确方法是查找:

the_5_tables = {
    "table_1": Table1,
    "table_2": Table2,
    # etc.
}

那么你需要做的就是

#table to be searched
model = the_5_tables[request.params['table']]

这具有将用户能够使用的表列入白名单的附加好处,即使当前作用域可以访问其他表。

使用实际的Column对象而不是键来生成过滤器更容易:

results = {}

for col in model.__table__.columns:
    try:
        searchdb = request.dbsession.query(model).filter(col == search).all()                                     
        results[col.key] = searchdb

    except Exception as e:
        print(f"Unhandled error: {e}")
        continue

return results

遗憾的是,与表中的列相比,这将导致数据库的往返次数。现在,我猜测裸except:是为了掩盖类型不匹配引起的错误,例如当你试图搜索数字列时。您可以进一步检查列以避免这种情况:

from sqlalchemy.types import String

# ...

results = {}

for col in model.__table__.columns:
    if isinstance(col.type, String):
        searchdb = request.dbsession.query(model).filter(col == search).all()                                     
        results[col.key] = searchdb

return results

更进一步,如果你真的不想对匹配的列感兴趣,你可以只形成一个查询,例如:

from sqlalchemy import literal
from sqlalchemy.types import String

# ...

str_cols = [c for c in model.__table__.c if isinstance(c.type, String)]
results = request.dbsession.query(model).filter(literal(search).in_(str_cols)).all()                                     
return results

虽然有点hacky,但仍然可以在单个查询中获取哪个column(s)匹配(这不是那么有用;在查询后在Python中执行相同操作是微不足道的):

from sqlalchemy import func

# ...

results = request.dbsession.query(
        model,
        func.concat_ws(",", *[
            func.if_(c == search, c.key, None)
            for c in str_cols
        ]).label("in_columns")).\
    filter(literal(search).in_(str_cols)).all()                                     
return results

in_columns将是comma分离的匹配的列名string。