简体   繁体   中英

Understanding of join in SqlAlchemy ORM

I actually have working codes but I'm wondering if it can be done more in the Sqlalchmey ORM way.

I have these 3 tables:

class NodesModel(db.Model):
    __tablename__ = 'nodes'

    id = db.Column(db.BigInteger, primary_key=True)
    project_id = db.Column(db.BigInteger, db.ForeignKey('projects.id'))
    name = db.Column(db.String)

    posts_nodes = relationship("PostsNodesModel", backref="nodes")
class PostsModel(db.Model):
    __tablename__ = 'posts'

    id = db.Column(db.BigInteger, server_default=db.FetchedValue(), primary_key=True)
    project_id = db.Column(db.BigInteger, db.ForeignKey('projects.id'))

    posts_nodes = relationship("PostsNodesModel", backref="posts")
class PostsNodesModel(db.Model):
    __tablename__ = 'posts_nodes'

    post_id = db.Column(db.BigInteger, db.ForeignKey('posts.id'), primary_key=True)
    node_id = db.Column(db.BigInteger, db.ForeignKey('nodes.id'), primary_key=True)

So I have this query which would look for the name and get the posts that is associated with the name:

return (db.session.query(PostsModel.data_date, NodesModel.topic)
        .join(PostsNodesModel, PostsNodesModel.node_id == NodesModel.id)
        .join(PostsModel, PostsModel.id == PostsNodesModel.post_id)
        .filter(NodesModel.project_uuid == project_uuid)
        .all())

This works but I read this stackoverflow question In SQLAlchemy what is the difference between the "filter" vs "join and filter" syntax?

and noticed that person was using the method

.join(Author, Publisher, Retailer)

And that person said that the relationship takes care of the links.

I've tried to do it for my statement:

return (db.session.query(NodesModel)
        .with_entities(PostsModel.data_date, NodesModel.topic)
        .join(PostsNodesModel, PostsModel)
        .filter(NodesModel.project_uuid == project_uuid)
        .all())

But of course, it didn't work.

So out of learning more about Sqlalchemy, can anyone get the above to work and explain or point me to an explanation of how this can be done?

There is this line in the Sqlalchemy "Configuring how Relationship Joins" that states "relationship() will normally create a join between two tables by examining the foreign key relationship between the two tables to determine which columns should be compared." So in actual fact my statement above should work.

I've tried reading the documentation on it but I'm still no closer to understanding how it works and how to get it to work.

Another thing, there is no parent or child as my queries goes both ways, I sometimes query the post to get the names that is linked to this post.

Thanks. Desmond

You can try:


query = NodesModel.join(
    PostsNodeModel, PostsNodesModel.node_id == NodesModel.id
).join(
    PostsModel, PostsModel.id == PostsNodesModel.post_id
).filter(
    NodesModel.project_uuid == project_uuid
).with_entities(
 .filter(NodesModel.project_uuid == project_uuid)
)

return query

And from the calling function you can simply call .all()

def calling_function():
   query = get_query()
   query.all()

After the help from benvc, I've been looking and puzzling over it and I've found the answer.

My rewritten models:

class PostsModel(db.Model):
    __tablename__ = 'posts'

    id = db.Column(db.BigInteger, server_default=db.FetchedValue(), primary_key=True)
    project_id = db.Column(db.BigInteger, db.ForeignKey('projects.id'))

    post = relationship("PostsModel", back_populates="nodes")
    node = relationship("NodesModel", back_populates="posts")
class PostsModel(db.Model):
    __tablename__ = 'posts'

    id = db.Column(db.BigInteger, server_default=db.FetchedValue(), primary_key=True)
    project_id = db.Column(db.BigInteger, db.ForeignKey('projects.id'))

    nodes = relationship("PostsNodesModel", back_populates="post")
class PostsNodesModel(db.Model):
    __tablename__ = 'posts_nodes'

    post_id = db.Column(db.BigInteger, db.ForeignKey('posts.id'), primary_key=True)
    node_id = db.Column(db.BigInteger, db.ForeignKey('nodes.id'), primary_key=True)

    posts = relationship("PostsNodesModel", back_populates="node")

The query:

return (db.session.query(PostsModel.data_date, NodesModel.topic)
        .join('nodes', 'node')
        .filter(NodesModel.project_uuid == project_uuid)
        .all())

The nodes and node (in the join statement) is using the nodes variable in PostsModel and the node is the variable in PostsNodesModel.

Interestingly, you have to put the nodes first. If you reverse the arrangement (ie join('node', 'nodes'), Sqlalchemy states that there is no property of node in PostsModel. This query reads from PostsModel to NodesModel as stated in the query. The following also works:

return (db.session.query(NodesModel.topic, PostsModel.data_date)
        .join('posts', 'post')
        .filter(NodesModel.project_uuid == project_uuid))

This creates the join statement from NodesModel to PostsModel and the join arrangement reflects that.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM