【问题标题】:SQLAlchemy - Search entire DB model columns for a patternSQLAlchemy - 在整个数据库模型列中搜索模式
【发布时间】:2019-07-24 16:01:36
【问题描述】:

我有一个数据库,我需要返回与查询匹配或看起来相似的任何值。

使用 flask-sqlalchemy 我可以手动过滤,但我无法使用列表推导或任何其他 Python 方法获取对象列表。

我已经尝试创建一个包含所有模型列(我有一个列表)的字典,并将搜索值传递给 filter_by 查询

column_dict_and_value = {column_name:'value' for column_name in columns}    
a = model.query.filter_by(**column_dict_and_value).all()

...但甚至不返回现有对象,尽管

a = model.query.filter_by(column_name='value').all()

实际返回对象。

我尝试了filter(.like('%')),它有点工作

a = model.query.filter(model.column_name.like('valu%')).all()

返回与该模式匹配但针对该列的所有对象的列表,我想遍历所有列。基本上是一个完整的搜索,因为我不确定用户要查找什么,如果查询存在于任何列上,我想显示对象,如果查询不完整,我想显示对象列表,例如任何搜索中的 * .

我尝试使用列表推导来遍历模型属性,但显然我不允许这样做:

a = [model.query.filter(model.column.like('valu%')) for column in columns]

...这抱怨 column 不是模型的属性,由于语法,这是有道理的,但无论如何我不得不尝试,innit?

我是数据库和类对象方面的新手,所以请保持温和。我试图寻找类似的答案,但找不到适合我的答案。 filter_by(one_column, second_column, etc) 在我看来并不像 pythonic,如果出于任何原因我更改模型,我也需要更改查询,因为在我看来创建模型的 dict 理解更万无一失。

解决方案

根据calestini 提出的答案,我编写了这个似乎可以解决问题的代码。它还会清理列表,因为如果 all() 没有任何结果(因为它会查看每个字段),它会返回 None 并添加到列表中。我宁愿它干净。

注意:我的所有字段都是文本。如果您的不同,请查看 calestini 提出的第三个解决方案。不过没有测试过。

columns = [
"foo",
"bar",
"foobar"
]


def list_everything(search):
    d = {column: search for column in columns}
    raw = [
        model.query.filter(getattr(model, col).ilike(f"{val}%")).all()
        for col, val in d.items()
    ]
    return [item for item in raw if item]

如果我有更好的解决方案,我会继续优化和更新这段代码。非常感谢

【问题讨论】:

标签: python sqlalchemy flask-sqlalchemy


【解决方案1】:

可能是你没有得到任何结果的原因

a = model.query.filter_by(**column_dict_and_value).all()

是因为filter_by 正在测试直接相等性。在您的情况下,您正在寻找一种模式,因此您需要循环并使用 filter 而不是 filter_by

假设您的所有列都是StringText 类型,您可以尝试以下操作:

a = model.query

for col, val in column_dict_and_value.items():
   a = a.filter(getattr(model, col).ilike(f'{val}%')) #ilike for case insensitive

a = a.all()

或与与

问题也可能是,在您的情况下,您可能正在测试 intersection,但期待 union。换句话说,只有当模式在 all 列中匹配时,您才会返回某些内容。如果您希望在 any 列匹配的情况下获得结果,那么您需要稍微调整一下代码:

condition = or_(*[getattr(model, col).ilike(f'{val}%') for col, val in column_dict_and_value.items()])

a = model.query.filter(codition).all()

现在如果你确实在列之间还有其他数据类型,那么你可以尝试先验证列类型,然后通过相同的逻辑:

for col, val in column_dict_and_value.items():
    ## check if field python equivalent is string
    if isinstance(class_.__table__.c[col].type.python_type, str):
       ...

【讨论】:

  • 我不能让你的代码正常工作,第一个也没有找到任何东西,第二个“条件”抛出一个ValueError: too many values to unpack (expected 2)。没有尝试过第三种方式,因为我所有的字段都是字符串,因为我不需要对数据进行操作,只需显示它。我觉得我快到了,但令人沮丧......
  • 我没有将其标记为已接受,因为我无法运行您的代码并且我不确定它是否正确。不过可能是这样。请仔细检查,我会接受。
  • 道歉@jose,我还没有真正运行你的代码,让我尝试运行一个烧瓶应用程序并更正答案。感谢您指出。
  • @JoseRodriguez 修正了这一行,它缺少 items() 来解压字典。
猜你喜欢
  • 1970-01-01
  • 2014-12-09
  • 1970-01-01
  • 2014-06-15
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多