I needed to use the as_scalar()
method on the subquery, which turns the SELECT expression into a regular column:
a_q = db.session.query(func.count(tag_relationships['Article'].c.post_id)).select_from(tag_relationships['Article']).filter(tag_relationships['Article'].c.tag_id == Tag.id).subquery()
r_q = db.session.query(func.count(tag_relationships['Review'].c.post_id)).select_from(tag_relationships['Review']).filter(tag_relationships['Review'].c.tag_id == Tag.id).subquery()
db.session.query(Tag, a_q.as_scalar(), r_q.as_scalar()).group_by(Tag.id).order_by(Tag.handle).limit(10).all()
I use a mixin to add a many-to-many relationship between Tag
objects (all stored in a single table) and any number of classes representing distinct types of posts.
I want to be able to query the count of each tag, grouped by the type of post. The raw SQL query in desired query.sql
provides the desired output. However, no matter how I massage SQLAlchemy ORM, I can't seem to generate that SQL.
I have tried subqueries like so:
a_q = db.session.query(func.count(tag_relationships['Article'].c.post_id)).select_from(tag_relationships['Article']).filter(tag_relationships['Article'].c.tag_id == Tag.id).subquery()
r_q = db.session.query(func.count(tag_relationships['Review'].c.post_id)).select_from(tag_relationships['Review']).filter(tag_relationships['Review'].c.tag_id == Tag.id).subquery()
db.session.query(Tag, a_q, r_q).group_by(Tag.id).order_by(Tag.handle).limit(10).all()
which outputs:
SELECT tags.id AS tags_id, tags.handle AS tags_handle, tags.label AS tags_label, anon_1.count_1 AS anon_1_count_1, anon_2.count_2 AS anon_2_count_2
FROM tags, (SELECT count(tag_associations.post_id) AS count_1
FROM tag_associations, tags
WHERE tag_associations.tag_id = tags.id) AS anon_1, (SELECT count(shelf_associations.post_id) AS count_2
FROM shelf_associations, tags
WHERE shelf_associations.tag_id = tags.id) AS anon_2 GROUP BY tags.id ORDER BY tags.handle
and does not result in the correct frequencies (I get tuples, 1 for each tag, with the same number repeated across every tuple).