【问题标题】:Get posts only from those who follow you back in flask app with sqlalchemy仅从使用 sqlalchemy 在烧瓶应用程序中关注您的人那里获取帖子
【发布时间】:2021-05-26 23:43:30
【问题描述】:

我有一个烧瓶应用程序,它混合使用了 sqlalchemy (ORM) 和烧瓶 sqlalchemy。

我有一个用户模型、一个帖子模型和一个关注者表,如下所示:

from app import db
from flask_login import UserMixin
from sqlalchemy.orm import relationship

class User(UserMixin, db.Model)
    id = db.Column(db.Integer, primary_key=True)
    followed = db.relationship('User', secondary=followers,
                               primaryjoin=(followers.c.follower_id == id),
                               secondaryjoin=(followers.c.followed_id == id),
                               backref=db.backref('followers', lazy='dynamic'), lazy='dynamic') # many to many relationship with other Users
    posts = relationship('Post') # one to many relationship with posts
    # ...

class Post(db.Model)
    id = db.Column(db.Integer, primary_key=True)
    user_id = db.Column(db.Integer, db.ForeignKey('user.id'))
    #...

followers= db.Table('followers',
                       db.Column('follower_id', db.Integer, db.ForeignKey('user.id')),
                       db.Column('followed_id', db.Integer, db.ForeignKey('user.id'))
                       )

我的问题是如何查询 Post 表/模型,以便它只返回关注您的用户(即朋友)撰写的帖子。
flask mega tutorial by Miguel Grinberg(这些模型的基础)使用以下 sqlalchemy 查询来完成与我所追求的类似的事情。

def followed_posts(self):
        return Post.query.join(
            followers, (followers.c.followed_id == Post.user_id)).filter(
                followers.c.follower_id == self.id).order_by(
                    Post.timestamp.desc())

我一直在尝试修改上述查询,以便 Post 表仅与关注者的子集连接,其特点是如果 (a,b) 在关注者中,然后是 (b,a) 在关注者中。我只是不确定如何实现这一点。

任何帮助将不胜感激。此外,如果有人推荐一个指南,可以在没有大量纯 SQL 先决条件知识的情况下进行更“高级”的 sqlalchemy 查询,例如上面的查询,请告诉我。 提前致谢!

【问题讨论】:

  • 我猜你只是在 Miguels 的案例中切换 def followed_posts(self): ... 查询中的 followed_idfollower_id:他列出了你关注的人的帖子,所以反之将是那些关注的人的帖子跟着你。
  • 感谢@van 的建议。这比原始查询更接近我所追求的。但就我而言,我仍然需要检查两个用户是否相互关注。

标签: flask sqlalchemy flask-sqlalchemy


【解决方案1】:

为了在一个查询中执行并获得您想要的结果,以下两个选项应该产生相同的结果,但在pythonicSQL-ic 方面有很大不同。

选项 1: 我觉得这很 pythonic,因为 Poster.followed.any(...) 读作“其中一个追随者是 me。”。但是它产生的 SQL 有点“丑”,虽然它完成了工作:

    def followed_posts__elegant_any(self):
        user_id = self.id
        Poster = aliased(User, name="poster")
        return (
            object_session(self)
            .query(Post)
            .join(Poster, Post.user)
            .filter(Poster.followed.any(User.id == user_id))
            .filter(Poster.followers.any(User.id == user_id))
            .order_by(Post.timestamp.desc())
        )

选项 2:

这要简单得多,并且用非常“纯”的 SQL 逻辑表示:

  • 创建别名以便可以在followers 表上加入两次
  • 执行两次join,保证当前用户和“Poster”的关系是双向的。连接本身并没有真正用于其他任何事情,而是过滤:
def followed_posts__optimal_join(self):
    user_id = self.id
    f1 = aliased(followers)
    f2 = aliased(followers)
    return (
        object_session(self)
        .query(Post)
        .join(User, Post.user)
        .join(f1, and_(User.id == f1.c.follower_id, f1.c.followed_id == user_id))
        .join(f2, and_(User.id == f2.c.followed_id, f2.c.follower_id == user_id))
        .order_by(Post.timestamp.desc())
    )

【讨论】:

  • 感谢您花时间编写这些解决方案。这两个查询都很好地工作,正是我的想法!我只需要在第一次加入时将Post.user 更改为User.query.filter_by(id=Post.user_id)。为我的朋友干杯。
【解决方案2】:

我想通了。

def get_friend_posts():
    return Post.query.filter(Post.user_id.in_([f.id for f in self.followed if f.is_following(self)]))

# helper function
def is_following(self, user):
        return self.followed.filter(followers.c.followed_id == user.id).count() > 0

【讨论】:

  • 我认为这不是最有效的方法,因为您将执行许多 SQL 查询以获得所需的结果。请参阅我对 1 查询选项的回答。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2018-04-13
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-01-22
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多