简体   繁体   English

使用sql alchemy查询有一种方法来迭代表中的所有列

[英]With sql alchemy query is there a way to iterate over all columns in a table

I want to create a search function on my web page that is linked to database of 5 tables. 我想在我的网页上创建一个链接到5个表的数据库的搜索功能。

The idea is that the user can select the table to search and enter a search term and it will return a list of results. 这个想法是用户可以选择要搜索的表并输入搜索词,它将返回结果列表。

Obviously I could do this by using 5 if-statements but seems like there should be an easier way. 显然我可以通过使用5个if语句来做到这一点,但似乎应该有一个更简单的方法。

This code is what I have tried but for some reason when I put item and table variables in to the query instead of the actual column names it doesn't work. 这段代码是我试过的,但出于某种原因,当我将itemtable变量放入查询而不是实际的列名时,它不起作用。 It only works if i type the specific terms in table and table.item . 它只适用于我在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)

I want to know if this is possible or I should just give up and write 5 if statements 我想知道这是否可行,或者我应该放弃并写下5 if语句

Please don't use eval() , it is mostly evil . 请不要使用eval()它主要是邪恶的 You cannot use it safely on user input, as shown in "Eval really is dangerous" . 您不能安全地在用户输入上使用它,如“Eval确实很危险”中所示 Imagine an adversary who is calling your API and instead of using your form or such just sends you something along the lines of 想象一下,如果一个对手正在调用你的API,而不是使用你的表单,那么只是向你发送一些东西

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

Depending on your setup you might lose your entire system, or the container, or what have you. 根据您的设置,您可能会丢失整个系统或容器,或者您拥有的东西。 And that is not the only thing they could do. 这不是他们唯一能做的事情。 They have the whole of Python expressions to play with. 他们可以使用整个Python表达式。

In your case a proper way to handle user selected tables (models) is to have a lookup: 在您的情况下,处理用户选择的表(模型)的正确方法是进行查找:

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

Then all you need to do is 那么你需要做的就是

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

This has the added benefit of whitelisting what tables the user is able to use, even if the current scope has access to others. 这具有将用户能够使用的表列入白名单的附加好处,即使当前作用域可以访问其他表。

Producing the filters is easier done using the actual Column objects instead of their keys: 使用实际的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

This will unfortunately make as many roundtrips to the database as you have columns in your table. 遗憾的是,与表中的列相比,这将导致数据库的往返次数。 Now, I'm guessing that the bare except: was there in order to mask errors arising from type mismatches, for example when you're trying to search against a numeric column. 现在,我猜测except:是为了掩盖由于类型不匹配引起的错误,例如当你试图搜索数字列时。 You could inspect the columns further to avoid that: 您可以进一步检查列以避免这种情况:

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

Taking it even further, if you're not actually that interested in which columns matched, you could just form a single query such as: 更进一步,如果你真的不想对匹配的列感兴趣,你可以只形成一个查询,例如:

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

Though a bit hacky, it's still possible to also fetch which column(s) matched in a single query (this is not that useful; it's trivial to do the same in Python after the query): 虽然有点hacky,但仍然可以在单个查询中获取匹配的列(这不是那么有用;在查询后在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

The in_columns will be a comma separated string of column names that matched. in_columns将是一个逗号分隔的匹配的列名字符串。

Plese don't use list (builtin type) for naming variables/identifiers. 请勿使用list (内置类型)来命名变量/标识符。

...
if ...

    table = eval(request.params['table']).__table__  # <-- find a better way to create table instance
    results = {}
    for col in table.c:
        try:
            searchdb = request.dbsession.query(table).filter(col == search).all()
            results[item] = searchdb
        except:
            continue
    return results

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM