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 = "[email protected]":

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 = "[email protected]").all()

Terima kasih,

barankin
sumber
Saya telah menemukan bahwa berikut ini berfungsi untuk menggabungkan dua tabel: result = session.query(User, Document).select_from(join(User, Document)).filter(User.email=='[email protected]').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()
Abdul Kader
sumber
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)
biarkan saja
sumber
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 = "[email protected]";

Maka Anda harus mencari sesuatu seperti:

session.query(
    User, 
    Document, 
    DocumentsPermissions
).filter(
    User.email == Document.author
).filter(
    Document.name == DocumentsPermissions.document
).filter(
    User.email == "[email protected]"
).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 == "[email protected]"
).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 .

Ullauri
sumber
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()
mih
sumber
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)
Valery Ramusik
sumber