sqlalchemy: bagaimana cara menggabungkan beberapa tabel dengan satu kueri?


98

Saya memiliki kelas yang dipetakan SQLAlchemy berikut:

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

Saya perlu mendapatkan meja seperti ini untuk user.email = "user@email.com":

email | name | document_name | document_readAllowed | document_writeAllowed

Bagaimana cara membuatnya menggunakan satu permintaan kueri untuk SQLAlchemy? Kode di bawah tidak berfungsi untuk saya:

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

Terima kasih,


Saya telah menemukan bahwa berikut ini berfungsi untuk menggabungkan dua tabel: result = session.query(User, Document).select_from(join(User, Document)).filter(User.email=='user@email.com').all()Tapi saya belum berhasil bagaimana membuat pekerjaan serupa untuk tiga tabel (untuk menyertakan DocumentPermissions). Ada ide?
barankin

Ketika saya melakukan tugas serupa, saya mendapatkan SyntaxError: kata kunci tidak bisa menjadi ekspresi
Ishan Tomar

Jawaban:


98

Coba ini

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

7
Jenis joinapa yang dilakukannya? batin, luar, salib atau apa?
Nawaz

7
Ini sebenarnya tidak melakukan gabungan sama sekali, ini mengembalikan objek baris dalam tupel. Dalam hal ini, itu akan mengembalikan [(<user>, <document>, <documentpermissions>),...]/
Nama Palsu

37
"tidak bergabung sama sekali" - itu sedikit menyesatkan. Ini akan memiliki sql seperti select x from a, b ,cyang merupakan gabungan silang. Filter kemudian membuatnya menjadi gabungan dalam.
Aidan Kane

7
Anda dapat mencetak kueri dengan meninggalkan .all(). Jadi print Session.query....untuk melihat dengan tepat apa yang dilakukannya.
boatcoder

4
Saya baru mengenal SQLAlchemy. Saya perhatikan .filter()dapat menerima beberapa kriteria jika dipisahkan dengan koma. Apakah lebih baik menggunakan satu .filter()dengan pemisah koma di dalam tanda kurung, atau menggunakan banyak .filter()seperti jawaban di atas?
Penjelajah Intrastellar

53

Gaya yang baik adalah mengatur beberapa relasi dan kunci utama untuk izin (sebenarnya, biasanya itu adalah gaya yang baik untuk menyiapkan kunci primer integer untuk semuanya, tetapi apa pun):

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

Kemudian lakukan kueri sederhana dengan gabungan:

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

Apa yang querydiatur di baris terakhir dan bagaimana Anda mengakses rekaman yang digabungkan di dalamnya?
Petrus Theron

@pate Saya tidak yakin apa yang Anda maksud dengan 'what is query set to', tapi itu akan bergabung sesuai relasi, dan menghasilkan 3-tuple. Argumen ke panggilan query () pada dasarnya adalah daftar pilihan di sqlalchemy.
letitbee

Bagaimana cara saya mengakses Document(nilai tuple ke-2) di set hasil kueri?
Petrus Theron

1
@PetrusTheron Seperti yang saya katakan, kueri akan menghasilkan 3-tupel. Anda dapat mengindeks elemen, atau hanya membongkar:for (user, doc, perm) in query: print "Document: %s" % doc
letitbee

1
Wah, ledakan dari masa lalu. 'izin' adalah atribut objek Dokumen, yang memberi Anda sekumpulan objek DocumentPermission. Oleh karena itu backref.
letitbee

41

Seperti yang dikatakan @letitbee, praktik terbaiknya adalah menetapkan kunci utama ke tabel dan menentukan hubungan dengan benar untuk memungkinkan kueri ORM yang tepat. Yang telah dibilang...

Jika Anda tertarik untuk menulis kueri di sepanjang baris:

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

Maka Anda harus mencari sesuatu seperti:

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

Jika sebaliknya, Anda ingin melakukan sesuatu seperti:

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

Maka Anda harus melakukan sesuatu di sepanjang baris:

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

Satu catatan tentang itu ...

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

Untuk informasi lebih lanjut, kunjungi dokumen .


4
MELAKUKAN HAL INI. Lihat - tidak banyak hal yang ditentukan dalam gabungan. Itu karena jika tabel / db-model telah disiapkan dengan kunci asing yang tepat di antara tabel-tabel ini - SQLAlchemy akan mengurus penggabungan pada kolom yang tepat untuk Anda.
Brad

8

Memperluas jawaban Abdul, Anda bisa mendapatkan KeyedTuplekumpulan baris terpisah dengan menggabungkan kolom:

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

1
Ini bekerja. Namun, nama kolom mereka tidak ada saat membuat serial objek.
Lucas

2

Fungsi ini akan menghasilkan tabel yang dibutuhkan sebagai daftar tupel.

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)
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.