sqlalchemy: làm thế nào để nối nhiều bảng bằng một truy vấn?


93

Tôi có các lớp được ánh xạ SQLAlchemy sau:

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"))

Tôi cần có một bảng như thế này cho user.email = "user@email.com":

email | name | document_name | document_readAllowed | document_writeAllowed

Làm thế nào nó có thể được thực hiện bằng cách sử dụng một yêu cầu truy vấn cho SQLAlchemy? Mã dưới đây không hoạt động đối với tôi:

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

Cảm ơn,


Tôi nhận thấy rằng các thao tác sau đây hoạt động để nối hai bảng: result = session.query(User, Document).select_from(join(User, Document)).filter(User.email=='user@email.com').all()Nhưng tôi vẫn chưa quản lý được cách làm cho công việc tương tự cho ba bảng (bao gồm DocumentPermissions). Bất kỳ ý tưởng?
barankin

Khi tôi thực hiện tác vụ tương tự, tôi nhận được SyntaxError: từ khóa không thể là một biểu thức
Ishan Tomar

Câu trả lời:


93

Thử cái này

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

6
Nó làm kiểu joingì? bên trong, bên ngoài, chữ thập hay cái gì?
Nawaz

7
Điều này thực sự không thực hiện một phép nối nào cả, nó trả về các đối tượng hàng trong một bộ tuple. Trong trường hợp này, nó sẽ quay trở lại [(<user>, <document>, <documentpermissions>),...]/
Tên giả

32
"hoàn toàn không tham gia" - điều đó hơi gây hiểu lầm. Nó sẽ có sql giống như select x from a, b ,cmột phép nối chéo. Các bộ lọc sau đó làm cho nó trở thành một liên kết bên trong.
Aidan Kane

7
Bạn có thể in truy vấn bằng cách bỏ dấu .all(). Vì vậy, print Session.query....để xem chính xác những gì nó đang làm.
boatcoder

4
Tôi mới sử dụng SQLAlchemy. Tôi nhận thấy .filter()có thể nhận được nhiều tiêu chí nếu được phân tách bằng dấu phẩy. Bạn nên sử dụng một từ .filter()có dấu phẩy phân cách bên trong ngoặc đơn hay sử dụng nhiều .filter()như câu trả lời trên?
Intrastellar Explorer

51

Một phong cách tốt sẽ là thiết lập một số quan hệ và khóa chính cho quyền (thực tế, thông thường, cách tốt là thiết lập khóa chính số nguyên cho mọi thứ, nhưng bất cứ điều gì):

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')

Sau đó, thực hiện một truy vấn đơn giản với các phép nối:

query = session.query(User, Document, DocumentsPermissions).join(Document).join(DocumentsPermissions)

Điều gì được queryđặt ở dòng cuối cùng và làm cách nào để bạn truy cập các bản ghi đã kết hợp trong đó?
Petrus Theron

@pate Tôi không chắc bạn muốn nói gì về 'truy vấn được đặt thành', nhưng nó sẽ tham gia theo quan hệ và mang lại 3 bộ giá trị. Các đối số cho lệnh gọi query () về cơ bản là danh sách chọn trong sqlalchemy.
letitbee

Làm cách nào để truy cập Document(giá trị tuple thứ 2) trong tập kết quả truy vấn?
Petrus Theron

1
@PetrusTheron Như tôi đã nói, truy vấn sẽ mang lại 3 bộ giá trị. Bạn có thể yếu tố chỉ số, hoặc chỉ giải nén:for (user, doc, perm) in query: print "Document: %s" % doc
letitbee

1
Whoa, vụ nổ từ quá khứ. 'quyền' là một thuộc tính của đối tượng Document, cung cấp cho bạn tập hợp các đối tượng DocumentPermission. Do đó backref.
letitbee

38

Như @letitbee đã nói, cách tốt nhất là gán khóa chính cho các bảng và xác định đúng các mối quan hệ để cho phép truy vấn ORM thích hợp. Điều đó đang được nói ...

Nếu bạn muốn viết một truy vấn dọc theo các dòng:

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

Sau đó, bạn nên tìm một cái gì đó như:

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

Nếu thay vào đó, bạn muốn làm điều gì đó như:

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

Sau đó, bạn nên làm điều gì đó dọc theo các dòng:

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

Một lưu ý về điều đó ...

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

Để biết thêm thông tin, hãy truy cập tài liệu .


4
LÀM CÁI NÀY. Xem - không có nhiều thứ được chỉ định trong các phép nối. Đó là bởi vì nếu các bảng / db-model đã được thiết lập với các khóa ngoại thích hợp giữa các bảng này - thì SQLAlchemy sẽ đảm nhận việc kết hợp BẬT các cột thích hợp cho bạn.
Brad

8

Mở rộng câu trả lời của Abdul, bạn có thể nhận được KeyedTuplethay vì một tập hợp các hàng rời rạc bằng cách nối các cột:

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

1
Những công việc này. Tuy nhiên, tên cột của chúng bị thiếu khi tuần tự hóa đối tượng.
Lucas

1

Hàm này sẽ tạo ra bảng yêu cầu dưới dạng danh sách các bộ giá trị.

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)
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.