简体   繁体   中英

SQLAlchemy eager join relationship doesn't work

class Post(Base):
    __tablename__ = 'posts'

    id = Column(BigInteger, primary_key=True, autoincrement=True)

    names = relationship('PostTranslation', backref='posts')
    tags = relationship('PostTag', backref='posts')


class Language(Base):
    __tablename__ = 'languages'

    id = Column(BigInteger, primary_key=True, autoincrement=True)
    lang = Column(Unicode(255), nullable=False)


class Tag(Base):
    __tablename__ = 'tags'

    id = Column(BigInteger, primary_key=True, autoincrement=True)
    name = Column(Unicode(255), nullable=False)


class PostTranslation(Base):
    __tablename__ = 'post_translation'

    id = Column(BigInteger, primary_key=True, autoincrement=True)
    post_id = Column(BigInteger, ForeignKey('posts.id'))
    language_id = Column(BigInteger, ForeignKey('languages.id'))
    title = Column(Unicode(255), nullable=False)

    post = relationship('Post', uselist=False)
    language = relationship('Language', uselist=False)


class PostTag(Base):
    __tablename__ = 'post_tags'

    id = Column(BigInteger, primary_key=True, autoincrement=True)
    post_id = Column(BigInteger, ForeignKey('posts.id'))
    tag_id = Column(BigInteger, ForeignKey('tags.id'))
    language_id = Column(BigInteger, ForeignKey('languages.id'))

    post = relationship('Post', uselist=False)
    language = relationship('Language', uselist=False)
    tag = relationship('Tag', uselist=False)

I separate table to posts and post_translation for multi language database design.

Also separated table to tags and post_tags .

And link tag_id(FK) to tags table.

In this case, I have to extract all of posts that contains specific tag name.

    query = session.query(PostTag).options(
        joinedload(PostTag.post).joinedload(Post.names)
    ).join(Tag).filter(Tag.name == 'python').options(
        contains_eager(PostTag.tag)
    ).all()

    for q in query:
        print(q.post.tags)

Above code works perfectly. But when I logging query,

2020-01-30 20:25:45,854 INFO sqlalchemy.engine.base.Engine SELECT tags.id AS tags_id, tags.name AS tags_name, post_tags.id AS post_tags_id, post_tags.post_id AS post_tags_post_id, post_tags.tag_id AS post_tags_tag_id, post_tags.language_id AS post_tags_language_id, post_translation_1.id AS post_translation_1_id, post_translation_1.post_id AS post_translation_1_post_id, post_translation_1.language_id AS post_translation_1_language_id, post_translation_1.title AS post_translation_1_title, posts_1.id AS posts_1_id 
FROM post_tags INNER JOIN tags ON tags.id = post_tags.tag_id LEFT OUTER JOIN posts AS posts_1 ON posts_1.id = post_tags.post_id LEFT OUTER JOIN post_translation AS post_translation_1 ON posts_1.id = post_translation_1.post_id 
WHERE tags.name = %(name_1)s
2020-01-30 20:25:45,854 INFO sqlalchemy.engine.base.Engine {'name_1': 'python'}
2020-01-30 20:25:45,856 INFO sqlalchemy.engine.base.Engine SELECT post_tags.id AS post_tags_id, post_tags.post_id AS post_tags_post_id, post_tags.tag_id AS post_tags_tag_id, post_tags.language_id AS post_tags_language_id 
FROM post_tags 
WHERE %(param_1)s = post_tags.post_id
2020-01-30 20:25:45,856 INFO sqlalchemy.engine.base.Engine {'param_1': 1}
[<model.PostTag object at 0x10951a710>, <model.PostTag object at 0x109522b90>]
2020-01-30 20:25:45,857 INFO sqlalchemy.engine.base.Engine SELECT post_tags.id AS post_tags_id, post_tags.post_id AS post_tags_post_id, post_tags.tag_id AS post_tags_tag_id, post_tags.language_id AS post_tags_language_id 
FROM post_tags 
WHERE %(param_1)s = post_tags.post_id
2020-01-30 20:25:45,857 INFO sqlalchemy.engine.base.Engine {'param_1': 3}

As you know that, it occured N+1 problems when access to tags. ( q.post.tags )

I don't know why query occured once again despite already joined(and eager loading) tags table.

Is there any solution here?

Thanks.

SOLVED

Solved by using subquery

from sqlalchemy import func


sub1 = session.query(Tag.id).filter(Tag.name == 'python')
sub2 = session.query(PostTag.post_id).filter(PostTag.tag_id.in_(sub1))

q = session.query(
    func.group_concat(PostTranslation.title.distinct()).label('title'),
    PostTag.post_id.label('id'),
    func.group_concat(Tag.name).label('tags'),
).join(
    Tag, Tag.id == PostTag.tag_id
).join(
    PostTranslation,
    PostTranslation.post_id == PostTag.post_id,
).filter(
    PostTag.post_id.in_(sub2),
).group_by(
    PostTag.post_id,
    PostTranslation.title,
).all()

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM