The database query builder provides a fluent interface to create and run database queries. It can be used to perform most database operations in your application, and works on all supported database systems.
Note
Since Orator uses DBAPI packages under the hood, there is no need to clean parameters passed as bindings.
Note
The underlying DBAPI connections are automatically configured to return dictionaries rather than the default tuple representation.
New in version 0.9: The returned rows also supports accessing the column values by attribute:
user = db.table('users').first()
name = user['name']
# is equivalent to
name = user.name
Changed in version 0.9: The returned rows will now be Collections to be more consistent with the behavior of the ORM.
users = db.table('users').get()
for user in users:
print(user['name'])
for users in db.table('users').chunk(100):
for user in users:
# ...
user = db.table('users').where('name', 'John').first()
print(user['name'])
user = db.table('users').where('name', 'John').pluck('name')
roles = db.table('roles').lists('title')
This method will return a list of role titles. It can return a dictionary if you pass an extra key parameter.
roles = db.table('roles').lists('title', 'name')
users = db.table('users').select('name', 'email').get()
users = db.table('users').distinct().get()
users = db.table('users').select('name as user_name').get()
query = db.table('users').select('name')
users = query.add_select('age').get()
users = db.table('users').where('age', '>', 25).get()
users = db.table('users').where('age', '>', 25).or_where('name', 'John').get()
users = db.table('users').where_between('age', [25, 35]).get()
users = db.table('users').where_not_between('age', [25, 35]).get()
users = db.table('users').where_in('id', [1, 2, 3]).get()
users = db.table('users').where_not_in('id', [1, 2, 3]).get()
users = db.table('users').where_null('updated_at').get()
query = db.table('users').order_by('name', 'desc')
query = query.group_by('count')
query = query.having('count', '>', 100)
users = query.get()
users = db.table('users').skip(10).take(5).get()
users = db.table('users').offset(10).limit(5).get()
The query builder can also be used to write join statements.
db.table('users') \
.join('contacts', 'users.id', '=', 'contacts.user_id') \
.join('orders', 'users.id', '=', 'orders.user_id') \
.select('users.id', 'contacts.phone', 'orders.price') \
.get()
db.table('users').left_join('posts', 'users.id', '=', 'posts.user_id').get()
You can also specify more advance join clauses:
clause = JoinClause('contacts').on('users.id', '=', 'contacts.user_id').or_on(...)
db.table('users').join(clause).get()
If you would like to use a “where” style clause on your joins, you may use the where and orWhere methods on a join. Instead of comparing two columns, these methods will compare the column against a value:
clause = JoinClause('contacts').on('users.id', '=', 'contacts.user_id').where('contacts.user_id', '>', 5)
db.table('users').join(clause).get()
Sometimes you may need to create more advanced where clauses such as “where exists” or nested parameter groupings. It is pretty easy to do with the Orator query builder
db.table('users') \
.where('name', '=', 'John') \
.or_where(
db.query().where('votes', '>', 100).where('title', '!=', 'admin')
).get()
The query above will produce the following SQL:
SELECT * FROM users WHERE name = 'John' OR (votes > 100 AND title != 'Admin')
db.table('users').where_exists(
db.table('orders').select(db.raw(1)).where_raw('order.user_id = users.id')
)
The query above will produce the following SQL:
SELECT * FROM users
WHERE EXISTS (
SELECT 1 FROM orders WHERE orders.user_id = users.id
)
The query builder also provides a variety of aggregate methods, `
such as count
, max
, min
, avg
, and sum
.
users = db.table('users').count()
price = db.table('orders').max('price')
price = db.table('orders').min('price')
price = db.table('orders').avg('price')
total = db.table('users').sum('votes')
Sometimes you may need to use a raw expression in a query.
These expressions will be injected into the query as strings, so be careful not to create any SQL injection points!
To create a raw expression, you may use the raw()
method:
db.table('users') \
.select(db.raw('count(*) as user_count, status')) \
.where('status', '!=', 1) \
.group_by('status') \
.get()
db.table('users').insert(email='foo@bar.com', votes=0)
db.table('users').insert({
'email': 'foo@bar.com',
'votes': 0
})
Note
It is important to note that there is two notations available. The reason is quite simple: the dictionary notation, though a little less practical, is here to handle columns names which cannot be passed as keywords arguments.
If the table has an auto-incrementing id, use insert_get_id
to insert a record and retrieve the id:
id = db.table('users').insert_get_id({
'email': 'foo@bar.com',
'votes': 0
})
db.table('users').insert([
{'email': 'foo@bar.com', 'votes': 0},
{'email': 'bar@baz.com', 'votes': 0}
])
db.table('users').where('id', 1).update(votes=1)
db.table('users').where('id', 1).update({'votes': 1})
Note
Like the insert
statement, there is two notations available.
The reason is quite simple: the dictionary notation, though a little less practical, is here to handle
columns names which cannot be passed as keywords arguments.
db.table('users').increment('votes') # Increment the value by 1
db.table('users').increment('votes', 5) # Increment the value by 5
db.table('users').decrement('votes') # Decrement the value by 1
db.table('users').decrement('votes', 5) # Decrement the value by 5
You can also specify additional columns to update:
db.table('users').increment('votes', 1, name='John')
db.table('users').where('age', '<', 25).delete()
db.table('users').delete()
db.table('users').truncate()
The query builder provides a quick and easy way to “union” two queries:
first = db.table('users').where_null('first_name')
users = db.table('users').where_null('last_name').union(first).get()
The union_all
method is also available.
The query builder includes a few functions to help you do “pessimistic locking” on your SELECT statements.
To run the SELECT statement with a “shared lock”, you may use the shared_lock
method on a query:
db.table('users').where('votes', '>', 100).shared_lock().get()
To “lock for update” on a SELECT statement, you may use the lock_for_update
method on a query:
db.table('users').where('votes', '>', 100).lock_for_update().get()