SQLAlchemy нетерпеливое соединение не работает - PullRequest
0 голосов
/ 30 января 2020
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)

Я разделяю таблицу на posts и post_translation для многоязычной базы данных.

Также разделяю таблицу на tags и post_tags.

И ссылку tag_id (FK) - tags таблица.

В этом случае мне нужно извлечь все сообщения, содержащие указанное c имя тега.

    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)

Над кодом отлично работает. Но когда я регистрировал запрос,

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}

Как вы знаете, это вызвало проблемы N + 1 при доступе к тегам. (q.post.tags)

Я не знаю, почему запрос произошел еще раз, несмотря на то, что уже объединены (и загружаются) таблица тегов.

Есть ли здесь какое-нибудь решение?

Спасибо.

1 Ответ

0 голосов
/ 30 января 2020

решено

решено с помощью подзапроса

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()
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...