不区分大小写的 Flask-SQLAlchemy 查询

问题描述 投票:0回答:7

我正在使用 Flask-SQLAlchemy 从用户数据库中进行查询;然而,虽然

user = models.User.query.filter_by(username="ganye").first()

会回来

<User u'ganye'>

user = models.User.query.filter_by(username="GANYE").first()

退货

None

我想知道是否有一种方法以不区分大小写的方式查询数据库,以便第二个示例仍然会返回

<User u'ganye'>
python sqlalchemy flask-sqlalchemy case-insensitive
7个回答
287
投票

您可以使用过滤器中的

lower
upper
函数来完成此操作:

from sqlalchemy import func
user = models.User.query.filter(func.lower(User.username) == func.lower("GaNyE")).first()

另一种选择是使用

ilike
而不是
like
进行搜索:

.query.filter(Model.column.ilike("ganye"))

21
投票

改进@plaes的答案,如果您只指定所需的列,这个答案将使查询更短:

user = models.User.query.with_entities(models.User.username).\
filter(models.User.username.ilike("%ganye%")).all()

如果需要使用 Flask 的 jsonify 来实现 AJAX 目的,然后在 javascript 中使用 data.result:

访问它,上面的示例非常有用
from flask import jsonify
jsonify(result=user)

12
投票

你可以做

user = db.session.query(User).filter_by(func.lower(User.username)==func.lower("GANYE")).first()

或者你可以使用ilike功能

 user = db.session.query(User).filter_by(User.username.ilike("%ganye%")).first()

3
投票

我认为最好的解决方案是

func.lower(User.username).contains(username.lower())

它会产生像这样的sql

WHERE (lower(user.username) LIKE '%%' || %s || '%%')

这意味着您的 db

username
将转换为小写,您的 python 值
username
也将转换为小写。所以现在你不用担心大小写敏感度了

如果您的

username
Sara Smith
它将适用于
ara
sar
Sara
或活动
a S


2
投票

如果适合您的用例,您可以考虑在列上设置自定义排序规则,以便列自动以不区分大小写的方式处理比较。

值得注意的是:

  • 排序规则将应用于该列上的所有查询
  • 排序规则也适用于
    ORDER BY
    子句
  • 排序规则可以直接在查询中指定,而不是在列上定义
    • 这可能会导致性能损失
  • 排序规则定义往往是 RDBMS/区域设置/特定于语言的 - 请参阅相关文档
  • 排序规则名称可能无法在不同的 RDBMS 之间移植
  • 可用的排序规则属性可能因 RDBMS 而异

换句话说,在使用此功能之前请查阅 RDBMS 的文档。

此示例脚本展示了如何使用 MySQL、Postgresql(注意特殊情况)和 Sqlite 的排序规则;每个 RDBMS 都会返回查询值的所有三个可能结果。

import sqlalchemy as sa
from sqlalchemy import orm

data = {
    'mysql': ('mysql:///test', 'utf8mb4_general_ci'),
    'postgresql': ('postgresql:///test', 'coll'),
    'sqlite': ('sqlite://', 'NOCASE'),
}


for dialect, (uri, collation) in data.items():
    Base = orm.declarative_base()

    class Test(Base):
        __tablename__ = 't16573095'

        id = sa.Column(sa.Integer, primary_key=True)
        name = sa.Column(sa.String(32, collation=collation))

    engine = sa.create_engine(uri, echo=False, future=True)
    Base.metadata.drop_all(engine)

    if dialect == 'postgresql':
        # Postgres collations are more complicated
        # - read the docs!
        with engine.begin() as conn:
            conn.execute(sa.text('DROP COLLATION IF EXISTS coll'))
            stmt = """CREATE COLLATION coll (provider='icu', locale='und-u-ks-level2', deterministic=false)"""
            conn.execute(sa.text(stmt))

    Base.metadata.create_all(engine)
    Session = orm.sessionmaker(engine, future=True)

    with Session.begin() as s:
        instances = [Test(name=name) for name in ['GANYE', 'ganye', 'gAnYe']]
        s.add_all(instances)

    with Session() as s:
        results = s.execute(sa.select(Test.name).where(Test.name == 'GaNyE')).scalars()
        print(f'{dialect:-<12}')
        for name in results:
            print(name)
        print('-' * 12)

要在查询中指定排序规则,请使用属性的 collate 方法:

with Session() as s:
    query = sa.select(Test).where(Test.name.collate('coll') == 'GaNyE')
    results = s.execute(query)

0
投票
  • 按一个字段
    uname = "ganye"
    user = models.User.query.filter(User.username.ilikef(f"%{uname}%").first()
    
  • 按多个领域
    return db.session.query(Post).filter(
        Post.title.ilike(f"%{search_query}%")
        | Post.body.ilike(f"%{search_query}%")
    )
    

0
投票

对于未来的读者 - SQLAlchemy 2.0 的等效项现在是 ColumnOperators.ilike 方法。 举一个声明性的例子:

class User(DeclarativeBase):
    username: str

select(User).where(User.username.ilike("GANYE"))

将渲染为

lower(User.username) LIKE lower("GANYE")

© www.soinside.com 2019 - 2024. All rights reserved.