Queries
Queries allow you to retrieve data from your database. Every query you build with ORM objects
is a subclass of QueryBuilder
at its base. This class provides a number of methods to help you
construct your query and pass the correct types for downstream processing.
Shortcuts
We offer convenience functions for the most common SQL operations. This avoids
the need to instantiate a QueryBuilder
directly every time you want a new SQL
query and typically results in cleaner code.
select
Creates a SELECT query to fetch data from the database. This is a shortcut function that creates and returns a new QueryBuilder instance.
Parameters
- Name
fields
- Type
- T | Type[T] | tuple[T | Type[T]] | tuple[T | Type[T], T2 | Type[T2]] | tuple[T | Type[T], T2 | Type[T2], T3 | Type[T3], *Ts]
- Description
The fields to select. Can be: - A single field or model class (e.g., User.id or User) - A tuple of fields (e.g., (User.id, User.name)) - A tuple of model classes (e.g., (User, Post))
Code
# Select all fields from User users = await conn.execute(select(User)) # Select specific fields results = await conn.execute(select((User.id, User.name))) # Select with conditions active_users = await conn.execute( select(User) .where(User.is_active == True) .order_by(User.created_at, "DESC") .limit(10) )
update
Creates an UPDATE query to modify existing records in the database. This is a shortcut function that creates and returns a new QueryBuilder instance.
Parameters
- Name
model
- Type
- Type[TableBase]
- Description
The model class representing the table to update
Code
# Update all users' status await conn.execute( update(User) .set(User.status, "inactive") .where(User.last_login < datetime.now() - timedelta(days=30)) ) # Update multiple fields with conditions await conn.execute( update(User) .set(User.verified, True) .set(User.verification_date, datetime.now()) .where(User.email_confirmed == True) )
delete
Creates a DELETE query to remove records from the database. This is a shortcut function that creates and returns a new QueryBuilder instance.
Parameters
- Name
model
- Type
- Type[TableBase]
- Description
The model class representing the table to delete from
Code
# Delete inactive users await conn.execute( delete(User) .where(User.is_active == False) ) # Delete with complex conditions await conn.execute( delete(User) .where( and_( User.created_at < datetime.now() - timedelta(days=90), User.email_confirmed == False ) ) )
Raw Queries
The QueryBuilder owns all construction of the SQL string given python method chaining. Each function call returns a reference to self, so you can construct as many queries as you want in a single line of code.
Internally we store most input-arguments as-is. We provide runtime value-checking to make sure the right objects are being passed in to query manipulation functions so our final build() will deterministically succeed if the query build was successful.
Note that this runtime check-checking validates different types than the static
analysis. To satisfy Python logical operations (like join(ModelA.id == ModelB.id)
) we
have many overloaded operators that return objects at runtime but are masked to their
Python types for the purposes of static analysis. This implementation detail should
be transparent to the user but is noted in case you see different types through
runtime inspection than you see during the typehints.
Class Methods
- Name
select
- Return type
- QueryBuilder[T, Literal['SELECT']] | QueryBuilder[tuple[T], Literal['SELECT']] | QueryBuilder[tuple[T, T2], Literal['SELECT']] | QueryBuilder[tuple[T, T2, T3], Literal['SELECT']] | QueryBuilder[tuple[T, T2, T3, *Ts], Literal['SELECT']]
- Description
Creates a SELECT query to fetch data from the database.
- Name
update
- Return type
- QueryBuilder[None, Literal['UPDATE']]
- Description
Creates a new update query for the given model. Returns the same QueryBuilder that is now flagged as an UPDATE query.
- Name
delete
- Return type
- QueryBuilder[None, Literal['DELETE']]
- Description
Creates a new delete query for the given model. Returns the same QueryBuilder that is now flagged as a DELETE query.
- Name
where
- Return type
- Description
Adds WHERE conditions to filter the query results. Multiple conditions are combined with AND. For OR conditions, use the
or_
function.
- Name
order_by
- Return type
- Description
Adds an ORDER BY clause to sort the query results.
- Name
join
- Return type
- Description
Adds a JOIN clause to combine data from multiple tables.
- Name
set
- Return type
- Description
Sets a column to a specific value in an update query.
- Name
limit
- Return type
- Description
Limits the number of rows returned by the query.
- Name
offset
- Return type
- Description
Skips the specified number of rows before returning results.
- Name
group_by
- Return type
- Description
Groups the results by specified fields, typically used with aggregate functions.
- Name
having
- Return type
- Description
Adds HAVING conditions to filter grouped results based on aggregate values.
- Name
distinct_on
- Return type
- Description
Adds a DISTINCT ON clause to remove duplicate rows based on specified fields.
- Name
text
- Return type
- Description
Uses a raw SQL query instead of the query builder.
- Name
for_update
- Return type
- QueryBuilder[P, QueryType]
- Description
Adds FOR UPDATE clause to the query. This is useful for pessimistic locking. Multiple calls will be combined, with the most restrictive options taking precedence.
- Name
build
- Return type
- tuple[str, list[Any]]
- Description
Builds and returns the final SQL query string and parameter values.
Code
# Basic SELECT query query = ( QueryBuilder() .select(User) .where(User.is_active == True) .order_by(User.created_at, "DESC") ) # Complex query with joins and aggregates query = ( QueryBuilder() .select((User.name, func.count(Order.id))) .join(Order, Order.user_id == User.id) .where(Order.status == "completed") .group_by(User.name) .having(func.count(Order.id) > 5) )
Represents metadata for SQL aggregate functions and other SQL function operations. This class bridges the gap between Python function calls and their SQL representations, maintaining type information and original field references.
Class Constructor
- Name
literal
- Type
- QueryLiteral
- Description
The SQL representation of the function call
- Name
original_field
- Type
- DBFieldClassDefinition
- Description
The database field this function operates on
- Name
local_name
- Type
- str | None
- Description
Default: None
Optional alias for the function result in the query
Class Methods
- Name
to_query
- Return type
- Description
Converts the function metadata to its SQL representation.
Code
# Internal representation of function calls: metadata = FunctionMetadata( literal=QueryLiteral("count(users.id)"), original_field=User.id, local_name="user_count" ) # Used in query: SELECT count(users.id) AS user_count
Abstract base class for database fields that can be used in comparisons. Provides standard comparison operators and methods for SQL query generation.
This class implements Python's comparison magic methods (eq, ne, etc.) to enable natural syntax for building SQL queries. It also provides additional methods for SQL-specific operations like IN, LIKE, and NULL comparisons.
Class Methods
- Name
in_
- Return type
- bool
- Description
Implements SQL IN operator. Checks if the field's value is in a sequence of values.
- Name
not_in
- Return type
- bool
- Description
Implements SQL NOT IN operator. Checks if the field's value is not in a sequence of values.
- Name
like
- Return type
- bool
- Description
Implements SQL LIKE operator for pattern matching. Case-sensitive string pattern matching.
- Name
not_like
- Return type
- bool
- Description
Implements SQL NOT LIKE operator. Case-sensitive string pattern non-matching.
- Name
ilike
- Return type
- bool
- Description
Implements PostgreSQL ILIKE operator. Case-insensitive string pattern matching.
- Name
not_ilike
- Return type
- bool
- Description
Implements PostgreSQL NOT ILIKE operator. Case-insensitive string pattern non-matching.
- Name
to_query
- Return type
- tuple[QueryLiteral, list[Any]]
- Description
Abstract method to convert the field to its SQL representation. Must be implemented by subclasses.
Code
# ComparisonBase enables these operations on database fields: User.age >= 21 User.status == "active" User.name.like("%John%") User.role.in_(["admin", "moderator"]) User.deleted_at.is_(None)
Functions
func
A global instance of FunctionBuilder that provides SQL function operations for use in queries. This instance offers a comprehensive set of SQL functions including aggregates, string operations, mathematical functions, date/time manipulations, and type conversions.
Available function categories:
- Aggregate Functions: count, sum, avg, min, max, array_agg, string_agg
- String Functions: lower, upper, length, trim, substring
- Mathematical Functions: abs, round, ceil, floor, power, sqrt
- Date/Time Functions: date_trunc, date_part, extract, age, date
- Type Conversion: cast, to_char, to_number, to_timestamp
Code
from iceaxe import func, select # Aggregate functions total_users = await conn.execute(select(func.count(User.id))) avg_salary = await conn.execute(select(func.avg(Employee.salary))) unique_statuses = await conn.execute(select(func.distinct(User.status))) # String operations users = await conn.execute(select(( User.id, func.lower(User.name), func.upper(User.email), func.length(User.bio) ))) # Date/time operations monthly_stats = await conn.execute(select(( func.date_trunc('month', Event.created_at), func.count(Event.id) )).group_by(func.date_trunc('month', Event.created_at))) # Mathematical operations account_stats = await conn.execute(select(( Account.id, func.abs(Account.balance), func.ceil(Account.interest_rate) ))) # Type conversions converted = await conn.execute(select(( func.cast(User.string_id, int), func.to_char(User.created_at, 'YYYY-MM-DD'), func.cast(User.status_str, UserStatus) ))) # Complex aggregations department_stats = await conn.execute( select(( Department.name, func.array_agg(Employee.name), func.string_agg(Employee.email, ','), func.sum(Employee.salary) )).group_by(Department.name) )
Builder class for SQL aggregate functions and other SQL operations. Provides a Pythonic interface for creating SQL function calls with proper type hints.
This class is typically accessed through the global func
instance:
Class Methods
- Name
count
- Return type
- int
- Description
Creates a COUNT aggregate function call.
- Name
distinct
- Return type
- T
- Description
Creates a DISTINCT function call that removes duplicate values.
- Name
sum
- Return type
- T
- Description
Creates a SUM aggregate function call.
- Name
avg
- Return type
- T
- Description
Creates an AVG aggregate function call.
- Name
max
- Return type
- T
- Description
Creates a MAX aggregate function call.
- Name
min
- Return type
- T
- Description
Creates a MIN aggregate function call.
- Name
abs
- Return type
- T
- Description
Creates an ABS function call to get the absolute value.
- Name
date_trunc
- Return type
- T
- Description
Truncates a timestamp or interval value to specified precision.
- Name
date_part
- Return type
- float
- Description
Extracts a subfield from a date/time value.
- Name
extract
- Return type
- int
- Description
Extracts a subfield from a date/time value using SQL standard syntax.
- Name
age
- Return type
- T
- Description
Calculates the difference between two timestamps. If reference is not provided, current_date is used.
- Name
date
- Return type
- T
- Description
Converts a timestamp to a date by dropping the time component.
- Name
lower
- Return type
- T
- Description
Converts string to lowercase.
- Name
upper
- Return type
- T
- Description
Converts string to uppercase.
- Name
length
- Return type
- int
- Description
Returns length of string.
- Name
trim
- Return type
- T
- Description
Removes whitespace from both ends of string.
- Name
substring
- Return type
- T
- Description
Extracts substring.
- Name
round
- Return type
- T
- Description
Rounds to nearest integer.
- Name
ceil
- Return type
- T
- Description
Rounds up to nearest integer.
- Name
floor
- Return type
- T
- Description
Rounds down to nearest integer.
- Name
power
- Return type
- T
- Description
Raises a number to the specified power.
- Name
sqrt
- Return type
- T
- Description
Calculates square root.
- Name
array_agg
- Return type
- list[T]
- Description
Collects values into an array.
- Name
string_agg
- Return type
- str
- Description
Concatenates values with delimiter.
- Name
cast
- Return type
- T
- Description
Converts value to specified type.
- Name
to_char
- Return type
- str
- Description
Converts value to string with format.
- Name
to_number
- Return type
- float
- Description
Converts string to number with format.
- Name
to_timestamp
- Return type
- datetime
- Description
Converts string to timestamp with format.
Code
from iceaxe import func # In a query: query = select(( User.name, func.count(User.id), func.max(User.age) ))
Conditions
If you want to add conditions to your query, you can use the where
method. If you pass multiple
conditions to this, they'll be chained together as an AND
filter. To
combine OR and AND predicates together, you can use and_
and or_
.
and_
Combines multiple conditions with logical AND. All conditions must be true for the group to be true.
Parameters
- Name
conditions
- Type
- bool
- Description
Default: ()
Variable number of conditions to combine
Code
query = select(User).where( and_( User.age >= 21, User.status == "active", User.role == "member" ) )
or_
Combines multiple conditions with logical OR. At least one condition must be true for the group to be true.
Parameters
- Name
conditions
- Type
- bool
- Description
Default: ()
Variable number of conditions to combine
Code
query = select(User).where( or_( User.role == "admin", and_( User.role == "moderator", User.permissions.contains("manage_users") ) ) )