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 %}
Followers.html
.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','',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 学习 十一 关注者的更多相关文章
- Flask学习之八 关注、联系人和好友
英文博客地址:http://blog.miguelgrinberg.com/post/the-flask-mega-tutorial-part-viii-followers-contacts-and- ...
- flask学习(十一):if判断语句
1. 语法: {% if xxx %} {% else %} {% endif %} 2. if的使用,和python中if相差无几 用if判断,当用户名存在且年龄大于18时,才显示用户名和注销
- [ZHUAN]Flask学习记录之Flask-SQLAlchemy
From: http://www.cnblogs.com/agmcs/p/4445583.html 各种查询方式:http://www.360doc.com/content/12/0608/11/93 ...
- Flask 学习目录
Flask 学习目录 Flask 的学习过程是一个了解如何从单个模块搭建一个 Web 框架的过程. Python.Flask 的安装和设置 简单的 Hello World 程序 使用 Jinjia2 ...
- Python Flask学习笔记之模板
Python Flask学习笔记之模板 Jinja2模板引擎 默认情况下,Flask在程序文件夹中的templates子文件夹中寻找模板.Flask提供的render_template函数把Jinja ...
- Python Flask学习笔记之Hello World
Python Flask学习笔记之Hello World 安装virtualenv,配置Flask开发环境 virtualenv 虚拟环境是Python解释器的一个私有副本,在这个环境中可以安装私有包 ...
- 【转载】 强化学习(十一) Prioritized Replay DQN
原文地址: https://www.cnblogs.com/pinard/p/9797695.html ------------------------------------------------ ...
- Flask学习-Wsgiref库
一.前言 前面在Flask学习-Flask基础之WSGI中提到了WerkZeug,我们知道,WerkZeug是一个支持WSGI协议的Server,其实还有很多其他支持WSGI协议的Server.htt ...
- Flask 学习篇二:学习Flask过程中的记录
Flask学习笔记: GitHub上面的Flask实践项目 https://github.com/SilentCC/FlaskWeb 1.Application and Request Context ...
随机推荐
- Xampp apache与mySQL开不了 解决办法
Xampp安装后,打开Xampp control panel. 点击Apache对应的Start,开不了.原因是系统的服务占用了80端口,所以要么结束系统服务,要么修改apache端口. 个人比较喜欢 ...
- 【BZOJ4012】开店(主席树)
[BZOJ4012]开店(主席树) 题面 Description 风见幽香有一个好朋友叫八云紫,她们经常一起看星星看月亮从诗词歌赋谈到 人生哲学.最近她们灵机一动,打算在幻想乡开一家小店来做生意赚点钱 ...
- Vue-生命周期图示 注解
根据腾讯课堂视频讲解,将官网生命周期图示进行注解,以加深印象和理解 贴一个源码示例: 注意位置和写法
- 空间金字塔池化(Spatial Pyramid Pooling, SPP)原理和代码实现(Pytorch)
想直接看公式的可跳至第三节 3.公式修正 一.为什么需要SPP 首先需要知道为什么会需要SPP. 我们都知道卷积神经网络(CNN)由卷积层和全连接层组成,其中卷积层对于输入数据的大小并没有要求,唯一对 ...
- webrtc起步 - apprtc服务器搭建
简介 apprtc 是什么,webrtc.org官方指定体验app 原料: ubuntu14.04,其他linux版本不限,官方并没特殊说明 chrome M51+ stunnle ...
- 面向对象设计模式_命令模式(Command)解读
在.Net框架中很多对象的方法中都会有Invoke方法,这种方法的设计实际是用了设计模式的命令模式, 模式图如下 其核心思路是将Client 向Receiver发送的命令行为进行抽象(ICommand ...
- C#中的多线程与线程互斥
通过多线程,C#可以并行地执行代码. 每一个线程都有它独立的执行路径,所有线程都能访问共有变量. 这就引发了线程竞争 这时就需要使用线程安全的处理方式使得线程互斥 先来看一段多线程代码 using S ...
- 发个2012年用java写的一个控制台小游戏
时间是把杀狗刀 突然发现了12年用java写的控制台玩的一个文字游戏,有兴趣的可以下载试试哈汪~ 里面难点当时确实遇到过,在计算倒计时的时候用了多线程,当时还写了好久才搞定.很怀念那个时间虽然不会做游 ...
- python第二篇博客,关于数据类型的详细讲解
数据,就是我们要存储的信息,因为信心种类很多,所以,数据种类也有很多 数字类型数据 整数int 表示年龄,手机号等等 浮点型float 表示身高,工资等带有小数点的数据 字符串类型数据 用来做描述性信 ...
- xampp配置多端口访问
1.修改D:\xampp\apache\conf\extra中的httpd-vhosts.conf文件,在最底部添加 <VirtualHost *:8080> ##需要监听的端口号 Ser ...