根据Youtube上视频学习

Flask 框架学习

通过 Pycharm 创建新的工程,创建 Falsk 项目

生成的 Falsk 项目中带有一个 app.py 文件




1
2
3
4
5
6
7
8
9
10
11
12
13
14
from flask import Flask

app = Flask(__name__)

@app.route('/') # 指定路由: '/' 根路由
def hello_world():
return 'Hello World!'

if __name__ == '__main__':
# debug 默认为 False, debug=True 让开发变得友好,在修改App 文件后,刷新立即可以在本地服务上看到变化
# windows 可能做不到
# port 默认端口 5000 ,port=6333 更改端口
# host 默认为本地 127.0.0.1, host='0.0.0.0'面向局域网都可以访问
app.run(debug=True, port=6333, host='0.0.0.0')

进行简单修改,对页面进行布局,渲染

  1. 基础:在 return 内增加 HTML 类型文件

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    from flask import Flask

    app = Flask(__name__)

    @app.route('/') # 指定路由
    def hello_world():
    # 更改 return 内为 HTML 文件
    return '<h1>Hello</h1><p>Flask</p>'

    if __name__ == '__main__':
    app.run(debug=True)
  2. 优化: 在工程下建立文件夹 “templates” (现在版本已经实现自建),”templates” 中建立 “index.html” 文件
    在 app.py 内引进 Flask_note1 渲染

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    from flask import Flask, render_template
    app = Flask(__name__)

    @app.route('/') # 指定路由
    def hello_world():
    # title=title 将 title 传入 index 作为标题
    # <title>{{ title }}</title>
    # 写在 <p>{{ title }}</p> 内,也可以调用 填入 'Flask Web test'
    title = 'Flask Web test'
    return render_template('index.html', title=title)

    if __name__ == '__main__':
    app.run(debug=True)
  3. 更高级渲染 (条件判断、循环)

    • 条件判断:
      如果 title 为空则默认 标题为 ‘Falsk App’

      1
      2
      3
      4
      5
      {% if title %}
      <title>{{ title }}</title>
      {% else %}
      <title>Falsk App</title>
      {% endif %}
    
1
2
3
def hello_world():
return render_template('index.html',
)
+ 循环 建立三个 <p></p> ![](https://cdn.jsdelivr.net/gh/Forgotten-Forever/BlogImages/images/Flask_p.png)
1
2
3
{% for p in data %}
<p>{{ p }}</p>
{% endfor %}
1
2
3
4
5
6
7
8
9
10
def hello_world():
title = 'Flask Web test'
paragraphs = [
"Selection 1",
"Selection 2",
"Selection 3"
]
return render_template('index.html',
title=title,
data=paragraphs)

模板继承和引用

  1. 模板的继承

    • 在 “templates” 下建立 “base.html” 作为模板

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      <!DOCTYPE html>
      <html lang="en">
      <head>
      <meta charset="UTF-8">
      {% if title %}
      <title>{{ title }}</title>
      {% else %}
      <title>Falsk App</title>
      {% endif %}
      </head>
      <body>
      <h3><a href="/">Flask App</a> </h3>
      <hr>
      </body>
      </html>
    • 在 “index.html” 中继承 “base.html” 模板

      1
      {% extends 'base.html' %}
    • 在 “index.html” 继承模板后,写入属于自己的东西
      在 “base.html” 文件相应位置写下 block 定义此自定义模块名称为 content

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      <!DOCTYPE html>
      <html lang="en">
      <head>
      <meta charset="UTF-8">
      {% if title %}
      <title>{{ title }}</title>
      {% else %}
      <title>Falsk App</title>
      {% endif %}
      </head>
      <body>
      <h3><a href="/">Flask App</a> </h3>
      <hr>
      {% block content %}
      <p>Test</p>
      {% endblock %}
      </body>
      </html>
    • 在 “index.html” 文件中 引用并更改

      1
      2
      3
      {% block content%}
      <p>{{ title }}</p>
      {% endblock %}
  2. 模板的引用 (导入,对于会在很多地方用到的文件引用)
    模板模块中定义的会被,引用模板的模块重写掉,如果没有引用则会显示模板模块内的内容,常规情况下默认为空
    可以把每一个模块(导航栏,报头)新建一个 html 文件 (一个个小组件) 并引入继承到模块
    “navbar.html” 自定义报头

    1
    2
    <h3><a href="/">Flask App</a> </h3>
    <hr>

    “base.html” 引入

    1
    {% include 'navbar.html' %}

了解 flask-bootstrap (框架中已经定义好许多 class 的命名、风格) 使界面布局更加好看,将布局封装到Flask的插件

可以通过定义 class 的值定义组件位于 页面中的位置
Flask—bootstrap 重点学习官网

1
2
3
4
5
6
pip install flask-bootstrap
from flask import Flask, render_template
from flask_bootstrap import Bootstrap

app = Flask(__name__)
bootstrap = Bootstrap(app)

  1. HTML 中引用 bootstrap 从 bootstrap 的 base.html 中的库内引用

    Boot-straap 官方提供的模板

    1
    2
    3
    4
    5
    6
    7
    8
    9
    {% extends "bootstrap/base.html" %}
    {% block title %}This is an example page{% endblock %}
    {% block navbar %}
    {# {% include 'navbar.html' %}#}
    {% endblock %}

    {% block content %}
    <h1>Hello, Bootstrap</h1>
    {% endblock %}
  2. 重写 bootstrap 里面的类,使之满足自己的需求
    “External Libraries -> site-packages -> flask_bootstrap -> templates -> bootstrap” 内修改内置的各种页面,或者,复制出来在自己的页面中引用修改

  3. 创建新的页面 (以点击形式进入新的连接)

    • app.py
      1
      2
      3
      4
      @app.route('/register')
      def register():
      # 转到 regist.html 页面
      return render_template('register.html')
    • register.html
      引用 “base.html” 模板
      1
      2
      3
      4
      5
      {% extends 'base.html' %}

      {% block app_content %}
      <h1>Register Now</h1>
      {% endblock %}
    • base.html
      引用 navbar.html 页面
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      {% block navbar %}
      {% include 'navbar.html' %}
      {% endblock %}

      {% block content %}
      <div class="container">
      {% block app_content %}
      {% endblock %}
      </div>
      {% endblock %}
    • navbar.html.html
      设置页面共有文件
      1
      2
      3
      4
      5
      6
      7
      <ul class="nav navbar-nav navbar-right">
      {# 只有当 request.endpoint 是 register 时 才是 active,否则是其他 #}
      <li class="{% if request.endpoint == 'register' %}active{% endif %}">
      {# 当点击 Regist 时 ,转到 register() 函数 ,由 render_template(register.html) 转到 register.html #}
      <a href="{{ url_for('register') }}">Register</a>
      </li>
      </ul>

Flask 连接数据库、 Flask mine 找回密码、Flask 登录 (采用关系数据库,Flask提供flask-sqlalchemy连接数据库)

flask_sqlalchemy 重点学习官方文档地址

1
2
3
4
5
6
7
8
9
from flask import Flask, render_template
from flask_bootstrap import Bootstrap
from flask_sqlalchemy import SQLAlchemy

app = Flask(__name__)
# 给出的是本地的 SQLite 数据库链接地址,可以改为 MySQL 等,只需要改变app.config['SQLALCHEMY_DATABASE_URI'] 值
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:////tmp/test.db'
db = SQLAlchemy(app)
bootstrap = Bootstrap(app)
  1. 为了方便管理,建议将 SQL 配置新建一个 config.py 用于整理配置文件
    1
    2
    3
    4
    5
    6
    7
    8
    9
    import os

    basedir = os.path.abspath(os.path.dirname(__file__))


    class Config(object):
    # 如果找到 SQLite 路径就是用,如果没有找到就新建一个
    SQLALCHEMY_DATABASE_URI = os.environ.get('DATABASE_URL') or "sqlite:///" + os.path.join(basedir, 'app.db')
    SQLALCHEMY_TRACK_MODIFICATIONS = False
  2. 建立 models.py 存放构建数据库的结构
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    from app import db


    class User(db.Model):
    # nullable 非空 ; unique 不能重复,出现相同就报错
    id = db.Column(db.Integer, primary_key=True)
    username = db.Column(db.String(20), unique=True, nullable=False)
    password = db.Column(db.String(20), nullable=False)
    email = db.Column(db.String(120), unique=True, nullable=False)

    def __repr__(self):
    return '<User %r>' % self.username
  3. 要创建初始数据库,需要在 “Python Console” (3、4、5都在 Python Console 中执行) 导入对象并运行 “SQLAlchemy.create_all” 方法
    1
    2
    from app.models import db
    db.create_all()
  4. 创建用户用于测试
    1
    2
    3
    4
    5
    6
    from app.models import User
    from app.models import db
    user1 = User(username="", password="", email="")
    # 添加进数据库,并运行指令
    db.session.add(user1)
    db.session.commit()
  5. 访问数据库中数据
    1
    2
    3
    4
    5
    6
    from app.models import User
    u = User.query.all()
    # [<User 'Jack'>]
    U = u[0]
    U.password
    # 'pwd'

FlaskWTF 创建 Flask 中的表单,基本包含所有表单

1
2
3
下载:
1. pip install flask-WTF
2. pip install wtforms

FlaskWTF 重点官网主页
Flask Bootstrap 与 其他组件联系内容包括 WTF

  1. 创建表单 forms.py (用于注册,规定注册页面的内容)
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    # FlaskForm 主要负责整合 wtforms 的内容、类型 以及包装
    from flask_wtf import FlaskForm
    # 定义数据类型字符串,密码,提交按钮
    from wtforms import StringField, PasswordField, SubmitField
    # validators 验证者:需要的数据、数据范围Length(min=6, max=20)、Email、验证密码 EqualTo('password')
    from wtforms.validators import DataRequired, Length, Email, EqualTo


    class RegisterForm(FlaskForm):
    username = StringField('Username', validators=[DataRequired(), Length(min=6, max=20)])
    email = StringField('Email', validators=[DataRequired(), Email()])
    password = PasswordField('Password', validators=[DataRequired(), Length(min=8, max=20)])
    confirm = PasswordField('Repeat Password', validators=[DataRequired(), EqualTo('password')])
    submit = SubmitField('Register')
  2. 使用 Email类型 时需要下载支持
    1
    pip install email_validator
    使用 FlaskWTF 需要秘钥设置, CSRF token 验证 每次随机生成

  • 在 config.py 内设置
    1
    2
       # SECRET_KEY 秘钥为路径内或者 字符串 'A-VERY-LONG-SECRET' 可以随意设置,主要为了防止跨网站攻击
    SECRET_KEY = os.environ.get('SECRET_KEY') or 'A-VERY-LONG-SECRET'
  • 在 app.py 主页设置
    1
    app.secret_key = '123456'
    1. 定义 app.py 内注册页面
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      from flask import Flask, render_template, request, url_for
      from flask_bootstrap import Bootstrap
      from flask_sqlalchemy import SQLAlchemy

      from config import Config
      # 引用 form 内的 注册表单 RegisterForm
      from app.forms import RegisterForm

      app = Flask(__name__)
      db = SQLAlchemy(app)
      bootstrap = Bootstrap(app)

      app.config.from_object(Config)
      # 设置 传递数据的方法为 "GET" 与 "POST"
      @app.route('/register', methods=['GET', 'POST'])
      def register():
      # 调用 RegisterForm 表单定义
      form = RegisterForm()
      # 定义如果点击后 pass
      if form.validate_on_submit():
      pass
      # 将表单传入 register,html
      return render_template('register.html', form=form)
    2. 定义 “register.html” : 引用 bootstrap 内的 wtf.html 内容
      WTF 内部包含 表单提交的各种报错,验证等
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      {% extends 'base.html' %}

      {% block app_content %}
      <h1>Register Now</h1>
      <br>
      <div class="row">
      {# col-md-6 表单大小 #}
      <div class="col-md-6">
      {# 引用 bootstrap 内的 wtf.html 内容 #}
      {% import 'bootstrap/wtf.html' as wtf %}
      {# 重点 一句话获取表单 form.py 的设置,快速完成表单,而无需进行大量的微调 #}
      {{ wtf.quick_form(form) }}
      </div>
      </div>

      {% endblock %}
    3. 额外: 人机验证 RECAPTCHA PUBLIC KEY 验证秘钥,验证是否为机器
    • forms.py 添加验证
      1
      2
      from flask_wtf import FlaskForm, RecaptchaField
      recaptcha = RecaptchaField()
    • config.py 注册秘钥
      1
      2
      3
         # RECAPTCHA PUBLIC KEY 验证秘钥,验证是否为机器人
      RECAPTCHA_PUBLIC_KEY = os.environ.get('RECAPTCHA_PUBLIC_KEY') or 'A-VERY-LONG-PUBLIC-KEY'
      RECAPTCHA_PRIVATE_KEY = os.environ.get('RECAPTCHA_PRIVATE_KEY') or 'A-VERY-LONG-PRIVATE_KEY'
    • 遇到报错 “需要网站所有者处理的错误:网站密钥无效”,需要自行注册页面

Flask-Bcrypt 加密数据项 ,在服务端处理表单存入数据库

1
pip install flask-bcrypt
  1. 加密数据项 “flask-bcrypy” 中的 “Bcrypy” 模块, 传入数据库

    为了避免与 congig.py 内的引用构成 bug 新建 名为”app” 的 package 将除了数据库、config.py移入, 将 app.py 分为 init.py 与 route.py,在外部创建与 config.py 同级的 run.py

    • __init__.py
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      from flask import Flask
      from flask_bootstrap import Bootstrap
      from flask_sqlalchemy import SQLAlchemy
      # 调用模块
      from flask_bcrypt import Bcrypt

      from config import Config


      app = Flask(__name__)
      db = SQLAlchemy(app)
      # 定义模块
      bcrypt = Bcrypt(app)
      bootstrap = Bootstrap(app)

      app.config.from_object(Config)
      from app.route import *
    • route.py
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      from flask import render_template, flash

      from app import app, bcrypt, db
      from app.forms import RegisterForm
      from app.models import User


      @app.route('/') # 指定路由
      def index():
      return render_template('index.html')


      @app.route('/register', methods=['GET', 'POST'])
      def register():
      form = RegisterForm()
      # 获取提交上来的注册数据,进行处理
      if form.validate_on_submit():
      username = form.username.data
      email = form.email.data
      # 变种hash加密,相同密码生成值也不同
      password = bcrypt.generate_password_hash(form.password.data)
      # 传入数据库内
      user = User(username=username, email=email, password=password)
      db.session.add(user)
      db.session.commit()
      # 检查产生密码与hash 是否对应正确
      # bcrypt.check_password_hash(hash, password)
      return render_template('register.html', form=form)
    • run.py
      1
      2
      3
      4
      from app import app

      if __name__ == '__main__':
      app.run(debug=True)
  2. 在设置 “models.py” 数据库时,设置 “unique=True 不能重复”,遇见重复的就会报错,需要写函数来辅助判断用户名和密码是否在数据库中,然后给与提示

    • forms.py 判断是否在数据库中重复,如果在给与报错

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      from wtforms.validators import DataRequired, Length, Email, EqualTo, ValidationError
      class RegisterForm(FlaskForm):
      def validate_username(self, username):
      user = User.query.filter_by(username=username.data).first()
      if user:
      raise ValidationError('Username already token, please choose another one.')

      def validate_email(self, email):
      email = User.query.filter_by(email=email.data).first()
      if email:
      raise ValidationError('Email already token, please choose another one.')
    • route.py (flash提示注册成功,并跳转页面)
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      from flask import render_template, flash, redirect, url_for

      from app import app, bcrypt, db
      from app.forms import RegisterForm
      from app.models import User


      @app.route('/') # 指定路由
      def index():
      return render_template('index.html')


      @app.route('/register', methods=['GET', 'POST'])
      def register():
      form = RegisterForm()
      # 获取提交上来的注册数据,进行处理
      if form.validate_on_submit():
      username = form.username.data
      email = form.email.data
      # 变种hash加密,相同密码生成值也不同
      password = bcrypt.generate_password_hash(form.password.data)
      user = User(username=username, email=email, password=password)
      db.session.add(user)
      db.session.commit()
      # flash 提示 "用户注册成功",提示信息为 "success" 样式
      flash('Congrats registration success', category='success')
      # 提示后转至 index.html 页面
      return redirect(url_for('index'))
      # 检查产生密码与hash 是否对应正确
      # bcrypt.check_password_hash(hash, password)
      return render_template('register.html', form=form)
    • base.html (修改 base.html 页面,确保 flash 成功显示在页面上)

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      {% block content %}
      <div class="container">
      {# 提示框 #}
      <div class="row">
      {# 页面大小 #}
      <div class="col-lg-6">
      {# 判断与循环 #}
      {% with messages = get_flashed_messages(with_categories=True) %}
      {% if messages %}
      {% for category, message in messages %}
      <div class="alert alert-{{ category }}">
      {{ message }}
      </div>
      {% endfor %}
      {% endif %}
      {% endwith %}

      </div>
      </div>
      {# 在 app_content 注册 上添加 注册成功 提示 #}
      {% block app_content %}
      {% endblock %}
      </div>
      {% endblock %}

Flask-login 用户登录包,包含很多用于登录、登出的函数

pip install flask-login

  1. 在 __init__.py 内引入 flask-login 包,并进行设置

    1
    2
    3
    4
    5
    6
    7
    from flask_login import LoginManager

    # 登录界面的位置
    login.login_view = 'login'
    # 提示 "You must login to access the page" 提示框格式为 "info"
    login.login_message = "You must login to access the page"
    login.login_message_category = "info"
  2. 新建 login.html 登录页面,在 forms.py 下 新建 Login 函数,构造 登录需要的部件

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    {% extends 'base.html' %}

    {% block app_content %}
    <h1>Login In</h1>
    <br>
    <div class="row">
    <div class="col-md-6">
    {% import 'bootstrap/wtf.html' as wtf %}
    {{ wtf.quick_form(form) }}
    </div>
    </div>

    {% endblock %}
    1
    2
    3
    4
    5
    6
    from wtforms import StringField, PasswordField, SubmitField, BooleanField
    class LoginForm(FlaskForm):
    username = StringField('Username', validators=[DataRequired(), Length(min=6, max=20)])
    password = PasswordField('Password', validators=[DataRequired(), Length(min=8, max=20)])
    remember = BooleanField('Remember')
    submit = SubmitField('Sign In')
  3. 修改 navbar.html 在主页面新增 “Login” 、”Logout” 选项框,

    增加判断项,在登录后只显示 “Logout” 选项,未登录时显示 “Login” 与 “Register” 选项,

    修改 主页面 (index.html) 显示为 Hello,


    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    <ul class="nav navbar-nav navbar-right">
    {# 判断是否处于登录状态,current_user.is_authenticated 获取登录状态 #}
    {% if not current_user.is_authenticated %}
    {# 登录界面 #}
    <li class="{% if request.endpoint == 'login' %}active{% endif %}">
    <a href="{{ url_for('login') }}">Login In</a>
    </li>
    {# 只有当 request.endpoint 是 index 时 才是 active,否则是其他 #}
    <li class="{% if request.endpoint == 'register' %}active{% endif %}">
    <a href="{{ url_for('register') }}">Register</a>
    </li>
    {% else %}
    {# 登出界面 #}
    <li class="{% if request.endpoint == 'logout' %}active{% endif %}">
    <a href="{{ url_for('logout') }}">Logout</a>
    </li>
    {% endif %}
    </ul>
    1
    2
    3
    4
    5
    {% extends "base.html" %}

    {% block app_content %}
    <h1>Hello, {{ current_user.username }}</h1>
    {% endblock %}
  4. 修改数据库设置 model.py,删除数据库重新构建数据库结构

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    # 引用 UserMinxin
    from flask_login import UserMixin
    # 从 app 包的 __init__.py 引用 login
    from app import db, login


    @login.user_loader
    # 获取登录用户的 id
    def load_user(user_id):
    return User.query.filter_by(id=user_id).first()


    class User(db.Model, UserMixin):
    # nullable 非空 ; unique 不能重复
    id = db.Column(db.Integer, primary_key=True)
    username = db.Column(db.String(20), unique=True, nullable=False)
    password = db.Column(db.String(20), nullable=False)
    email = db.Column(db.String(120), unique=True, nullable=False)


    def __repr__(self):
    return '<User %r>' % self.username

    重构数据库:

    在 "Python Console" 内重置数据库
    1
    2
    from app.models import db
    db.create_all()
  5. 设置 route.py 为登录方法进行实现

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    from flask import render_template, flash, redirect, url_for, request
    # 从登录模块引入所需的方法
    from flask_login import login_user, login_required, current_user, logout_user
    from app import app, bcrypt, db
    # 从 form.py 内引入 注册表格与登录表格
    from app.forms import RegisterForm, LoginForm
    from app.models import User


    @app.route('/') # 指定路由
    # 需要登录
    @login_required
    def index():
    return render_template('index.html')


    @app.route('/register', methods=['GET', 'POST'])
    def register():
    # 判断是否是已经处于登录状态,如果是回到主界面
    if current_user.is_authenticated:
    return redirect(url_for('index'))
    form = RegisterForm()
    # 获取提交上来的注册数据,进行处理
    if form.validate_on_submit():
    username = form.username.data
    email = form.email.data
    # 变种hash加密,相同密码生成值也不同
    password = bcrypt.generate_password_hash(form.password.data)
    user = User(username=username, email=email, password=password)
    db.session.add(user)
    db.session.commit()
    flash('Congrats registration success', category='success')
    return redirect(url_for('index'))
    # 检查产生密码与hash 是否对应正确
    # bcrypt.check_password_hash(hash, password)
    return render_template('register.html', form=form)

    # 设置登录函数、登录页面、登录方法
    @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():
    username = form.username.data
    # 获取密码,检查密码是否与数据库中匹配
    password = form.password.data
    remember = form.remember.data
    # 根据输入用户名找到数据库中用户信息
    user = User.query.filter_by(username=username).first()
    # 如果用户存在且密码对应正确
    if user and bcrypt.check_password_hash(user.password, password):
    # 设置是否记住登录信息单选框
    login_user(user, remember=remember)
    # 返回登录成功信息
    flash("login success", category='info')
    # http://127.0.0.1:5000/login?next=%2F 由 next 决定接下来进入的页面
    if request.args.get('next'):
    next_page = request.args.get('next')
    return redirect(next_page)
    return redirect(url_for('index'))
    # 如果用户不岑在或者密码错误,flash 出错误
    flash("User not exists or password not match", category='danger')
    return render_template('login.html', form=form)

    # 设置登出函数、页面
    @app.route('/logout')
    def logout():
    # 直接引用 flask_login 模块中的 logout_user()
    logout_user()
    # 登出后返回登录页面
    return redirect(url_for('login'))