python web框架Flask——后台登录

项目搭建

创建一个项目之后,需要在手动创建几个包(含有__init__.py文件的目录)和文件

1、在主目录下创建配置文件:config.py

2、在主目录下创建扩展文件:exts.py

3、在主目录下创建管理文件(供在命令行使用):manage.py

4、在主目录下创建app包,在app包下再创建cms包管理后台的文件

5、在cms包下创建views.py文件管理视图函数、models.py文件管理数据库模型、forms.py文件管理表单验证

填写配置文件

在配置文件下配置密钥、数据库连接等(配置文件的所有变量必须是全部大写)

主目录/config.py
import os

SECRET_KEY = os.urandom(24)

DEBUG = True

SQLALCHEMY_DATABASE_URI = "mysql://你的数据库用户名:用户密码@127.0.0.1:3306/数据库名称"
SQLALCHEMY_TRACK_MODIFICATIONS = False

填写扩展文件

Flask框架封装了数据库的一些操作,将其置为扩展包falsk-sqlalchemy,只需要实例化SQLAlchemy对象

主目录/exts.py
from flask_sqlalchemy import SQLAlchemy


db = SQLAlchemy()

创建应用app

在入口文件定义一个创建app的方法,供蓝图使用,这里说的蓝图就是app包下的包(模块).由于本篇博客实现的是后台登录,即涉及表单验证,所以需要防止csrf注入攻击

主目录/app.py
from flask import Flask
from flask_wtf import CSRFProtect
from apps.cms import bp as cms_bp
from apps.cms import login_manager as cms_login_manager

import config
from exts import db


def create_app():
    """
    主入口文件创建app,供其他蓝图使用
    :return: 返回一个app
    """
    app = Flask(__name__)
    # 防止csrf注入攻击
    CSRFProtect(app)
    # 注册蓝图模块
    app.register_blueprint(cms_bp, url_prefix="/cms")

    # 导入配置文件
    app.config.from_object(config)
    # 数据库db初始化app
    db.init_app(app)
    # 后台登录login_manager初始化app
    cms_login_manager.init_app(app)
    return app


if __name__ == '__main__':
    app = create_app()
    app.run()

初始化蓝图

cms包下有一个初始化py文件,一般是用来供导包的

主目录/app/cms/__init__.py
from .views import bp
from .views import login_manager

创建用户模型

在蓝图中的模型文件中创建用户模型,即在cms包下的models.py文件创建。创建模型已作详细说明

主目录/app/cms/models.py
from exts import db
from datetime import datetime
from werkzeug.security import generate_password_hash, check_password_hash
from flask_login import UserMixin


class CMSUser(db.Model, UserMixin):
    """
    定义一个类名,系统将类转换为表格,表名为cms_user
    定义类的属性就是表格的字段名
    为了安全性,可以将用户表的password属性置为保护属性如    _password
    为了方便用户操作保护属性像操作普通属性一样,需要装饰 _password
    在设置密码的过程中,需要对密码加密——>调用generate_password_hash()
    另外定义一个校验密码的方法check_password()
    在校验密码的过程中,需要对密码解密——>调用check_password_hash()
    """
    __tablename__ = "cms_user"
    id = db.Column(db.Integer, primary_key=True, autoincrement=True)
    username = db.Column(db.String(50), nullable=False)
    _password = db.Column(db.String(100), nullable=False)
    email = db.Column(db.String(50), nullable=False, unique=True)
    join_time = db.Column(db.DateTime, default=datetime.now)

    def __init__(self, username, password, email):
        self.username = username
        self.password = password
        self.email = email

    @property
    def password(self):
        return self._password

    @password.setter
    def password(self, raw_password):
        """
        设置密码
        :param raw_password: 前端后台传入的密码参数
        :return:没有返回值
        """
        self._password = generate_password_hash(raw_password)

    def check_password(self, raw_password):
        """
        校验密码,将数据库的存入的密码解密之后再与传入的密码参数匹配
        :param raw_password: 前端后台传入的密码参数
        :return: True or False
        """
        result = check_password_hash(self.password, raw_password)
        return result

迁移并同步到数据库中

Flask框架不像Django框架可以直接使用命令行生成迁移文件并同步到数据库当中,Flask框架需要借助flask_script包下的管理类Manager、flask_migrate包下的迁移类Migrate和一些相关的迁移命令类MigrateCommand。

1、先获取app对象

2、再实例化一个管理对象manage,绑定到app上

3、将app应用与db数据库对象绑定到迁移类中

4、将迁移文件中用到的命令封装成 db (这个db不是db数据库对象),调用add_command()添加到manage中

5、完成上面几个文件的代码填充,可以在命令行中输入相关命令,这里不做解释,代码中有提示

主目录/manage.py
from flask_script import Manager
from flask_migrate import Migrate, MigrateCommand
from app import create_app
from exts import db
from apps.cms import models as cms_models

# 获取模型中定义的类
CMSUser = cms_models.CMSUser

app = create_app()
# 定义Manager类实例化对象
manage = Manager(app)

# 将app应用与db数据库绑定到迁移文件中
Migrate(app, db)
# 将迁移文件用到的命令换成db开头(manage.py文件可以自定义命令)添加到manage中
manage.add_command("db", MigrateCommand)


@manage.option("-u", "--username", dest="username")
@manage.option("-p", "--password", dest="password")
@manage.option("-e", "--email", dest="email")
def create_cms_user(username, password, email):
    """
    自定义创建后台用户的命令,在cmd命令行中输入类似于如下命令,即可添加用户:(在虚拟环境下)
        python manage.py db create_cms_user -u 用户名 -p 密码 -e 邮箱地址
    :param username:
    :param password:
    :param email:
    :return: 返回一提示消息
    """
    user = CMSUser(username=username, password=password, email=email)
    db.session.add(user)
    db.session.commit()
    print("cms用户添加成功")


if __name__ == "__main__":
    manage.run()

表单验证

对前端后台登录的表单提供的数据进行验证,需要借助flask_wtf包下的FlaskForm类

主目录/app/cms/forms.py
from flask_wtf import FlaskForm
from wtforms import StringField, IntegerField
from wtforms.validators import DataRequired, Length, Email


class LoginForm(FlaskForm):
    email = StringField(validators=[DataRequired(message="请输入邮箱地址"), Email(message="请输入正确的邮箱格式")])
    password = StringField(validators=[Length(6, 20, message="请输入正确格式的密码")])
    remember = IntegerField()

渲染页面

这里没有设置页面,就不渲染页面,直接返回一个字符串标识一下即可。渲染页面做的事情一般是在views.py中实现的,这个views.py文件采用了两种方式:视图函数和基于调度方法的类视图。

这里有必要提一下:对于没有登录的行为,是不能访问主页(这里是指登录成功之后跳转的页面)。实现这一要求,我们需要从flask_login包下导入login_user、logout_user、login_required、LoginManager

1、和上面一样,实例化LoginManager对象login_manager也要初始化app(在入口文件app.py会有一行代码)

2、对于没有登录的用户我们需要给一个重定向(我们把它作为重定向)。这个实例化对象有一个属性login_view,给其赋值(值是一个url)

3、最重要的一点:调用了login_user()会自动生成一个session值,调用了logout_user()会自动删除这一个session值。这个session值是通过装饰器@login_manager.user_loader修饰的函数生成

4、视图函数和类视图已在代码中作详细说明

主目录/app/cms/views.py
from flask import Blueprint, views, render_template, request, session, redirect, url_for
from flask_login import login_required, LoginManager
from flask_login import login_user
from .forms import LoginForm
from .models import CMSUser

# 指定没有登录时重定向的页面,需要将LoginManager的实例化对象初始化app(在入口文件中)
login_manager = LoginManager()
login_manager.login_view = "cms.login"
# 定义蓝图对象
bp = Blueprint("cms", __name__)


@bp.route("/")
@login_required
def index():
    return "cms index"


class LoginView(views.MethodView):
    """
    get方法对应前端后台的get请求,主要是渲染页面
    post方法对应前端后台的post请求:
        1、先验证用户的输入是否符合表单验证的要求,如果不符合要求,则返回具体的错误
        2、先通过邮箱查找用户并通过查找的用户密码与后台表格密码进行验证,如果没有找到,返回邮箱或者密码错误
        3、匹配成功则通过login_user(user)自动保存session值
        4、通过获取前端后台的remember值给session定义过期时间,默认是浏览器会话关闭
    """
    def get(self, message=None):
        return render_template("cms/cms_login.html", message=message)

    def post(self):
        form = LoginForm(request.form)
        if form.validate():
            email = form.email.data
            password = form.password.data
            remember = form.remember.data
            user = CMSUser.query.filter(CMSUser.email == email).first()
            if user and user.check_password(password):
                # 自动生成一个session值
                login_user(user)

                if remember:
                    # 设置session的过期时间,默认为31天
                    session.permanent = True
                    return redirect(url_for("cms.index"))
                return redirect(url_for("cms.index"))
            else:
                return self.get(message="邮箱或者密码错误")
        else:
            message = form.errors.popitem()[1][0]
            return self.get(message=message)


bp.add_url_rule("/login", view_func=LoginView.as_view("login"))


@login_manager.user_loader
def load_user(user_id):
    """
    后台用户类必须继承UserMixin,以防用户表没有定义校验的字段,如:is_active等
    :param user_id:
    :return:
    """
    return CMSUser.query.get(user_id)

前台用当前登录的用户的相关信息的几种方式:

1、可以在模板上(页面)使用当前登陆用户的代理current_user(具体值就加“点属性”)

2、在views.py文件中将登录的用户信息保存到 g 对象中(这个 g 对象很重要,而且使用很方便)

这样一个后台登录实现已经实现了,能帮助到大家,点个赞,加个关注,诚挚感谢!