利用Flask搭建微电影视频网站(五):访问控制器
关于博主
努力与运动兼备~~~有任何问题可以加我好友或者关注微信公众号,欢迎交流,我们一起进步!
微信公众号: 啃饼思录
![](https://img.haomeiwen.com/i8964398/e5729d3e50344ba3.jpg)
QQ: 2810706745(啃饼小白)
写在前面
本篇笔记,我们开始介绍后台页面逻辑的开发,里面涉及的知识点很多,请跟紧我的步伐,先说访问控制器(视图装饰器)。
本篇笔记对应上传的仓库为:https://github.com/licheetools/movie对应第五篇。
管理员登录
1、app/--init--.py 中创建db对象
2、app/models.py 中导入db对象
3、app/admin/forms.py 定义表单验证
4、app/templates/admin/login.html 中使用表单字段,信息验证,消息闪现
5、app/admin/views.py 中处理登录请求,保存会话
6、app/admin/views.py 定义登录装饰器,访问控制
使用到的模型:
模型: Admin
表单: LoginForm
请求方法: GET POST
访问控制: 无
然后进行代码的调整和优化,具体如下:打开app/--init--.py和models.py文件,将models.py中涉及app的代码,全部粘贴到--init--.py中,models.py只保留datetime的导入部分和db的引入部分,再删除--init--.py中重复包的导入部分即可,这么说你可能不明白,我直接贴上修改后的代码:
# --init--.py
from flask import Flask, render_template
from flask_sqlalchemy import SQLAlchemy
import pymysql
app = Flask(__name__)
app.config["SQLALCHEMY_DATABASE_URI"] = "mysql+pymysql://root:root@127.0.0.5/movie"
app.config["SQLALCHEMY_TRACK_MODIFICATIONS "] = True
app.debug = True
db = SQLAlchemy(app)
from app.home import home as home_blueprint
from app.admin import admin as admin_blueprint
app.register_blueprint(home_blueprint)
app.register_blueprint(admin_blueprint, url_prefix="/admin")
# 404页面
@app.errorhandler(404)
def page_not_found(error):
return render_template("home/404.html"), 404
# models.py
from datetime import datetime
from app import db
其余代码不变
然后运行一下我们的项目,你会发现出现一个警告:
FSADeprecationWarning: SQLALCHEMY_TRACK_MODIFICATIONS adds significant overhead and will be disabled by default in the future. Set it to True or False to suppress this warning.
'SQLALCHEMY_TRACK_MODIFICATIONS adds significant overhead and '
那是因为默认这里是None,我们需要设定为True或者False,点击出现问题的文件:
![](https://img.haomeiwen.com/i8964398/03ae62f6635b2d6e.png)
我们把这里的None修改为True即可!然后重新启动就没有问题了。
Python3中Flask使用flask_sqlalchemy的的问题以及解决
开始定义后段登录表单
首先安装flask-wtf,你可以选择pip install flask-wtf
或者在Pycharm里面安装。
然后打开forms.py文件,新增以下代码:
from flask_wtf import FlaskForm
from wtforms import StringField, PasswordField, SubmitField
from wtforms.validators import DataRequired
# 后台管理员登录表单
class LoginForm(FlaskForm):
account = StringField(
label="账号",
validators=[
DataRequired("账号不能为空!")
],
description="账号",
render_kw={
"class": "form-control",
"placeholder": "请输入账号!",
"required": "required"
}
)
pwd = PasswordField(
label="密码",
validators=[
DataRequired("密码不能为空!")
],
description="密码",
render_kw={
"class": "form-control",
"placeholder": "请输入密码!",
"required": "required"
}
)
submit = SubmitField(
'登录',
render_kw={
"class": "btn btn-primary btn-block btn-flat",
}
)
其中validators是进行数据合法性判断,render_kw 顾名思义就是与html相关,其作用就是渲染生成html代码,通过该参数可以加上各种class属性。
我们知道Flask这个框架也是符合MVC的网页设计模式的,M(models)数据库字段的定义我们已经完成了,C(controller)控制器就是处理url相关的业务逻辑,也写完了,现在就是只剩下V(view)视图的编写了,它的作用就是负责显示信息,让用户读取。如果你不熟悉MVC,这里推荐你去学习Python中MVC模式的使用
现在我们打开admin/views.py文件,我们导入刚才定义的LoginForm,并且实例化一个对象,然后传给我们的html即可:
from app.admin.forms import LoginForm
# 登入
@admin.route('/login/')
def login():
form = LoginForm()
return render_template("admin/login.html", form=form)
然后打开login.html页面,进行form字段的页面显示:
将<input name="user" type="text" class="form-control" placeholder="请输入账号!">
修改为 {{ form.account }}即可
![](https://img.haomeiwen.com/i8964398/89ab4fdf5285ea5d.png)
然后密码,提交都是这样:
{{ form.pwd }}
{{ form.submit }}
接着运行一下我们的manage.py文件,发现出了这么一个错误:
KeyError: 'A secret key is required to use CSRF.'
说我们缺少csrf,关于常见的网络攻击,请点击我的另外一个专题里面的:零基础使用Django2.0.1打造在线教育网站(二十五):常见的网络攻防,这里就不细说了。
而关于CSRF的详细,可以点击这里CSRF 保护,我们需要两步操作:第一步:打开我们app/--init--.py文件(注意不是admin/--init--.py),新增一行代码:
app.config["SECRET_KEY"] = "movie_licheetools_top" # 这里的SECRET_KEY可以随意设置,但最好是英文
第二步,所有的form表单都要在submit提交之前添加csrf_token。
{{ form.csrf_token }}
刷新一下,发现错误没有了!
视图的处理
修改views.py里面的登录函数:
# 登入
@admin.route('/login/')
def login():
form = LoginForm()
if form.validate_on_submit():
data = form.data
return render_template("admin/login.html", form=form)
然后是错误信息的提示:
![](https://img.haomeiwen.com/i8964398/64af812eeafca94a.png)
最后是把from提交表单里面的action去掉,只保留method="post"即可!
因为我们默认请求方式是GET,所以需要去登录函数进行配置:
# 登入
@admin.route('/login/', methods=["GET", "POST"])
def login():
form = LoginForm()
if form.validate_on_submit():
data = form.data
return render_template("admin/login.html", form=form)
现在重新运行一下我们的项目,在浏览器地址栏输入:http://127.0.0.1:5000/admin/login/,
![](https://img.haomeiwen.com/i8964398/c184f6822080dccd.png)
什么都不填,点击登录就出现这个提示字段,此处效果是由forms.py中"required": "required" 实现的,如果把这个注释掉就会显示我们自己定义的forms报错信息。
账号和密码的验证
首先我们来定义对账号字段的验证:打开admin/forms.py文件,在底部输入以下代码:
def validate_account(self, field):
account = field.data
admin = Admin.query.filter_by(name=account).count()
if admin == 0:
raise ValidationError("你输入的账号不存在!")
但是对于密码的验证我们需要,进行HASH加密,打开models.py文件,找到管理员函数Admin,我们在函数内部新增几行代码:
def check_pwd(self, pwd):
from werkzeug.security import check_password_hash
return check_password_hash(self.pwd, pwd)
接着我们打开views.py文件,修改Login函数:
from flask import flash, session, request
from app.models import Admin
# 登入
@admin.route('/login/', methods=["GET", "POST"])
def login():
form = LoginForm()
if form.validate_on_submit():
data = form.data
admin = Admin.query.filter_by(name=data["account"]).first()
if not admin.check_pwd(data["pwd"]): # 切记密码错误时,check_pwd返回false,但此时not check_pwd(data["pwd"])为真!
flash("密码错误!")
return redirect(url_for('admin.login'))
session["admin"] = data["account"] # 如果密码正确,就定义session的会话把数据保存到数据库。
return redirect(request.args.get("next") or redirect(url_for("admin.index")))
return render_template("admin/login.html", form=form)
接下来我们需要在前端页面中进行错误信息的flash闪现,打开login.html页面:
![](https://img.haomeiwen.com/i8964398/ef7af055d0dadd75.png)
注销账号
# 登出
@admin.route('/logout/')
def logout():
session.pop("admin, None)
return redirect(url_for('admin.login'))
我们发现退出登陆之后,还可以访问我们的后台,这是不允许的,所以我们这里需要用到装饰器,用于对访问进行控制。关于装饰器,你可以点击查看这里视图装饰器!
打开views.py文件,我们在最前面新增代码:
from functools import wraps
# 登录装饰器
def admin_login_req(f):
@wraps(f)
def decorated_function(*args, **kwargs):
if "admin" not in session:
return redirect(url_for("admin.login", next=request.url))
return f(*args, **kwargs)
return decorated_function
然后在所有函数(登录函数除外)路由下面添加@admin_login_req
,注意顺序不能放错,访问控制装饰器一定要写在路由装饰器之后,否则访问控制将不起作用。!
# 后台首页
@admin.route("/")
@admin_login_req
def index():
return render_template("admin/index.html")
注意:登录函数(Login)路由函数下面是不能添加装饰器的,否则会造成重定向次数过多,无法进行登录的!
但是到目前为止,你还登入不了后台(登入都是密码错误),为什么呢,因为你后台数据库里面是没有数据的,那有人就问了,我在数据库里自己加一个不就可以了么,是的,但是还是不可以登录,因为我们开启了登录验证,那怎么办呢?我提供两种方法!
登入后台系统
方法一:目前先去掉所有的登录验证
我们之前不是在后台每个页面都增加了@admin_login_req,为了后续开发的需要,我们先去掉这个@admin_login_req,等所有功能开发完了之后再加上,这是可以的,但是怕后面会忘记,我这里推荐使用第二种方法!
方法二:自己生成加密密码然后使用
因为我们是采用hash加密,你不懂hash加密没关系,你可以这样想,因为密码可以保存很长时间,而且一旦输入并确认就是惟一的,多了一个空格或者大小写都会报错,那么这样我们可以先定义一个自己后台的账号。例如,账号:admin,密码也是:admin,我们可以自己先对密码admin进行加密,然后保存到数据库里,不就可以登入了么?
我们在movie里面新建一个python文件,里面写入下面2行代码:
from werkzeug.security import generate_password_hash
print(generate_password_hash('admin'))
结果就是这样:
pbkdf2:sha256:50000$TBmI1Hmu$c85b1b204d96f47f3807e54a5cd599cc4e7d83e0f617dd1d86e4b0904670bbca
你自己试试看,是不是也是这个加密密码!现在你就可以把这个加密密码保存到你数据库admin的pwd里面了,然后就可以后台登录了。
问题解答
有小伙伴出现这个问题:
sqlalchemy.exc.InvalidRequestError: Table 'user' is already defined for this MetaData instance. Specify 'extend_existing=True' to redefine options and columns on an existing Table object.
报错说你缺少extend_existing=True
,解决办法是:在所有的models里面添加
__table_args__ = {"useexisting": True}
就是这样:
# 定义会员数据模型
class User(db.Model):
__tablename__ = "user"
__table_args__ = {"useexisting": True}
id = db.Column(db.Integer, primary_key=True) # 编号
......................
至此,本篇关于访问控制器的介绍就到此为止了,感谢你的赏阅!
本篇笔记对应上传的仓库为:https://github.com/licheetools/movie对应第五篇。