展开

文章目录

修改历史

历史修改版本

  1. 2021-01-26 12:21:49
  2. 2021-01-26 10:29:40
  3. 2021-01-26 09:55:26
  4. 2020-12-28 23:51:16

使用Flask搭建一个校园论坛3-登录注册

2020-12-28 23:50:49 系列教程 314

简介

在上一节中,主要介绍了项目入口怎么实现,同时基于jinja2提供的模板继承功能完成了基模板的编写,同时使用flask的错误处理装饰器完成了继承自基模板的错误页面自定义。在本章节中,将介绍用户注册、登录等功能模块的实现。

1.知识预览

在本章节中,将学习到以下内容:

  1. flask-sqlachemy 数据库orm的使用
  2. click 注册命令,初始化项目基础数据

2.轮子

Flask本身的定义是一个微框架,何为其义呢?“微”并不代表整个应用只能塞在一个 Python 文件内, 当然塞在单一文件内也没有问题。 “微”也不代表 Flask 功能不强。 微框架中的“微”字表示 Flask 的目标是保持核心简单而又可扩展。 Flask 不会替你做出许多决定,比如选用何种数据库。 类似的决定,如使用何种模板引擎,是非常容易改变的。 Flask 可以变成你任何想要的东西,一切恰到好处,由你做主。因此,可以在开源的世界中找到非常多的flask扩展,本项目中使用的orm框架是flask-sqlachemy,基于sqlachemy开发的flask扩展。

在bbs目录下新建extensions.py模块,这个模块主要是用来存放我们第三方拓展使用。

bbs/extensions.py

from flask_sqlalchemy import SQLAlchemy
db = SQLAlchemy()

3.项目配置

项目可以有开发环境、测试环境、生产环境等,不同环境所使用的配置肯定也是不同的,同时项目也有一些配置是通用的。在bbs目录下面新建setting.py模块,该模块用来存储项目所有配置参数。

在第三小节中,我们使用了flask-salachemy这个框架,使用这个框架之前,有一些默认参数需要我们进行配置,我们将下面的代码写入到setting.py文件中去。

bbs/setting.py

import os

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


class BaseConfig(object):
    SQLALCHEMY_TRACK_MODIFICATIONS = False

    DATABASE_USER = os.getenv('DATABASE_USER')
    DATABASE_PWD = os.getenv('DATABASE_PWD')
    DATABASE_HOST = os.getenv('DATABASE_HOST')
    DATABASE_PORT = os.getenv('DATABASE_PORT')


class DevelopmentConfig(BaseConfig):
    SQLALCHEMY_DATABASE_URI = 'mysql+pymysql://{}:{}@{}/bbs?charset=utf8mb4'.format(BaseConfig.DATABASE_USER,
                                                                                    BaseConfig.DATABASE_PWD,
                                                                                    BaseConfig.DATABASE_HOST)
    # REDIS_URL = "redis://localhost"
    REDIS_URL = "redis://localhost:6379"

首先通过内置的os模块获取了项目的根目录,同时新建了一个BaseConfig类,在该类中定义了一些参数(配置参数名称一般使用大写命名),由于数据库连接的参数都属于是比较高危的数据,因此将其保存在环境变量中,通过os.getenv()去获取这些参数的具体数值。

DevelopmentConfig类继承于BaseConfig类,该类是在开发环境中使用的。在该类中定义了SQLALCHEMY_DATABASE_URI参数,flask-sqlachemy在初始化时,会自动根据该参数去连接数据库。

数据库连接参数都保存在环境变量中,有以下两种方式将其保存至环境变量:

  • 使用export命令(Linux)
export DATABASE_USER=root
export DATABASE_PWD=123456
export DATABASE_HOST=127.0.0.1
export DATABASE_PORT=3306

使用该方式的弊端就是我们每次都需要手动去执行这些命令。

  • .env文件
    可以将这些参数保存到.env文件中,然后通过python-dotenv库自动加载.env文件中的内容到环境变量。在university-bbs目录下新建.env文件,并嵌入下面的代码。
DATABASE_USER=weijiang
DATABASE_PWD=1994124
DATABASE_HOST=127.0.0.1
DATABASE_PORT=3306

如果没有自动加载我们可以在setting.py模块顶部中加入如下代码

from dotenv import load_dotenv
load_dotenv('.env')

4.表的创建

在完成了上面的准备工作之后就可以开始创建数据库表了。作为一个CMS系统,数据库的使用是必不可少的,在这里采用的MySQL数据库,当然你也可以使用其他的关心数据库,如MariaDB等。

bbs目录下新建一个models.py模块,该模块主要是用来定义我们的数据库表模型用的,嵌入以下代码

bbs/models.py

class User(db.Model, UserMixin):
    __tablename__ = 't_user'

    id = db.Column(db.INTEGER, primary_key=True, nullable=False, index=True, autoincrement=True)
    username = db.Column(db.String(40), nullable=False, index=True, unique=True, comment='user name')
    nickname = db.Column(db.String(40), nullable=False, unique=True, comment='user nick name')
    password = db.Column(db.String(256), comment='user password')
    email = db.Column(db.String(128), unique=True, nullable=False, comment='user register email')
    slogan = db.Column(db.String(40), default='')
    website = db.Column(db.String(128), default='', comment="user's website")
    location = db.Column(db.String(128), default='', comment='user location')
    avatar = db.Column(db.String(100), nullable=False, comment='user avatar')
    avatar_raw = db.Column(db.String(100), comment='use avatar raw file')
    create_time = db.Column(db.DATETIME, default=datetime.datetime.now)

    status_id = db.Column(db.INTEGER, db.ForeignKey('t_status.id'))
    college_id = db.Column(db.INTEGER, db.ForeignKey('t_college.id'))
    role_id = db.Column(db.INTEGER, db.ForeignKey('t_role.id'), default=3, comment='user role id default is 3 '
    
    college = db.relationship('College', back_populates='user')
    role = db.relationship('Role', back_populates='user')
    status = db.relationship('Status', back_populates='user')                                                                                      'that is student role')

在上面代码中定义了一个User类,并且让它继承自ModelUserMiXin两个基类。之后在类中定义了一些参数,其中使用db.Column定义就是表的字段,使用db.relationship定义是表与表之间的关系。在有些字段中我们使用了db.Foreignkey,这表示该字段是一个外键。通过外键关系,可以保证数据唯一性与完整性。

在这种小项目中使用外键可以提高开发效率,但如果项目量级大,并且业务并发量大,就不要去使用外键这种数据库层面的逻辑维护,可以将数据完整性维护添加到业务代码中去。

外键的字段命名没有明确的要求,因为使用的其他表的主键id作为外键进行连接,因此为了方便阅读,使用表名_id的形式进行命名,同时ForeignKey类传入参数为表名.字段名。模型类对应的表名由flask-sqlachemy自动生成,如果你在模型类中定义了__tablename__ 参数,则会使用该参数的值作为表名。

在User表中,我们使用了三个外键,分别是status_id、college_id、role_id,因此需要新建Status、College、Role三个表模型,同时role跟permission又是外键关系,因此还需要创建permission模型,在models.py模块中嵌入如下代码

class College(db.Model):
    __tablename__ = 't_college'

    id = db.Column(db.INTEGER, primary_key=True, nullable=False, autoincrement=True, index=True)
    name = db.Column(db.String(100), nullable=False)
    create_time = db.Column(db.DATETIME, default=datetime.datetime.now)

    user = db.relationship('User', back_populates='college', cascade='all')


class Role(db.Model):
    __tablename__ = 't_role'

    id = db.Column(db.INTEGER, primary_key=True, autoincrement=True, index=True)
    name = db.Column(db.String(40), nullable=False)
    permission_id = db.Column(db.INTEGER, db.ForeignKey('t_permission.id'), nullable=False)

    user = db.relationship('User', back_populates='role', cascade='all')
    permission = db.relationship('Permission', back_populates='role')


class Status(db.Model):
    __tablename__ = 't_status'

    id = db.Column(db.INTEGER, primary_key=True, autoincrement=True, index=True)
    name = db.Column(db.String(40), nullable=False)

    user = db.relationship('User', back_populates='status', cascade='all')


class Permission(db.Model):
    __tablename__ = 't_permission'

    id = db.Column(db.INTEGER, primary_key=True, autoincrement=True, index=True)
    name = db.Column(db.String(40), nullable=False)

    role = db.relationship('Role', back_populates='permission', cascade='all')

定义db.relationship()关系属性有什么作用呢?通过下图来进行解释。

一个角色可以对应多个用户,一个用户只能对应一个角色,我们在Role模型类中定义了user关系变量,在User模型类中定义了role关系变量。
user = User.query.filter_by(id=1).first()
print(user.role.name)
role = Role.query.filter_by(id=1).first()
print(role.user)

上面代码就解释了定义关系变量的作用,在一对多的关系中,在一端我们可以直接通过关系参数获取到对应多端的值,在多端关系参数返回是一个集合,在这个集合中我们可以获取到集合元素的所有值,大大的简化了我们工作。

5.初始化基础数据

完成了上述模型类的定义后,我们需要在数据库中建立我们的表。在__init__.py模块中加入下面的代码:

from bbs.models import *
from bbs.extensions import db
def create_app(config_name=None):
    # 省略之前的代码
    app.config.from_object(DevelopmentConfig)
    register_extensions(app)
    return app

  
def register_extensions(app: Flask):
    db.init_app(app)

在register_extensions()函数中初始化flask-sqlachemy,然后在工厂函数中进行注册调用。

有关Flask的第三方拓展一般都是使用的extension.init_app(app)的方式进行初始化注册!

接下来我们继续在__init.py__模块中添加下面的代码,用作初始化项目的必须基础数据。

def create_app(config_name=None):
    # 省略已有代码
    register_cmd(app)
    return app


def register_cmd(app: Flask):
    @app.cli.command()
    def init():
        click.confirm('这个操作会清空整个数据库,要继续吗?', abort=True)
        db.drop_all()
        click.echo('清空数据库完成!')
        db.create_all()
        init_status()
        click.echo('初始化状态表完成!')
        init_colleges()
        click.echo('初始化学院表完成!')
        init_permission()
        click.echo('初始化权限表完成!')
        init_role()
        click.echo('初始化角色表完成!')
        db.session.commit()
        click.echo('数据库初始化完成!')


def init_status():
    s1 = Status(name='正常')
    db.session.add(s1)
    s2 = Status(name='禁用')
    db.session.add(s2)
    db.session.commit()


def init_colleges():
    colleges = ['计算机科学与技术学院', '信息与通信工程学院', '法学院', '外国语学院', '体育学院', '生命科学学院', '文学院']
    for college in colleges:
        c = College(name=college)
        db.session.add(c)
    db.session.commit()


def init_permission():
    permissions = ['ALL', 'SOME', 'LITTLE']
    for per in permissions:
        p = Permission(name=per)
        db.session.add(p)
    db.session.commit()


def init_role():
    roles = ['超级管理员', '老师', '学生']
    r1 = Role(name=roles[0], permission_id=1)
    r2 = Role(name=roles[1], permission_id=2)
    r3 = Role(name=roles[2], permission_id=3)
    db.session.add(r1)
    db.session.add(r2)
    db.session.add(r3)
    db.session.commit()

在上述代码中我们定义了一个register_cmd(app)函数,同时在工厂函数中调用了它。在register_cmd函数中,通过app.cli.command()装饰器可以将函数admin()注册为一个flask命令,在admin()函数中,做了数据库初始化的操作。

通过使用前面定义的模型类,实例化对应的模型类对象,然后通过db.session.add(instance)添加到数据库,最后通过db.session.commit()函数将修改提交到数据库中。

在对数据库做了任何修改操作之后,我们都需要进行commit()操作!

确保激活了虚拟环境,在终端输入如下命令,就可以进行数据库初始化操作,通过终端输出的信息,可以看到数据库初始化的进度。

flask init

6. auth蓝图

完成上述工作之后就可以开始用户注册功能开发了。用户注册、登录等操作都属于验证操作,可以将其放入同一个功能蓝图中去,在bbs/blueprint目录下新建auth.py模块,嵌入如下代码。

from flask import Blueprint
auth_bp = Blueprint('auth', __name__, url_prefix='/auth')
别忘了在创建新的蓝图之后需要到create_app()函数中去进行注册,否则路由会找不到对应的视图函数而抛出404错误!

注册、登录部分的功能我打算分为两个章节来写,如果一个章节过长,就会导致读者失去耐心,因此,本章节的内容就到这里了。

教程中的资源文件可以进入我的github仓库下载源代码使用 仓库连接

下一节,我们将继续进行用户注册、登录功能的实现啦,尽请期待啦~~~

当前共有0条评论