sqlalchemy: как объединить несколько таблиц одним запросом? - PullRequest
73 голосов
/ 18 мая 2011

У меня есть следующие сопоставленные классы SQLAlchemy:

class User(Base):
    __tablename__ = 'users'
    email = Column(String, primary_key=True)
    name = Column(String)

class Document(Base):
    __tablename__ = "documents"
    name = Column(String, primary_key=True)
    author = Column(String, ForeignKey("users.email"))

class DocumentsPermissions(Base):
    __tablename__ = "documents_permissions"
    readAllowed = Column(Boolean)
    writeAllowed = Column(Boolean)

    document = Column(String, ForeignKey("documents.name"))

Мне нужно получить такую ​​таблицу для user.email = "user@email.com":

email | name | document_name | document_readAllowed | document_writeAllowed

Как это можно сделать с помощью одного запросазапрос на SQLAlchemy?Код ниже не работает для меня:

result = session.query(User, Document, DocumentPermission).filter_by(email = "user@email.com").all()

Спасибо,

Ответы [ 5 ]

71 голосов
/ 18 мая 2011

Попробуйте это

q = (Session.query(User,Document,DocumentPermissions)
    .filter(User.email == Document.author)
    .filter(Document.name == DocumentPermissions.document)
    .filter(User.email == 'someemail')
    .all())
42 голосов
/ 03 июня 2011

Хорошим стилем будет установка некоторых отношений и первичного ключа для разрешений (на самом деле, обычно это хороший стиль для установки целых первичных ключей для всего, но не для всех):

class User(Base):
    __tablename__ = 'users'
    email = Column(String, primary_key=True)
    name = Column(String)

class Document(Base):
    __tablename__ = "documents"
    name = Column(String, primary_key=True)
    author_email = Column(String, ForeignKey("users.email"))
    author = relation(User, backref='documents')

class DocumentsPermissions(Base):
    __tablename__ = "documents_permissions"
    id = Column(Integer, primary_key=True)
    readAllowed = Column(Boolean)
    writeAllowed = Column(Boolean)
    document_name = Column(String, ForeignKey("documents.name"))
    document = relation(Document, backref = 'permissions')

Затем выполните простой запрос с объединениями:

query = session.query(User, Document, DocumentsPermissions).join(Document).join(DocumentsPermissions)
20 голосов
/ 05 июня 2018

Как сказал @letitbee, рекомендуется назначать первичные ключи таблицам и правильно определять отношения, чтобы обеспечить надлежащие запросы ORM. Как говорится ...

Если вы заинтересованы в написании запроса в виде:

SELECT
    user.email,
    user.name,
    document.name,
    documents_permissions.readAllowed,
    documents_permissions.writeAllowed
FROM
    user, document, documents_permissions
WHERE
    user.email = "user@email.com";

Тогда вы должны пойти на что-то вроде:

session.query(
    User, 
    Document, 
    DocumentsPermissions
).filter(
    User.email == Document.author
).filter(
    Document.name == DocumentsPermissions.document
).filter(
    User.email == "user@email.com"
).all()

Если вместо этого вы хотите сделать что-то вроде:

SELECT 'all the columns'
FROM user
JOIN document ON document.author_id = user.id AND document.author == User.email
JOIN document_permissions ON document_permissions.document_id = document.id AND document_permissions.document = document.name

Тогда вы должны сделать что-то вроде:

session.query(
    User
).join(
    Document
).join(
    DocumentsPermissions
).filter(
    User.email == "user@email.com"
).all()

Одна записка об этом ...

query.join(Address, User.id==Address.user_id) # explicit condition
query.join(User.addresses)                    # specify relationship from left to right
query.join(Address, User.addresses)           # same, with explicit target
query.join('addresses')                       # same, using a string

Для получения дополнительной информации посетите документы .

4 голосов
/ 08 сентября 2017

Расширяя ответ Абдула, вы можете получить KeyedTuple вместо дискретного набора строк, соединив столбцы:

q = Session.query(*User.__table__.columns + Document.__table__.columns).\
        select_from(User).\
        join(Document, User.email == Document.author).\
        filter(User.email == 'someemail').all()
1 голос
/ 20 ноября 2017

Эта функция создаст необходимую таблицу в виде списка кортежей.

def get_documents_by_user_email(email):
    query = session.query(User.email, User.name, Document.name,
         DocumentsPermissions.readAllowed, DocumentsPermissions.writeAllowed,)
    join_query = query.join(Document).join(DocumentsPermissions)
    return join_query.filter(User.email == email).all()

user_docs = get_documents_by_user_email(email)
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...