peewee v0.6.2 documentation

This Page

Querying API

Constructing queries

Queries in peewee are constructed one piece at a time.

The “pieces” of a peewee query are generally representative of clauses you might find in a SQL query. All pieces are chainable so rather complex queries are possible.

>>> user_q = User.select() # <-- query is not executed
>>> user_q
<peewee.SelectQuery object at 0x7f6b0810c610>
>>> [u.username for u in user_q] # <-- query is evaluated here
[u'admin', u'staff', u'editor']

We can build up the query by adding some clauses to it:

>>> user_q = user_q.where(username__in=['admin', 'editor']).order_by(('username', 'desc'))
>>> [u.username for u in user_q] # <-- query is re-evaluated here
[u'editor', u'admin']

Django-style queries

If you are already familiar with the Django ORM, you can construct select queries using the familiar “double-underscore” syntax.

# get the active users
active_users = User.filter(active=True)

# how many active users are there?
active_users.count()

# query users who are either staffers or superusers
editors = User.filter(Q(is_staff=True) | Q(is_superuser=True))

# get tweets by a specific user
Tweet.filter(user__username='charlie')

# get tweets by editors
Tweet.filter(Q(user__is_staff=True) | Q(user__is_superuser=True))

Where clause

All queries except InsertQuery support the where() method. If you are familiar with Django’s ORM, it is analagous to the filter() method.

>>> User.select().where(is_staff=True).sql()
('SELECT * FROM user WHERE is_staff = ?', [1])

Note

User.select() is equivalent to SelectQuery(User).

The where() method acts on the Model that is the current “context”. This is either:

  • the model the query class was initialized with
  • the model most recently JOINed on

Here is an example using JOINs:

>>> User.select().where(is_staff=True).join(Blog).where(status=LIVE)

This query grabs all staff users who have a blog that is “LIVE”. This does the opposite, grabs all the blogs that are live whose author is a staffer:

>>> Blog.select().where(status=LIVE).join(User).where(is_staff=True)

Note

to join() from one model to another there must be a ForeignKeyField linking the two.

Another way to write the above query would be:

>>> Blog.select().where(status=LIVE, user__in=User.select().where(is_staff=True))

The above bears a little bit of explanation. First off the SQL generated will not perform any explicit JOINs - it will rather use a subquery in the WHERE clause:

# using subqueries
SELECT * FROM blog
WHERE (
    status = ? AND
    user_id IN (
        SELECT t1.id FROM user AS t1 WHERE t1.is_staff = ?
    )
)

# using joins
SELECT t1.* FROM blog AS t1
INNER JOIN user AS t2
    ON t1.user_id = t2.id
WHERE
    t1.status = ? AND
    t2.is_staff = ?

The other bit that’s unique about the query is that it specifies “user__in”. Users familiar with Django will recognize this syntax - lookups other than “=” are signified by a double-underscore followed by the lookup type. The following lookup types are available in peewee:

__eq:
x = y, the default
__lt:
x < y
__lte:
x <= y
__gt:
x > y
__gte:
x >= y
__ne:
x != y
__is:
x IS y, used for testing against NULL values
__contains:
case-sensitive check for substring
__icontains:
case-insensitive check for substring
__in:
x IN y, where y is either a list of values or a SelectQuery

Performing advanced queries

As you may have noticed, all the examples up to now have shown queries that combine multiple clauses with “AND”. Taking another page from Django’s ORM, peewee allows the creation of arbitrarily complex queries using a special notation called Q objects.

>>> sq = User.select().where(Q(is_staff=True) | Q(is_superuser=True))
>>> print sq.sql()[0]
SELECT * FROM user WHERE (is_staff = ? OR is_superuser = ?)

Q objects can be combined using the bitwise “or” and “and” operators. In order to negate a Q object, use the bitwise “invert” operator:

>>> staff_users = User.select().where(is_staff=True)
>>> Blog.select().where(~Q(user__in=staff_users))

This query generates the following SQL:

SELECT * FROM blog
WHERE
    NOT user_id IN (
        SELECT t1.id FROM user AS t1 WHERE t1.is_staff = ?
    )

Rather complex lookups are possible:

>>> sq = User.select().where(
...     (Q(is_staff=True) | Q(is_superuser=True)) &
...     (Q(join_date__gte=datetime(2009, 1, 1)) | Q(join_date__lt=datetime(2005, 1 1)))
... )
>>> print sq.sql()[0] # cleaned up
SELECT * FROM user
WHERE (
    (is_staff = ? OR is_superuser = ?) AND
    (join_date >= ? OR join_date < ?)
)

This query selects all staff or super users who joined after 2009 or before 2005.

Note

if you need more power, check out RawQuery below.

Query evaluation

In order to execute a query, it is always necessary to call the execute() method.

To get a better idea of how querying works let’s look at some example queries and their return values:

>>> dq = User.delete().where(active=False) # <-- returns a DeleteQuery
>>> dq
<peewee.DeleteQuery object at 0x7fc866ada4d0>
>>> dq.execute() # <-- executes the query and returns number of rows deleted
3

>>> uq = User.update(active=True).where(id__gt=3) # <-- returns an UpdateQuery
>>> uq
<peewee.UpdateQuery object at 0x7fc865beff50>
>>> uq.execute() # <-- executes the query and returns number of rows updated
2

>>> iq = User.insert(username='new user') # <-- returns an InsertQuery
>>> iq
<peewee.InsertQuery object at 0x7fc865beff10>
>>> iq.execute() # <-- executes query and returns the new row's PK
3

>>> sq = User.select().where(active=True) # <-- returns a SelectQuery
>>> sq
<peewee.SelectQuery object at 0x7fc865b7a510>
>>> qr = sq.execute() # <-- executes query and returns a QueryResultWrapper
>>> qr
<peewee.QueryResultWrapper object at 0x7fc865b7a6d0>
>>> [u.id for u in qr]
[1, 2, 3, 4, 7, 8]
>>> [u.id for u in qr] # <-- re-iterating over qr does not re-execute query
[1, 2, 3, 4, 7, 8]

>>> [u.id for u in sq] # <-- as a shortcut, you can iterate directly over
>>>                    #     a SelectQuery (which uses a QueryResultWrapper
>>>                    #     behind-the-scenes)
[1, 2, 3, 4, 7, 8]

Note

iterating over a SelectQuery will cause it to be evaluated, but iterating over it multiple times will not result in the query being executed again.

QueryResultWrapper

As I hope the previous bit showed, Delete, Insert and Update queries are all pretty straightforward. Select queries are a little bit tricky in that they return a special object called a QueryResultWrapper. The sole purpose of this class is to allow the results of a query to be iterated over efficiently. In general it should not need to be dealt with explicitly.

The preferred method of iterating over a result set is to iterate directly over the SelectQuery, allowing it to manage the QueryResultWrapper internally.

SelectQuery

SelectQuery is by far the most complex of the 4 query classes available in peewee. It supports JOINing on other tables, aggregation via GROUP BY and HAVING clauses, ordering via ORDER BY, and can be sliced to return only a subset of results. All methods are chain-able.

__init__(self, model, query=None)

if no query is provided, it will default to ‘*’. this parameter can be either a dictionary or a string:

>>> sq = SelectQuery(Blog, {Blog: ['id', 'title']})
>>> sq = SelectQuery(Blog, {
...     Blog: ['*'],
...     Entry: [peewee.Count('id')]
... }).group_by('id').join(Entry)
>>> print sq.sql()[0] # formatted
SELECT t1.*, COUNT(t2.id) AS count
FROM blog AS t1
INNER JOIN entry AS t2
    ON t1.id = t2.blog_id
GROUP BY t1.id

>>> sq = SelectQuery(Blog, 'id, title')
>>> print sq.sql()[0]
SELECT id, title FROM blog
where(self, *args, **kwargs)

generate a WHERE clause for the current “query context”. *args is either a list of Q or Node objects, and **kwargs is a mapping of column + lookup to value:

>>> sq = SelectQuery(Blog).where(title='some title', author=some_user)
>>> sq = SelectQuery(Blog).where(Q(title='some title') | Q(title='other title'))
join(self, model, join_type=None, on=None)

generate a JOIN clause from the current “query context” to the model passed in, and establishes model as the new “query context”.

Parameters:
  • model – the model to join on. there must be a ForeignKeyField between the current “query context” and the model passed in.
  • join_type – allows the type of JOIN used to be specified explicitly
  • on – if multiple foreign keys exist between two models, this parameter is a string containing the name of the ForeignKeyField to join on.
>>> sq = SelectQuery(Blog).join(Entry).where(title='Some Entry')
>>> sq = SelectQuery(User).join(Relationship, on='to_user_id').where(from_user=self)
switch(self, model)

switches the “query context” to the given model. raises an exception if the model has not been selected or joined on previously.

>>> sq = SelectQuery(Blog).join(Entry).switch(Blog).where(title='Some Blog')
count(self)

returns an integer representing the number of rows in the current query

>>> sq = SelectQuery(Blog)
>>> sq.count()
45 # <-- number of blogs
>>> sq.where(status=DELETED)
>>> sq.count()
3 # <-- number of blogs that are marked as deleted
group_by(self, clause)

clause can be either a single field name or a list of field names, in which case it takes its context from the current query_context. it can also be a model class, in which case all that models fields will be included in the GROUP BY clause

>>> # get a list of blogs with the count of entries each has
>>> sq = Blog.select({
...     Blog: ['*'],
...     Entry: [Count('id')]
... }).group_by('id').join(Entry)

>>> # slightly more complex, get a list of blogs ordered by most recent pub_date
>>> sq = Blog.select({
...     Blog: ['*'],
...     Entry: [Max('pub_date', 'max_pub_date')],
... }).join(Entry)
>>> # now, group by the entry's blog id, followed by all the blog fields
>>> sq = sq.group_by('blog_id').group_by(Blog)
>>> # finally, order our results by max pub date
>>> sq = sq.order_by(peewee.desc('max_pub_date'))
having(self, clause)

adds the clause to the HAVING clause

>>> sq = Blog.select({
...     Blog: ['*'],
...     Entry: [Count('id', 'num_entries')]
... }).group_by('id').join(Entry).having('num_entries > 10')
order_by(self, clause)

adds the provided clause (a field name or alias) to the query’s ORDER BY clause. if a field name is passed in, it must be a field on the current “query context”, otherwise it is treated as an alias. peewee also provides two convenience methods to allow ordering ascending or descending, called asc() and desc().

example:

>>> sq = Blog.select().order_by('title')
>>> sq = Blog.select({
...     Blog: ['*'],
...     Entry: [Max('pub_date', 'max_pub')]
... }).join(Entry).order_by(desc('max_pub'))

check out how the query context applies to ordering:

>>> blog_title = Blog.select().order_by('title').join(Entry)
>>> print blog_title.sql()[0]
SELECT t1.* FROM blog AS t1
INNER JOIN entry AS t2
    ON t1.id = t2.blog_id
ORDER BY t1.title

>>> entry_title = Blog.select().join(Entry).order_by('title')
>>> print entry_title.sql()[0]
SELECT t1.* FROM blog AS t1
INNER JOIN entry AS t2
    ON t1.id = t2.blog_id
ORDER BY t2.title # <-- note that it's using the title on Entry this time
paginate(self, page_num, paginate_by=20)

applies a LIMIT and OFFSET to the query.

>>> Blog.select().order_by('username').paginate(3, 20) # <-- get blogs 41-60
distinct(self)

indicates that this query should only return distinct rows. results in a SELECT DISTINCT query.

execute(self)

executes the query and returns a QueryResultWrapper for iterating over the result set. the results are managed internally by the query and whenever a clause is added that would possibly alter the result set, the query is marked for re-execution.

__iter__(self)

executes the query:

>>> for user in User.select().where(active=True):
...     print user.username

UpdateQuery

UpdateQuery is fairly straightforward and is used for updating rows in the database.

__init__(self, model, **kwargs)

creates an UpdateQuery instance for the given model. “kwargs” is a dictionary of field: value pairs:

>>> uq = UpdateQuery(User, active=False).where(registration_expired=True)
>>> print uq.sql()
('UPDATE user SET active=? WHERE registration_expired = ?', [0, 1])
execute(self)

performs the query, returning the number of rows that were updated

DeleteQuery

DeleteQuery deletes rows of the given model. It will not traverse foreign keys or ensure that constraints are obeyed, so use it with care.

__init__(self, model)

creates a DeleteQuery instance for the given model:

>>> dq = DeleteQuery(User).where(active=False)
>>> print dq.sql()
('DELETE FROM user WHERE active = ?', [0])
execute(self)

performs the query, returning the number of rows that were deleted

InsertQuery

InsertQuery creates a new row for the given model.

__init__(self, model, **kwargs)

creates an InsertQuery instance for the given model where kwargs is a dictionary of field name to value:

>>> iq = InsertQuery(User, username='admin', password='test', active=True)
>>> print iq.sql()
('INSERT INTO user (username, password, active) VALUES (?, ?, ?)', ['admin', 'test', 1])
execute(self)

performs the query, returning the primary key of the row that was added

RawQuery

RawQuery allows execution of an arbitrary SELECT query and returns instances of the model via a QueryResultsWrapper.

__init__(self, model, query, *params)

creates a RawQuery instance for the given model which, when executed, will run the given query with the given parameters and return model instances:

>>> rq = RawQuery(User, 'SELECT * FROM users WHERE username = ?', 'admin')
>>> for obj in rq.execute():
...     print obj
<User: admin>
execute(self)

executes the query and returns a QueryResultWrapper for iterating over the result set. the results are instances of the given model.