后台业务逻辑
管理员登录
app/__init__.py 中创建 db 对象
app/models.py 中导入 db 对象
app/admin/forms.py 定义表单验证
app/templates/admin/login.html
中使用表单字段, 信息验证, 消息闪现
app/admin/views.py 中处理登录请求, 保存会话
app/admin/views.py 定义登录装饰器, 访问控制
模型: Admin
表单: LoginForm
请求方法: GET POST
访问控制: 无
对于代码进行调整, 将原本 models 中涉及 app 的代码, 全部转移到 init.py 中
models 中只保留 datetime 的导入, init 中重复的包的引入删除掉
将 models 中的 Create_all 先注释掉
开始定义表单
- pip install flask-wtf
- 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 因为 forms 会为我们直接生成 html 代码,
通过该参数加上各种 class 等
如何将表单传递到我们的前端 html 中呢, 这里需要用到 views, 在 return 的时候附加参数
views.py 中的 login 方法
- from flask import render_template, redirect, url_for
- from app.admin.forms import LoginForm
- @admin.route("/login/")
- def login():
- """后台登录"""
- form = LoginForm()
- return render_template("admin/login.html", form=form)
去模板中进行展示: 前往 login.html 修改
mark
mark
mark
- builtins.KeyError
- KeyError: 'A secret key is required to use CSRF.'
所有的 form 表单的提交 submit 之前都要添加 csrf
只添加这个还不行, 还需要添加一个 secret key
app.config['SECRET_KEY'] = 'mtianyan_movie'
mark
进行视图的处理
- if form.validate_on_submit():
- data = form.data
将错误信息显示到模板中
mark
将提交时的 action 去掉, 只需要通过 method=post 进行提交
@admin.route("/login/", methods=["GET", "POST"])
为 login 的路由添加 get post 方法
此时前往 / admin/login / 访问
mark
点击登录, 此处效果为 forms.py 中 "required": "required" 实现
我们将其注释掉可以显示出来我们自己的 forms 的报错信息
mark
验证账号和密码
在 forms 中自定义一个对于账号的验证
- def validate_account(self, field):
- account = field.data
- admin = Admin.query.filter_by(name=account).count()
- if admin == 0:
- raise ValidationError("账号不存在!")
注意此处自定义验证方法的命名规范 validate_+ 字段名
可能的报错:
ImportError: cannot import name db
mark
注意导入的顺序问题
原因有点复杂, 有人可以解释清楚还请在评论中跟我也说说
进行密码的验证:
前往模型的 admin 类中定义方法;
- def check_pwd(self, pwd):
- from werkzeug.security import check_password_hash
- return check_password_hash(self.pwd, pwd)
views 中账号密码的处理
- def login():
- """后台登录"""
- form = LoginForm()
- if form.validate_on_submit():
- data = form.data
- admin = Admin.query.filter_by(name=data["account"]).first()
- # 密码错误时, check_pwd 返回 false, 则此时 not check_pwd(data["pwd"]) 为真
- if not admin.check_pwd(data["pwd"]):
- flash("密码错误!")
- return redirect(url_for("admin.login"))
- # 如果是正确的, 就要定义 session 的会话进行保存
- session["admin"] = data["account"]
- return redirect(request.args.get("next") or url_for("admin.index"))
- return render_template("admin/login.html", form=form)
在前端网页中显示错误信息的 flash 闪现
mark
报错:
sqlalchemy.exc.InvalidRequestError: Table 'comment' is already defined for this MetaData instance. Specify 'extend_existing=True' to redefine options and columns on an existing Table object.
解决方案: 为所有的 models 添加
- __table_args__ = {"useexisting": True}
- sqlalchemy.exc.InvalidRequestError
- sqlalchemy.exc.InvalidRequestError: Multiple classes found for path "Userlog" in the registry of this declarative base. Please use a fully module-qualified path.
这是因为我们在引入 models 中的模型时没有采用全量路径, 而是采用了很短的路径
解决方案 1:
- from models import Admin
- # 改为
- from app.models import Admin
方法 2 在 django 中遇到这种问题, 一般可以采用将 app 路径加入根目录当中, 但是在 flask 中尝试失败:
- import sys
- import os
- # BASE_DIR = os.path.dirname(os.path.abspath(__file__))
- # sys.path.insert(0, os.path.join(BASE_DIR, 'movie_project\\app'))
- # sys.path.insert(0, os.path.join(BASE_DIR, 'movie_project\\app\\admin'))
- # sys.path.insert(0, os.path.join(BASE_DIR, 'movie_project\\app\\home'))
在没有登录的状态下也能点击登录后才可以点击的操作, 因为我们没有进行一个访问的控制
注销时的处理
- @admin.route("/logout/")
- def logout():
- """后台注销登录"""
- session.pop("admin", None)
- return redirect(url_for("admin.login"))
装饰器的访问控制
- 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.route("/pwd/")
- @admin_login_req
访问控制装饰器一定要写在路由装饰器之后, 否则会导致没有效果
来源: http://www.jianshu.com/p/60f94da8040e