sqlalchemy: birkaç tabloyu bir sorgu ile nasıl birleştirebilirim?


98

Aşağıdaki SQLAlchemy eşlenmiş sınıflarım var:

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

Bunun gibi bir masa almam gerekiyor user.email = "user@email.com":

email | name | document_name | document_readAllowed | document_writeAllowed

SQLAlchemy için tek bir sorgu isteği kullanılarak nasıl yapılabilir? Aşağıdaki kod benim için çalışmıyor:

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

Teşekkürler,


Aşağıdakilerin iki tabloyu birleştirmek için çalıştığını buldum: result = session.query(User, Document).select_from(join(User, Document)).filter(User.email=='user@email.com').all()Ama henüz üç tablo için benzer çalışmayı (DocumentPermissions dahil) yapmayı başaramadım. Herhangi bir fikir?
barankin

Benzer bir görevi gerçekleştirdiğimde SyntaxError: anahtar kelime bir ifade olamaz
Ishan Tomar

Yanıtlar:


101

Bunu dene

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

7
Ne tür bir joinişe yarar? iç, dış, haç veya ne?
Nawaz

7
Bu aslında bir birleştirme yapmaz, bir demetteki satır nesnelerini döndürür. Bu durumda, [(<user>, <document>, <documentpermissions>),...]/
Sahte İsim

39
"hiç birleştirme yapmaz" - bu biraz yanıltıcıdır. select x from a, b ,cÇapraz birleşim olan sql benzeri olacaktır . Filtreler daha sonra onu bir iç birleştirme yapar.
Aidan Kane

7
Sorguyu .all(). Yani print Session.query....tam olarak ne yaptığını görmek için.
boatcoder

4
SQLAlchemy'de yeniyim. .filter()Virgülle ayrılırsa birden çok kriter alabileceğimi fark ettim . .filter()Parantez içinde virgül ayrımı olan birini kullanmak mı yoksa .filter()yukarıdaki cevapta olduğu gibi çoklu kullanmak mı tercih edilir ?
Intrastellar Explorer

53

Bazı ilişkiler ve izinler için birincil anahtar ayarlamak iyi bir stil olabilir (aslında, her şey için tamsayı birincil anahtarları ayarlamak genellikle iyidir , ancak her neyse):

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

Ardından birleştirmelerle basit bir sorgu yapın:

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

querySon satırda ne ayarlandı ve içindeki birleştirilen kayıtlara nasıl erişirsiniz?
Petrus Theron

@pate 'Sorgu neye ayarlanmış' ile ne demek istediğinizden emin değilim, ancak ilişkilere ve verime göre 3-tuple birleşecek. Sorgu () çağrısının argümanları temelde sqlalchemy'deki seçim listesidir.
letitbee

DocumentSorgu sonuç kümesindeki (2. demet değerine) nasıl erişirim?
Petrus Theron

1
@PetrusTheron Dediğim gibi, sorgu 3-tuple verecektir. Öğeleri dizine ekleyebilir veya paketten for (user, doc, perm) in query: print "Document: %s" % doc
çıkarabilirsiniz

1
Whoa, geçmişten gelen patlama. 'izinler', size DocumentPermission nesneleri kümesi veren Document nesnesinin bir özniteliğidir. Dolayısıyla geri referans.
letitbee

42

@Letitbee'nin dediği gibi, tablolara birincil anahtarlar atamak ve uygun ORM sorgulamasına izin vermek için ilişkileri doğru şekilde tanımlamak en iyi uygulamasıdır. Söyleniyor ki...

Aşağıdaki satırlar boyunca bir sorgu yazmakla ilgileniyorsanız:

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

O zaman şöyle bir şey yapmalısın:

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

Bunun yerine şöyle bir şey yapmak istersiniz:

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

O zaman şu satırlarda bir şeyler yapmalısınız:

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

Bununla ilgili bir not ...

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

Daha fazla bilgi için dokümanları ziyaret edin .


4
BUNU YAP. Bakın - birleşimlerde belirtilmiş pek çok şey yok. Bunun nedeni, tablolar / db-modeli zaten bu tablolar arasında uygun yabancı anahtarlarla kurulmuşsa - SQLAlchemy sizin için uygun sütunlara ON'a katılmaya özen gösterecektir.
Brad

8

Abdul'un cevabını genişleterek KeyedTuple, sütunlara katılarak ayrık bir satır koleksiyonu yerine bir satır elde edebilirsiniz :

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

1
Bu çalışıyor. Ancak, nesneyi serileştirirken sütun adları eksik.
Lucas

2

Bu fonksiyon, tuple listesi olarak gerekli tabloyu üretecektir.

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)
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.