【问题标题】:With sql alchemy query is there a way to iterate over all columns in a table使用 sql alchemy 查询是否可以遍历表中的所有列
【发布时间】:2019-11-24 10:31:56
【问题描述】:

我想在我的网页上创建一个链接到 5 个表的数据库的搜索功能。

这个想法是用户可以选择要搜索的表并输入搜索词,它将返回结果列表。

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

这段代码是我尝试过的,但由于某种原因,当我将 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 语句

【问题讨论】:

    标签: python mysql sqlalchemy pyramid


    【解决方案1】:

    请不要使用eval()it is mostly evil。您不能在用户输入时安全地使用它,如"Eval really is dangerous" 所示。想象一个对手正在调用您的 API,而不是使用您的表单或类似的方式,只是向您发送类似

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

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

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

    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,但仍然可以在单个查询中获取匹配的列(这不是很有用;在查询之后在 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 将是一个逗号分隔的匹配列名字符串。

    【讨论】:

    • 非常感谢!这已经奏效了,我再也不会使用 eval 了!
    【解决方案2】:

    请不要使用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
    

    【讨论】:

      猜你喜欢
      • 2018-11-22
      • 2015-03-10
      • 2012-06-20
      • 1970-01-01
      • 2010-10-27
      • 1970-01-01
      • 2021-11-20
      • 2011-03-06
      • 1970-01-01
      相关资源
      最近更新 更多