sqlalchemy:如何通过一个查询联接多个表?


92

我有以下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:关键字不能是表达式
Ishan Tomar

Answers:


91

试试这个

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

6
什么样的join它能做什么?内部,外部,交叉还是什么?
纳瓦兹

7
实际上,这根本不执行联接,它返回元组中的行对象。在这种情况下,它将返回[(<user>, <document>, <documentpermissions>),...]/
假名称

32
“根本不参加”-这有点误导。它将像select x from a, b ,c交叉连接一样具有sql 。然后,筛选器使其成为内部联接。
艾丹·凯恩2016年

7
您可以通过省略来打印查询.all()。因此,print Session.query....要确切了解它在做什么。
boatcoder '17

4
我是SQLAlchemy的新手。我注意到.filter()如果用逗号分隔可以收到多个条件。最好.filter()在圆括号内使用逗号分隔,还是.filter()像上面的答案一样使用倍数?
星际探索者

50

一种好的样式是为权限设置一些关系和一个主键(实际上,通常为所有的东西设置整数主键是一种好样式,但无论如何):

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

@pate我不确定“查询设置为”是什么意思,但是它将根据关系和产量3元组进行联接。query()调用的参数本质上是sqlalchemy中的选择列表。
letitbee 2014年

如何访问Document查询结果集中的(第二个元组值)?
Petrus Theron

1
@PetrusTheron就像我说的那样,查询将产生3个元组。您可以索引元素,也可以仅解压缩文件:for (user, doc, perm) in query: print "Document: %s" % doc
letitbee

1
哇,爆炸过去。“权限”是Document对象的一个​​属性,为您提供了一组DocumentPermission对象。因此,backref。
letitbee

37

就像@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

有关更多信息,请访问docs


4
做这个。请参阅-联接中没有指定太多内容。这是因为,如果已经使用这些表之间的正确外键设置了表/数据库模型-SQLAlchemy将为您连接适当的列。
布拉德(Brad)

8

扩展Abdul的答案,您可以KeyedTuple通过连接列来获取而不是离散的行集合:

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

1
这可行。但是,序列化对象时缺少它们​​的列名。
卢卡斯

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)
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.