SQLAlchemy: Perform Case-Insensitive Filter

Updated: January 3, 2024 By: Guest Contributor Post a comment

Overview

SQLAlchemy is a powerful and flexible ORM (Object-Relational Mapping) library for Python. It allows developers to work with databases using Python classes and objects, abstracting the raw SQL queries that interact with the database engine.

In this tutorial, you will learn how to apply case-insensitive filters in SQLAlchemy, which is an essential technique for building robust database queries that are not affected by the case of the data.

When you need to filter query results, the case of the values can matter. Sometimes, we want our filter to be case-insensitive to provide better usability and reliability. In SQLAlchemy, this is achieved using various functions and methods such as func.lower().

Basic Filtering with SQLAlchemy

Let’s start with a basic filtering example where case sensitivity is the default behavior. To set up our environment, we first need to define our model and create a session:

from sqlalchemy import create_engine, Column, String
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker

Base = declarative_base()

class User(Base):
    __tablename__ = 'users'
    username = Column(String, primary_key=True)

# Engine/connection setup
engine = create_engine('sqlite:///example.db', echo=True)
Base.metadata.create_all(engine)

# Creating a session
Session = sessionmaker(bind=engine)
session = Session()

Now let’s add a filter to our queries in the standard case-sensitive way:

# Standard case-sensitive filter
user = session.query(User).filter(User.username == 'JohnDoe').first()

Case-Insensitive Filtering with SQLAlchemy

To implement a case-insensitive filter, you can use the func.lower() from SQLAlchemy:

from sqlalchemy import func

# Case-insensitive filter using func.lower()
user = session.query(User).filter(func.lower(User.username) == func.lower('JohnDoe')).first()

This query will ignore case when filtering the username field. It’s particularly useful when dealing with user input where the case of the input might vary.

Advanced Case-Insensitive Queries

As you become more familiar with SQLAlchemy, you might encounter more complex cases. For example, you might want to perform a case-insensitive search across multiple fields or use pattern matching. Here’s how to extend our case-insensitive logic to those scenarios.

# Case-insensitive filter across multiple fields using or_
from sqlalchemy import or_

user = session.query(User).filter(
    or_(
        func.lower(User.username) == func.lower('JohnDoe'),
        func.lower(User.email) == func.lower('[email protected]')
    )
).first()

# Case-insensitive LIKE pattern matching
user = session.query(User).filter(
    func.lower(User.username).like(func.lower('%doe%'))
).all()

You can also integrate these filters with other SQLAlchemy features for even more powerful querying.

Using Hybrid Properties for Case-Insensitive Filtering

Hybrid properties offer a way to define properties on Python classes that behave like Column properties on the database level. We can use this to streamline our case-insensitive filters.

from sqlalchemy.ext.hybrid import hybrid_property

class User(Base):
    __tablename__ = 'users'
    _username = Column('username', String)

    @hybrid_property
    def username(self):
        return self._username.lower()

    @username.expression
    def username(cls):
        return func.lower(cls._username)

# Filtering by hybrid property
user = session.query(User).filter(User.username == 'johndoe').first()

Conclusion

In conclusion, performing case-insensitive filters in SQLAlchemy is a versatile practice that can significantly enhance the user experience by removing restrictions imposed by the case-sensitivity of strings in databases. Utilizing the func.lower() function to normalize the cases of the column and the input values enables comparison on a level playing field. As your requirements grow more complex, the power of SQLAlchemy’s functionality shines through with features like hybrid properties and pattern matching that can keep your queries both robust and efficient.