I'm designing an API with SQLAlchemy (querying MySQL) and I would like to force all my queries to have page_size (LIMIT) and page_number (OFFSET) parameters.
Is there a clean way of doing this with SQLAlchemy? Perhaps building a factory of some sort to create a custom Query object? Or maybe there is a good way to do this with a mixin class?
I tried the obvious thing and it didn't work because .limit() and .offset() must be called after all filter conditions have been applied:
def q(page=0, page_size=None):
q = session.query(...)
if page_size: q = q.limit(page_size)
if page: q = q.offset(page*page_size)
return q
When I try using this, I get the exception:
sqlalchemy.exc.InvalidRequestError: Query.filter() being called on a Query which already has LIMIT or OFFSET applied. To modify the row-limited results of a Query, call from_self() first. Otherwise, call filter() before limit() or offset() are applied.
all() method. The Query object, when asked to return full entities, will deduplicate entries based on primary key, meaning if the same primary key value would appear in the results more than once, only one object of that primary key would be present.
As the documentation says, all() returns the result of the query as a list.
Execution Slowness this would indicate that the database is taking a long time to start returning results, and it means your query should be optimized, either by adding indexes or restructuring the query and/or underlying schema.
SQLAlchemy supports the widest variety of database and architectural designs as is reasonably possible. Unit Of Work. The Unit Of Work system, a central part of SQLAlchemy's Object Relational Mapper (ORM), organizes pending insert/update/delete operations into queues and flushes them all in one batch.
Try adding a first, required argument, which must be a group of query filters. Thus,
# q({'id': 5}, 2, 50)
def q(filters, page=0, page_size=None):
query = session.query(...).filter_by(**filters)
if page_size:
query = query.limit(page_size)
if page:
query = query.offset(page*page_size)
return query
or,
# q(Model.id == 5, 2, 50)
def q(filter, page=0, page_size=None):
query = session.query(...).filter(filter)
if page_size:
query = query.limit(page_size)
if page:
query = query.offset(page*page_size)
return query
Not an option at the time of this question, since version 1.0.0 you can take advantage of Query events to ensure limit
and offset
methods are always called just before your query
object is compiled, after any manipulation is performed by the users of your q
function:
from sqlalchemy.event import listen
def q(page=0, page_size=None):
query = session.query()
listen(query, 'before_compile', apply_limit(page, page_size), retval=True)
return query
def apply_limit(page, page_size):
def wrapped(query):
if page_size:
query = query.limit(page_size)
if page:
query = query.offset(page * page_size)
return query
return wrapped
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With