I have the following SQLAlchemy mapped classes:
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"))
I need to get a table like this for user.email = "user@email.com"
:
email | name | document_name | document_readAllowed | document_writeAllowed
How can it be made using one query request for SQLAlchemy? The code below does not work for me:
result = session.query(User, Document, DocumentPermission).filter_by(email = "user@email.com").all()
Thanks,
result = session.query(User, Document).select_from(join(User, Document)).filter(User.email=='user@email.com').all()
But I have not managed yet how to make work the similar for three tables (to include DocumentPermissions). Any Idea?
Try this
q = Session.query(
User, Document, DocumentPermissions,
).filter(
User.email == Document.author,
).filter(
Document.name == DocumentPermissions.document,
).filter(
User.email == 'someemail',
).all()
As @letitbee said, its best practice to assign primary keys to tables and properly define the relationships to allow for proper ORM querying. That being said...
If you're interested in writing a query along the lines of:
SELECT
user.email,
user.name,
document.name,
documents_permissions.readAllowed,
documents_permissions.writeAllowed
FROM
user, document, documents_permissions
WHERE
user.email = "user@email.com";
Then you should go for something like:
session.query(
User,
Document,
DocumentsPermissions
).filter(
User.email == Document.author
).filter(
Document.name == DocumentsPermissions.document
).filter(
User.email == "user@email.com"
).all()
If instead, you want to do something like:
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
Then you should do something along the lines of:
session.query(
User
).join(
Document
).join(
DocumentsPermissions
).filter(
User.email == "user@email.com"
).all()
One note about that...
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
For more information, visit the docs.
A good style would be to setup some relations and a primary key for permissions (actually, usually it is good style to setup integer primary keys for everything, but whatever):
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')
Then do a simple query with joins:
query = session.query(User, Document, DocumentsPermissions).join(Document).join(DocumentsPermissions)
query
set to in the last line and how do you access the joined records in it?
Document
(2nd tuple value) in the query result set?
for (user, doc, perm) in query: print "Document: %s" % doc
Expanding on Abdul's answer, you can obtain a KeyedTuple
instead of a discrete collection of rows by joining the columns:
q = Session.query(*User.__table__.columns + Document.__table__.columns).\
select_from(User).\
join(Document, User.email == Document.author).\
filter(User.email == 'someemail').all()
This function will produce required table as list of tuples.
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)
Success story sharing
join
does it do? inner, outer, cross or what?[(<user>, <document>, <documentpermissions>),...]
/select x from a, b ,c
which is a cross join. The filters then make it an inner join..all()
. Soprint Session.query....
to see exactly what it is doing..filter()
can receive multiple criteria if comma separated. Is it preferable to use one.filter()
with comma separations inside the parenthesis, or use multiple.filter()
like the above answer?