Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What is the equivalent of Query.count in the SQLAlchemy 1.4 ORM?

Given a SQLAlchemy ORM model like this

class Foo(Base):
    __tablename__ = 'foo'

    id = sa.Column(sa.Integer, primary_key=True)
    name = sa.Column(sa.String())

In SQLAlchemy 1.4 / 2.0, the ORM's session.query idiom is being unified with the SQLAlchemy core select function*, so to fetch all Foos we would do

foos = session.execute(Foo).scalars().all()

instead of

foos = session.query(Foo).all()

In the current (sqlalchemy<=1.3) ORM we can obtain the number of Foos in the database with this query:

nfoos = session.query(Foo).count()

But how can we get the count in SQLALchemy 1.4?

session.execute(sa.select(Foo).count())

raises

AttributeError: 'Select' object has no attribute 'count'

session.execute(sa.select(Foo)).count()

raises

AttributeError: 'ChunkedIteratorResult' object has no attribute 'count'

session.execute(sa.select(sa.func.count(Foo)))

raises

sqlalchemy.exc.ArgumentError: SQL expression element expected, got <class '__main__.Foo'>.

This works,

session.execute(sa.select(sa.func.count(Foo.id))).scalars()

but specifying an attribute seems less OO / elegant than the Query.count version. Moreover, it precludes building a query but deferring the decision of whether to retrieve the count or the model instances.

What is the idiomatic way to get the count() of an ORM query in the new SQLAlchemy 1.4 world?


* the session.query() API will still work in 1.4 and later

like image 670
snakecharmerb Avatar asked Jan 16 '21 19:01

snakecharmerb


People also ask

What is subquery in SQLAlchemy?

The grouping is done with the group_by() query method, which takes the column to use for the grouping as an argument, same as the GROUP BY counterpart in SQL. The statement ends by calling subquery() , which tells SQLAlchemy that our intention for this query is to use it inside a bigger query instead of on its own.

How does the querying work with SQLAlchemy?

Python Flask and SQLAlchemy ORM All SELECT statements generated by SQLAlchemy ORM are constructed by Query object. It provides a generative interface, hence successive calls return a new Query object, a copy of the former with additional criteria and options associated with it.

What is all () in SQLAlchemy?

method sqlalchemy.orm.Query. all() Return the results represented by this Query as a list. This results in an execution of the underlying SQL statement. The Query object, when asked to return either a sequence or iterator that consists of full ORM-mapped entities, will deduplicate entries based on primary key.

What is SQLAlchemy ORM?

SQLAlchemy is a library that facilitates the communication between Python programs and databases. Most of the times, this library is used as an Object Relational Mapper (ORM) tool that translates Python classes to tables on relational databases and automatically converts function calls to SQL statements.


1 Answers

Based on Ilja Everilä's comments, it seems that there is no direct equivalent to Query.count in the new ORM-querying API released in SQLAlchemy 1.4 (in beta at the time of writing).

The functional equivalent is to call count(), selecting from a subquery*

from sqlalchemy import func, select

count = (
    session.execute(select(func.count()).select_from(select(Foo).subquery()))
    .scalar_one()
)

Generating this SQL

SELECT count(*) AS count_1 
 FROM (SELECT foo.id AS id, foo.name AS name 
         FROM foo) 
AS anon_1

Counting an attribute, such as the primary key column, generates a simple SELECT COUNT

count = session.execute(select(func.count(Foo.id))).scalar_one()
SELECT count(foo.id) AS count_1 
FROM foo

Query.count is still supported in 1.4, so it can still be used, as can all the ORM features available in 1.3.


* Query.count also selects from a subquery rather than executing SELECT COUNT directly.

like image 55
snakecharmerb Avatar answered Oct 13 '22 01:10

snakecharmerb