Question

Wondering if/how I can return a list of tags shared between two blog posts with SQLAlchemy or if I'll need to pull each one's tags separately and make the comparison on the Python side. Take this example (borrowed from SQLAlchemy how to filter by children in many to many):

import sqlalchemy as alc
Session = alc.sessionmaker(bind=engine)
session = Session()
Base = alc.declarative_base(bind=engine)

class Tag(Base):
    __tablename__ = 'tags'
    id = alc.Column(alc.Integer, primary_key=True)
    name = alc.Column(alc.String)
    def __repr__(self): 
        return '<Tag "%s">' % self.name

class Post(Base):
    __tablename__ = 'posts'
    id = alc.Column(alc.Integer, primary_key=True)
    text = alc.Column(alc.String)
    def __repr__(self): 
        return '<Post id %d>' % self.id


class PostTag(Base):
    __tablename__ = 'post_tags'
    post_id = alc.Column(alc.Integer, alc.ForeignKey('posts.id'), primary_key=True)
    tag_id = alc.Column(alc.Integer, alc.ForeignKey('tags.id'), primary_key=True)
    post = alc.relationship("Post", backref="ptags")
    tag = alc.relationship("Tag", backref="tposts")
    tagging_user_name = alc.Column(alc.String)

I'm trying to figure out how to construct a query that, given two posts, will return a list of tags shared by the two. For example, if we have post_1 with tags "recipe", "guestpost", "video", and "vegetarian", and post_2 with tags "recipe", "audio", "dessert", and "vegetarian", I want a query that returns [<Tag "recipe">, <Tag "vegetarian">].

I've figured out:

session.query(Post).join(PostTag).filter(PostTag.tag.has(name='joke')).all()

will return every post tagged "joke" in a list, but I'm at a loss how to find a list of tags shared between two posts beyond the hunch that I might want to start from the tags table. Any help would be much appreciated.

Was it helpful?

Solution

This works for me:

from sqlalchemy.sql import exists, and_

pt1 = exists().where(and_(PostTag.tag_id == Tag.id, PostTag.post_id == id1))
pt2 = exists().where(and_(PostTag.tag_id == Tag.id, PostTag.post_id == id2))
tags = session.query(Tag.name).filter(pt1, pt2).all()

Should produce SQL similar to the following:

SELECT name FROM tags
WHERE EXISTS(SELECT 1 FROM post_tags WHERE tag_id=id AND post_id=<post id 1>)
AND EXISTS(SELECT 1 FROM post_tags WHERE tag_id=id AND post_id=<post id 2>)
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top