Skip to content

Conversation

@davidism
Copy link
Member

@davidism davidism commented Sep 24, 2022

This adds db.paginate, db.get_or_404, db.first_or_404, and db.one_or_404 methods. These operate on select statements instead of the legacy query object. fixes #1088

# new
db.get_or_404(User, id)
db.paginate(db.select(User).order_by(User.id))

# old
User.query.get_or_404(id)
User.query.order_by(User.id).paginate()

The Pagination object should no longer be created manually. The constructor signature changed to actually apply the pagination, but it is also a base class that will raise a NotImplementedError. db.paginate creates a SelectPagination object, and query.paginate creates a QueryPagination object. This refactor made it very easy to create the two subclasses without duplicating code, but I decided to keep the public API in the paginate methods. Moving away from providing a generic pagination API was previously discussed in #282.

When querying a model, Query.all would automatically return single objects instead of tuples and dedupe joins. There doesn't seem to be a way to get the same automatic checks with select. We need to call unique and scalars, which means db.paginate currently only works for select(Model), not for select(Model.column, Model.column).


I'm going to open a discussion on the SQLAlchemy repo, but I'm not really sure if we're missing something with the new execute(select()) pattern. It's more verbose than Model.query, and doesn't provide some of the old query behavior.

# new select
db.session.execute(db.select(User).join(User.books)).unique().scalars().all()

# old query
db.session.query(User).join(User.books).all()
# even shorter
User.query.join(User.books).all()

Model.select wouldn't really be useful, since db is already needed for db.session.execute, and Model.select isn't much shorter than db.select(User). We could add a db.execute method as a shortcut for db.session.execute, but I'm not sure that's worth it either.

# theoretical shortcuts saves 12 characters
db.execute(User.select.join(User.books)).unique().scalars().all()

# current
db.session.execute(db.select(User).join(User.books)).unique().scalars().all()

@davidism davidism added this to the 3.0.0 milestone Sep 24, 2022
@davidism davidism mentioned this pull request Sep 24, 2022
@davidism davidism merged commit 904aafc into main Sep 24, 2022
@davidism davidism deleted the select-query-methods branch September 24, 2022 17:20
@github-actions github-actions bot locked as resolved and limited conversation to collaborators Oct 9, 2022
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Development

Successfully merging this pull request may close these issues.

port query methods to select

2 participants