sqlalchemy : 하나의 쿼리로 여러 테이블을 조인하는 방법은 무엇입니까?


93

다음 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()

감사,


다음은 두 테이블을 조인하는 데 작동하는 것으로 나타났습니다. result = session.query(User, Document).select_from(join(User, Document)).filter(User.email=='user@email.com').all()그러나 DocumentPermissions를 포함하기 위해 세 테이블에 대해 유사한 작업을 수행하는 방법을 아직 관리하지 않았습니다. 어떤 아이디어?
barankin

비슷한 작업을 수행하면 SyntaxError : keyword ca n't be an expression
Ishan Tomar

답변:


93

이 시도

q = Session.query(
         User, Document, DocumentPermissions,
    ).filter(
         User.email == Document.author,
    ).filter(
         Document.name == DocumentPermissions.document,
    ).filter(
        User.email == 'someemail',
    ).all()

6
어떤 종류의 join기능을합니까? 내부, 외부, 십자가 또는 무엇?
Nawaz

7
이것은 실제로 조인을 전혀 수행하지 않으며 튜플의 행 개체를 반환합니다. 이 경우 반환됩니다 [(<user>, <document>, <documentpermissions>),...]/
Fake Name

32
"조인을 전혀하지 않습니다"-약간 오해의 소지가 있습니다. select x from a, b ,c크로스 조인 과 같은 SQL이 있습니다 . 그런 다음 필터는 내부 조인으로 만듭니다.
Aidan Kane

7
을 생략하여 쿼리를 인쇄 할 수 있습니다 .all(). 그래서 print Session.query....그것이 무엇을하고 있는지 정확히보기 위해.
boatcoder

4
SQLAlchemy를 처음 사용합니다. 나는 눈치 .filter()쉼표로 구분하면 여러 조건을받을 수 있습니다. .filter()괄호 안에 쉼표로 구분 된 것을 사용 하거나 .filter()위의 답변과 같이 여러 개 를 사용하는 것이 바람직 합니까?
Intrastellar Explorer

51

좋은 스타일은 권한에 대한 몇 가지 관계와 기본 키를 설정하는 것입니다 (사실 일반적으로 모든 것에 정수 기본 키 를 설정하는 것이 좋습니다 ).

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)

query마지막 줄에는 무엇이 설정되어 있으며 그 안에 결합 된 레코드에 어떻게 액세스합니까?
Petrus Theron 2014 년

@pate 나는 당신이 '쿼리 세트가 무엇인지'의미를 잘 모르겠지만 관계에 따라 조인하고 3 튜플을 산출합니다. query () 호출에 대한 인수는 기본적으로 sqlalchemy의 선택 목록입니다.
letitbee

Document쿼리 결과 집합 의 (두 번째 튜플 값)에 어떻게 액세스 합니까?
Petrus Theron

1
@PetrusTheron 내가 말했듯이 쿼리는 3- 튜플을 생성합니다. 요소를 색인화하거나 압축을 풀 수 있습니다.for (user, doc, perm) in query: print "Document: %s" % doc
letitbee

1
워, 과거로부터의 폭발. 'permissions'는 DocumentPermission 개체 집합을 제공하는 Document 개체의 속성입니다. 따라서 backref.
letitbee

38

@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
이 작업을 수행. 참조-조인에 지정된 항목이 많지 않습니다. 이는 테이블 / db-model이 이러한 테이블간에 적절한 외래 키로 이미 설정되어있는 경우 SQLAlchemy가 적절한 열에 대한 조인을 처리하기 때문입니다.
Brad

8

Abdul의 대답을 확장하면 KeyedTuple열을 결합하여 개별 행 모음 대신 a 를 얻을 수 있습니다.

q = Session.query(*User.__table__.columns + Document.__table__.columns).\
        select_from(User).\
        join(Document, User.email == Document.author).\
        filter(User.email == 'someemail').all()

1
작동합니다. 그러나 개체를 직렬화 할 때 열 이름이 없습니다.
Lucas

1

이 함수는 튜플 목록으로 필요한 테이블을 생성합니다.

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)
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.