If it fits your use case, you may consider setting a custom collation on the column, such that the column automatically handles comparisons in a case-insensitive manner.
It's worth noting:
- the collation will apply to all queries on the column
- the collation will apply to
ORDER BY
clauses as well
- a collation may be specified directly in queries, rather than being defined on the column
- this may potentially incur performance penalties
- collation definitions tend to be RDBMS / locale / language-specific - consult the relevant documentation
- collation names may not be portable between different RDBMS
- available collation attributes may vary by RDBMS
in other words, consult your RDBMS's docs before using this feature.
This example script shows how you might use collations for MySQL, Postgresql (note the special-casing) and Sqlite; each RDBMS returns all three possible results for the query value.
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)
To specify a collation in a query, use an attribute's collate method:
with Session() as s:
query = sa.select(Test).where(Test.name.collate('coll') == 'GaNyE')
results = s.execute(query)
filter_by
method where the username column is indexed? – Lowerclassman