Flask 学习 十一 关注着
Posted Erick - LONG
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Flask 学习 十一 关注着相关的知识,希望对你有一定的参考价值。
数据库关系
1.1多对多关系
添加第三张表(关联表),多对多关系可以分解成原表和关联表之间的两个一对多的关系
多对多仍然使用db.relationship()方法定义,但是secondary参数必须设定为关联表,多对多关系可以在任何一个表中定义,backref参数会处理好关系的另一侧
1.2 自引用关系
如果关系中的两侧都在同一个表中称为自引用关系,在关注中,关系的左侧是用户实体,为关注着,关系的右侧也是用户实体,但是被关注着
本例的关联表是 follows,其中每一行都表示一个用户关注了另一个用户。图中左边表示的一对多关系把用户和 follows 表中的一组记录联系起来,用户是关注者。图中右边表示的一对多关系把用户和 follows 表中的一组记录联系起来,用户是被关注者。
1.3 高级多对多关系
在多对多关系中需要存储实体之间的额外信息,用户之间的关注可以把时间信息存储在关联表中
app/models.py/Follow 关注关联表模型的实现
class Follow(db.Model): __tablename__ = \'follows\' follower_id = db.Column(db.Integer,db.ForeignKey(\'users.id\'),primary_key=True) followed_id = db.Column(db.Integer, db.ForeignKey(\'users.id\'), primary_key=True) timestamp=db.Column(db.DateTime,default=datetime.utcnow)
app/models.py/User 使用两个一对多关系实现的多对多关系
class User(UserMixin,db.Model):
。。。。 # 为了消除外键间的歧义,定义关系时使用foreign_keys指定外键 # db.backref指的是回引Follow模型,lazy=\'joined\'可以实现立即一次性完成从连结查询中加载相关对象 # 如果把joined换成select就会成倍增加数据库查询次数 # lazy=\'dynamic\' 直接返回查询对象,可以在执行查询之前添加过滤器 # cascade 参数配置在父对象上执行的操作对相关对象的影响 # 层叠选项可设定添加用户到数据库会话中,自动把所有的关系对象添加到会话中 # delete-orphan的作用是把默认层叠行为(把对象联结的所有相关对象的外键设为空值),变成删除记录后把指向该记录的实体也删除,这样就有效的销毁的联结 # \'all,delete-orphan\'是逗号分隔的层叠选项,表示启用所有默认层叠选项并删除关联记录,all表示除了delete-orphan之外的所有层叠选项, followed=db.relationship(\'Follow\',foreign_keys=[Follow.follower_id], backref=db.backref(\'follower\',lazy=\'joined\'), lazy=\'dynamic\', cascade=\'all,delete-orphan\') followers = db.relationship(\'Follow\', foreign_keys=[Follow.followed_id], backref=db.backref(\'followed\', lazy=\'joined\'), lazy=\'dynamic\', cascade=\'all,delete-orphan\')
app/models.py/User 定义关注关系的辅助方法
class User(UserMixin,db.Model):
。。。 def follow(self,user): if not self.is_following(user): # 把关注着和被关注着联结在一起传入构造器并添加到数据库中 f = Follow(follower=self,followed=user) db.session.add(f) def unfollow(self,user): # followed找到联结用户和被关注用户的实例 f = self.followed.filter_by(followed_id=user.id).first() if f : # 销毁用户之间的联结,删除这个对象即可 db.session.delete(f) def is_following(self,user): # 搜索两边指定的用户,如果找到返回True return self.followed.filter_by(followed_id=user.id).first() is not None def is_followed_by(self,user): # 搜索两边指定的用户,如果找到返回True return self.followers.filter_by(follower_id=user.id).first() is not None
2 在资料页显示关注者
2.1 在用户资料页上部添加关注信息
<p> {% if current_user.can(Permission.FOLLOW) and user != current_user %} {% if not current_user.is_following(user) %} <a href="{{ url_for(\'.follow\',username=user.username) }}" class="btn btn-primary">关注</a> {% else %} <a href="{{ url_for(\'.unfollow\',username=user.username) }}" class="btn btn-default">取消关注</a> {% endif %} {% endif %} <a href="{{ url_for(\'.followers\',username=user.username) }}">关注数:<span class="badge">{{ user.followers.count() }}</span></a> <a href="{{ url_for(\'.followed_by\',username=user.username) }}">粉丝数:<span class="badge">{{ user.followed.count() }}</span></a> {% if current_user.is_authenticated and user != current_user and user.is_following(current_user) %} | <span class="label label-default">关注了你</span> {% endif %} </p>
用户点击关注按钮后执行/follow/<username>路由
app/main/views.py “关注”路由和视图函数
# 用户点击关注按钮后执行/follow/<username>路由 @main.route(\'/follow/<username>\') @login_required @permission_required(Permission.FOLLOW) def follow(username): user = User.query.filter_by(username=username).first() if user is None: flash(\'用户不存在\') return redirect(url_for(\'.index\')) if current_user.is_following(user): flash(\'你已经关注他了\') return redirect(url_for(\'.user\',username=username)) current_user.follow(user) flash(\'关注成功\') return redirect(url_for(\'.user\',username=username)) # 用户点击取消关注按钮后执行/unfollow/<username>路由 @main.route(\'/unfollow/<username>\') @login_required @permission_required(Permission.FOLLOW) def unfollow(username): user = User.query.filter_by(username=username).first() if user is None: flash(\'用户不存在\') return redirect(url_for(\'.index\')) if not current_user.is_following(user): flash(\'你还没有关注他\') return redirect(url_for(\'.user\',username=username)) current_user.unfollow(user) flash(\'取消关注成功\') return redirect(url_for(\'.user\',username=username)) # 用户点击关注者数量后调用/followers/<username> @main.route(\'/followers/<username>\') def followers(username): user = User.query.filter_by(username=username).first() if user is None: flash(\'用户不存在\') return redirect(url_for(\'.index\')) page = request.args.get(\'page\',1,type=int) pagination = user.followers.paginate(page,per_page=current_app.config[\'FLASKY_FOLLOWERS_PER_PAGE\'], error_out=False) follows =[{\'user\':item.follower,\'timestamp\':item.timestamp} for item in pagination.items] return render_template(\'followers.html\',user=user,title=\'粉丝列表\', endpoint=\'.followers\',pagination=pagination,follows=follows) # 用户点击粉丝数量后调用/followed/<username> @main.route(\'/followed_by/<username>\') def followed_by(username): user = User.query.filter_by(username=username).first() if user is None: flash(\'用户不存在\') return redirect(url_for(\'.index\')) page = request.args.get(\'page\',1,type=int) pagination = user.followed.paginate(page,per_page=current_app.config[\'FLASKY_FOLLOWERS_PER_PAGE\'],error_out=False) follows=[{\'user\':item.followed,\'timestamp\':item.timestamp} for item in pagination.items] return render_template(\'followers.html\',user=user,title=\'关注列表\', endpoint=\'.followed_by\',pagination=pagination,follows=follows)
{% extends "base.html" %} {% import "_macros.html" as macros %} {% block title %}Flasky - {{ title }} {{ user.username }}{% endblock %} {% block page_content %} <div class="page-header"> <h1>{{ title }} {{ user.username }}</h1> </div> <table class="table table-hover followers"> <thead><tr><th>用户</th><th>时间</th></tr></thead> {% for follow in follows %} <tr> <td> <a href="{{ url_for(\'.user\', username = follow.user.username) }}"> <img class="img-rounded" src="{{ follow.user.gravatar(size=32) }}"> {{ follow.user.username }} </a> </td> <td>{{ moment(follow.timestamp).format(\'YYYY年M月D日 HH:mm:ss\') }}</td> </tr> {% endfor %} </table> <div class="pagination"> {{ macros.pagination_widget(pagination, endpoint, username = user.username) }} </div> {% endblock %}
.table.followers tr { border-bottom: 1px solid #e0e0e0; }
2.3使用数据库联结查询所关注用户的文章
app/models.py
class User(UserMixin,db.Model): # 定义方法为属性,找到关注用户所发表的文章,SQLalchemy 首先收集所有的过滤器,再以最高效的方式生成查询 @property def followed_post(self): return Post.query.join(Follow,Follow.followed_id==Post.author_id).filter(Follow.follower_id == self.id)
2.4
app/main/views.py 显示所有博客文章或者只显示所关注的用户的文章
@main.route(\'/\', methods=[\'get\', \'post\']) def index():
。。。 show_followed = False if current_user.is_authenticated: show_followed = bool(request.cookies.get(\'show_followed\',\'\')) if show_followed: query = current_user.followed_posts else: query = Post.query # 显示分页需要用到sqlachemy提供的paginate方法 pagination=query.order_by(Post.timestamp.desc())\\ .paginate(page,per_page=current_app.config[\'FLASKY_POSTS_PER_PAGE\'],error_out=False) # 显示当前页面的记录 posts = pagination.items return render_template(\'index.html\',form=form,posts=posts,show_followed=show_followed,pagination=pagination)
app/main/views.py 设定cookie 来查询所有文章还是用户关注文章
@main.route(\'/all\') @login_required def show_all(): resp = make_response(redirect(url_for(\'.index\'))) resp.set_cookie(\'show_followed\',\'\',max_age=30*24*60*60) return resp @main.route(\'/followed\') @login_required def show_followed(): # cookies只能在响应中设置,需要用make_response创建响应对象 resp = make_response(redirect(url_for(\'.index\'))) # max_age设定cookies的过期时间,30*24*60*60为30天 resp.set_cookie(\'show_followed\',\'1\',max_age=30*24*60*60) return resp
index.html 添加两个导航选项卡,分别调用all和followed文章的选项卡
<div class="post-tabs"> <ul class="nav nav-tabs"> <li{% if not show_followed %} class="active"{% endif %}><a href="{{ url_for(\'.show_all\') }}">所有文章</a></li> {% if current_user.is_authenticated %} <li{% if show_followed %} class="active"{% endif %}><a href="{{ url_for(\'.show_followed\') }}">好友文章</a></li> {% endif %} </ul> {% include \'_posts.html\' %} </div>
在关注列表显示自己的文章
app.models.py 构建用户时把自己设为自己的关注着
class User(UserMixin,db.Model): def __init__(self,**kwargs):
。。。 # 构建用户时把自己设为自己的关注者 self.follow(self)
添加函数更新现有用户
class User(UserMixin,db.Model):
。。。 # 把用户设为自己的关注者 @staticmethod def add_self_follows(): for user in User.query.all(): if not user.is_following(user): user.follow(user) db.session.add(user) db.session.commit()
在shell中运行这个函数更新数据库
python manage.py shell
User.add_self_follows()
为保证数据准确需要在粉丝数,关注数中减去1,关注和粉丝列表也要除去自己
user.html user.followers.count() - 1
<a href="{{ url_for(\'.followers\',username=user.username) }}">粉丝数:<span class="badge">{{ user.followers.count() - 1 }}</span></a> <a href="{{ url_for(\'.followed_by\',username=user.username) }}">关注数:<span class="badge">{{ user.followed.count() - 1 }}</span></a>
followers.html 中添加判断以不显示自己
{% if follow.user != user %}
以上是关于Flask 学习 十一 关注着的主要内容,如果未能解决你的问题,请参考以下文章