Flask从入门到做出一个博客的大型教程(四)

在开始之前,先来看下项目的整体结构。

flask
├── app
│   ├── forms.py
│   ├── __init__.py
│   ├── models.py
│   ├── routes.py
│   └── templates
│       ├── base.html
│       ├── index.html
│       └── login.html
├── config.py
├── migrations
│   ├── alembic.ini
│   ├── env.py
│   ├── README
│   ├── script.py.mako
│   └── versions
│       ├── 3884184ade03_tables.py
├── myblog.py

5 用户登录模块

在上一部分讲了数据库,也建立了表,但是用户表里的密码字段一直没用,所以这里讲一讲密码字段。如果密码不加密就明文存储在数据库中,这是一个非常不安全而且愚蠢的行为,所以咱们要一起先看看密码的加密存储。

(venv) duke@coding:~/flask_tutorial/flask$ flask shell
Python 3.6.4 (default, May  3 2018, 19:35:55)
[GCC 5.4.0 20160609] on linux
App: app [production]
Instance: /home/duke/flask_tutorial/flask/instance
>>> from werkzeug.security import generate_password_hash
>>> hash = generate_password_hash('mima')
>>> hash
'pbkdf2:sha256:50000$S9FPhxbX$4f164ff06b409769e44556bcd9d8f906ca82998e433410f85898245e40ecd4d3'

这样就对密码进行了加密,存储在数据库中。但是用户登录输入的密码要怎么和存储在数据库中的这一大串密码进行比对呢?这是就要用到另外一个函数了。

>>> from werkzeug.security import check_password_hash
>>> check_password_hash(hash,'haha')
False
>>> check_password_hash(hash,'mima')
True

这样就可以根据返回的布尔值来判断用户输入的密码是否正确了。既然掌握了密码的加密和核对,那么就要model进一步完善了。

app/models.py : 完善user模型

# ...
from werkzeug.security import generate_password_hash, check_password_hashclass User(db.Model):# ...def set_password(self, password):self.password_hash = generate_password_hash(password)def check_password(self, password):return check_password_hash(self.password_hash, password)

user模型修改好了,现在到flask shell中去试一试是否成功能用?

(venv) duke@coding:~/flask_tutorial/flask$ flask shell
Python 3.6.4 (default, May  3 2018, 19:35:55)
[GCC 5.4.0 20160609] on linux
App: app [production]
Instance: /home/duke/flask_tutorial/flask/instance
>>> from app.models import User
>>> u = User(username='duke',email='duke@qq.com')
>>> u.set_password('mytest')
>>> u.check_password('yourtest')
False
>>> u.check_password('mytest')
True

flask中有很多写的非常不错的插件,像flask-migrate就很不错,这里介绍一个flask-login,当然你肯定可以自己从写一个,但是有别人造好的轮子为什么先不体验一下试试呢?

它会:

  • 在会话中存储当前活跃的用户 ID,让你能够自由地登入和登出。
  • 让你限制登入(或者登出)用户可以访问的视图。
  • 处理让人棘手的 “记住我” 功能。
  • 帮助你保护用户会话免遭 cookie 被盗的牵连。
  • 可以与以后可能使用的 Flask-Principal 或其它认证扩展集成。

但是,它不会:

  • 限制你使用特定的数据库或其它存储方法。如何加载用户完全由你决定。

  • 限制你使用用户名和密码,OpenIDs,或者其它的认证方法。

  • 处理超越 “登入或者登出” 之外的权限。

  • 处理用户注册或者账号恢复。

(venv) duke@coding:~/flask_tutorial/flask$ pip install flask-login

app/_ _ init_ _.py : 登录模块初始化

# ...
from flask_login import LoginManagerapp = Flask(__name__)
# ...
login = LoginManager(app)# ...

你用来表示用户的类需要实现这些属性和方法:

  • is_authenticated

    只有通过验证的用户会满足 login_required的条件。

  • is_active

    如果这是一个活动用户且通过验证,账户也已激活,未被停用,也不符合任何你 的应用拒绝一个账号的条件,返回 True 。不活动的账号可能不会登入(当然, 是在没被强制的情况下)。

  • is_anonymous

    如果是一个匿名用户,返回 True 。(真实用户应返回 False 。)

  • get_id()

    返回一个能唯一识别用户的,并能用于从 user_loader 回调中加载用户的 unicode。注意 必须 是一个 unicode —— 如果 ID 原本是 一个 int 或其它类型,你需要把它转换为 unicode

要简便地实现用户类,你可以从 UserMixin 继承,它提供了对所有这些方法的默认 实现。所以上面哔哔这么多,都可以无视它们,哈哈。

app/models.py : 为了使用flask-login,继承UserMixin

# ...
from flask_login import UserMixinclass User(UserMixin, db.Model):# ...

因为flask-login对数据库是真的一无所知啊,那怎么获得用户信息呢?所以这里要写一个加载用户信息的函数,因为是从session中读取,所以是id不是int类型,咱们要把它转成int类型哦。

app/models.py : flask-login用户加载函数

# ...
from app import login# ...@login.user_loader
def load_user(id):return User.query.get(int(id))

还记得视图模块中对登录处理过的方法吗?现在已经学习了这么多,把以前写的完善修改一下吧。

app/routes.py : 视图模块登录方法

# ...
from flask_login import current_user, login_user
from app.models import User# ...@app.route('/login',methods=['GET','POST'])
def login():#判断当前用户是否验证,如果通过的话返回首页if current_user.is_authenticated:return redirect(url_for('index'))form = LoginForm()#对表格数据进行验证if form.validate_on_submit():#根据表格里的数据进行查询,如果查询到数据返回User对象,否则返回Noneuser = User.query.filter_by(username=form.username.data).first()#判断用户不存在或者密码不正确if user is None or not user.check_password(form.password.data):#如果用户不存在或者密码不正确就会闪现这条信息flash('无效的用户名或密码')#然后重定向到登录页面return redirect(url_for('login'))#这是一个非常方便的方法,当用户名和密码都正确时来解决记住用户是否记住登录状态的问题login_user(user,remember=form.remember_me.data)return redirect(url_for('index'))return render_template('login.html',title='登录',form=form)

登录问题解决了,那么就要解决登出问题了。这个非常简单,直接调用一个方法即可。

app/routes.py : 解决登出问题

# ...
from flask_login import logout_user# ...@app.route('/logout')
def logout():logout_user()return redirect(url_for('index'))

好的,现在又新增了一个登出的方法,那么模板也要相应的增加一个登出的地方喽。

app/templates/base.html : 添加登出

     <div>博客 :<a href="{{ url_for('index') }}">首页</a><a href="{{ url_for('login') }}">登录</a><a href="{{ url_for('logout') }}">退出</a></div>

在你浏览过的大部分网站中,有一部分功能是要你登录后才能使用的,那么在flask中怎么实现这个登录才能使用的功能呢?

app/_ _ init _ _.py : 增加登录限制

# ...
# 在login = LoginManager(app)后面加上即可
login.login_view = 'login'

app/routes.py : 继续在视图模块中修改,导入装饰器

from flask_login import login_required@app.route('/')
@app.route('/index')
#这样,必须登录后才能访问首页了,会自动跳转至登录页
@login_required
def index():# ...

现在如果你访问首页http:// localhost:5000/ ,你就会发现会自动跳转至登录页,而且地址栏变成http:// localhost:5000/login?next=%2F,但是我想登录后想重定向原本要去的网址,怎么解决?肯定和next这个参数有关系,所以我们要对视图模块的登录函数进行修改。

# ...
from flask import request
from werkzeug.urls import url_parse#...@app.route('/login',methods=['GET','POST'])
def login():# ...if form.validate_on_submit():user = User.query.filter_by(username=form.username.data).first()if user is None or not user.check_password(form.password.data):flash('无效的用户名或密码')return redirect(url_for('login'))login_user(user,remember=form.remember_me.data)#此时的next_page记录的是跳转至登录页面是的地址next_page = request.args.get('next')#如果next_page记录的地址不存在那么就返回首页if not next_page or url_parse(next_page).netloc != '':next_page = url_for('index')#综上,登录后要么重定向至跳转前的页面,要么跳转至首页return redirect(next_page)#...

后端管理的代码都写的差不多了,那就改一改前端的页面吧。

app/templates/index.html : 显示当前用户的用户名和他的内容

{% extends 'base.html' %}{% block content %}<h1>你好呀, {{ current_user.username }} !</h1>{% for post in posts %}<div><p>{{ post.author.username }} 说:<b>{{ post.body }}</b></p></div>{% endfor %}{% endblock %}

额,下面咱们就要测试一下了,但是测试之前把首页的登录限制和指定的user去掉去掉

@app.route('/')
@app.route('/index')
def index():# ...return render_template('index.html',title='我的',user=user,posts=posts)

先写入一条测试用例:

(venv) duke@coding:~/flask_tutorial/flask$ flask shell
Python 3.6.4 (default, May  3 2018, 19:35:55)
[GCC 5.4.0 20160609] on linux
App: app [production]
Instance: /home/duke/flask_tutorial/flask/instance
>>> from app import db
>>> from app.models import User,Post
>>> u = User(username='duke',email='duke@qq.com')
>>> u.set_password('123456')
>>> db.session.add(u)
>>> db.session.commit()

好的,访问试一试吧!

登录后的界面!

退出后的界面!

登录写好了,也自己能往里面写数据了,但是不方便啊,一点点敲命令进去,反人类啊,不行不行。所以来写一个注册的方法吧!

首先要在表单模块修改喽。

app/forms.py : 添加注册表格

# ...
from wtforms.validators import ValidationError,Email,EqualTo
from app.models import User# ...class RegistrationForm(FlaskForm):username = StringField('用户名', validators=[DataRequired()])email = StringField('邮箱', validators=[DataRequired(), Email()])password = PasswordField('密码', validators=[DataRequired()])password2 = PasswordField('重复密码', validators=[DataRequired(), EqualTo('password')])submit = SubmitField('注册')#校验用户名是否重复def validate_username(self, username):user = User.query.filter_by(username=username.data).first()if user is not None:raise ValidationError('用户名重复了,请您重新换一个呗!')#校验邮箱是否重复def validate_email(self, email):user = User.query.filter_by(email=email.data).first()if user is not None:raise ValidationError('邮箱重复了,请您重新换一个呗!')

注册表格处理都写好了,那没页面怎么能行?

app/templates/register.html : 写注册界面

{% extends "base.html" %}{% block content %}<h1>注册</h1><form action="" method="post">{{ form.hidden_tag() }}<p>{{ form.username.label }}<br>{{ form.username(size=32) }}<br>{% for error in form.username.errors %}<span style="color: red;">[{{ error }}]</span>{% endfor %}</p><p>{{ form.email.label }}<br>{{ form.email(size=64) }}<br>{% for error in form.email.errors %}<span style="color: red;">[{{ error }}]</span>{% endfor %}</p><p>{{ form.password.label }}<br>{{ form.password(size=32) }}<br>{% for error in form.password.errors %}<span style="color: red;">[{{ error }}]</span>{% endfor %}</p><p>{{ form.password2.label }}<br>{{ form.password2(size=32) }}<br>{% for error in form.password2.errors %}<span style="color: red;">[{{ error }}]</span>{% endfor %}</p><p>{{ form.submit() }}</p></form>
{% endblock %}

注册页面写好了,那登录界面得给它一个链接才行,所以到登录面添加个超链接吧。

app/templates/login.html : 添加一个注册的链接。

<h1>登 录</h1>
<p>还没注册? <a href="{{ url_for('register') }}">点击一下就可以注册哦!</a></p>

现在什么都准备好了,就差视图模块进行一个总结啦,进入视图模块,添加方法吧!

# ...
from app import db
from app.forms import RegistrationForm# ...@app.route('/register', methods=['GET', 'POST'])
def register():# 判断当前用户是否验证,如果通过的话返回首页if current_user.is_authenticated:return redirect(url_for('index'))form = RegistrationForm()if form.validate_on_submit():user = User(username=form.username.data, email=form.email.data)user.set_password(form.password.data)db.session.add(user)db.session.commit()flash('恭喜你成为我们网站的新用户!')return redirect(url_for('login'))return render_template('register.html', title='注册', form=form)

注册页面哦 !

Flask从入门到做出一个博客的大型教程(四)相关推荐

  1. Flask从入门到做出一个博客的大型教程(五)

    Flask从入门到做出一个博客的大型教程(五) 在开始之前,先来看下项目的整体结构. flask/ ├── app │ ├── forms.py │ ├── __init__.py │ ├── mod ...

  2. Flask从入门到做出一个博客的大型教程(一)

    Flask从入门到做出一个博客的大型教程(一) 本项目全部在虚拟环境中运行,因此请参照前面的文章,链接为https://blog.csdn.net/u014793102/article/details ...

  3. Flask从入门到做出一个博客的大型教程

    https://blog.csdn.net/u014793102/article/category/7661475

  4. hexo 环境变量_小白使用 Github + Hexo 从 0 搭建一个博客

    最近有几位同学在公众号后台留言问我的博客站是怎么建站的,思来想去,还是写一篇从 0 开始吧. 前置准备 我们先聊一下前置准备,可能很多同学一听说要自己搭一个博客系统,直接就望而却步.不得有台服务器么, ...

  5. 个人博客建站教程wordpress+wdcp(小白入门首选)

    原文链接:个人博客建站教程 前言: 如何搭建个人博客,以及网站呢? 今天带领大家从小白完成搭建个人博客,以及网站. 如果你不知道,请不要觉得麻烦,跟着步骤很简单就会完成. 那么准备好了,我们就开始建站 ...

  6. 【FastAPI 03】FastAPI快速搭建一个博客系统

    标题:FastAPI快速搭建一个博客系统 大家好,我是Kuls. 这是<小白学FastAPI>系列的第三篇文章. 今天我们主要讲的是FastAPI快速搭建一个博客系统. 这里可能有些小伙伴 ...

  7. 最近做了一个博客 玩玩而已 运城搜搜 www.lenovoyh.com

    最近做了一个博客 玩玩而已 运城搜搜 www.lenovoyh.com  用了以前一个没用的域名做的  现在还没排名 等待吧 呵呵 转载于:https://www.cnblogs.com/kiah/a ...

  8. 运用BT在centos下搭建一个博客论坛

    在日常的工作和学习中,我们都很希望有自己的工作站,就是自己的服务器,自己给自己搭建一个博客或者是论坛,用于自己来写博客和搭建网站论坛.现在我们就用一个简单的方法来教大家如何30分钟内部署一个博客网站. ...

  9. Docker学习总结(6)——通过 Docker 化一个博客网站来开启我们的 Docker 之旅

    2019独角兽企业重金招聘Python工程师标准>>> 通过 Docker 化一个博客网站来开启我们的 Docker 之旅 这篇文章包含 Docker 的基本概念,以及如何通过创建一 ...

最新文章

  1. 再有人问你Netty是什么,就把这篇文章发给他
  2. 小博老师解析Java核心技术 ——JDBC数据库操作类封装
  3. Frameless - 用于预览 iOS8 原型的浏览器
  4. Linus Torvalds:我们都老了,但Linux维护者真的很难找
  5. 信息学奥赛一本通(1159:斐波那契数列)
  6. oracle获取表前几行,Oracle查询以获取同一表中相关行的先前值
  7. 观点 | 2017,区块链与加密货币“追逐者”玩命的一年
  8. matlab中箭头详细设置
  9. VSCode 拓展插件推荐
  10. 【信号处理】采样定理的深入浅出
  11. 【《系统集成项目管理工程师》必备100题】
  12. 零基础新手如何自学PS
  13. 几款常见的可视化HTML编辑器 WYSIWYG
  14. python迅雷下载任务出错_迅雷下载任务出错的原因和解决方法 来研究下吧
  15. SpringBoot测试类不需要加@RunWith?
  16. 【文末送书】知识体系目录
  17. Cannot create an instance of class AndroidViewModel (androidx ViewModelProvider AndroidViewModel)
  18. CSS的浮动以及清楚浮动的方法
  19. centos7 修改连接数_centos7+rsyslog+loganalyzer+mysql 搭建rsyslog日志服务器 - 夜空守望者2020...
  20. 无刷电机和桨叶的选择

热门文章

  1. Jquery获取radio的状态
  2. 【pytorch】CNN实战-花朵种类识别
  3. linux达人养成计划 ppt,Linux达人养成计划 I
  4. 一款分类超多的手机壁纸PHP源码
  5. Java对比两张Excel表的数据_比较两个Excel表格并提取重复数据
  6. Discuz!X3.4 全新安装教程
  7. 职场高手不会告诉你的10倍速学习心法!解密 “库伯学习圈” 和 “费曼学习法”...
  8. ashx PHP文件 优劣,ashx的文件如何控制安全性
  9. 浏览器兼容模式下,上传文件问题
  10. cefsharp保存文件为pdf