简介
在上一节中,主要介绍了项目入口怎么实现,同时基于jinja2提供的模板继承功能完成了基模板的编写,同时使用flask的错误处理装饰器完成了继承自基模板的错误页面自定义。在本章节中,将介绍用户注册、登录等功能模块的实现。
1.知识预览
在本章节中,将学习到以下内容:
- flask-sqlachemy 数据库orm的使用
- 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类,并且让它继承自Model
、UserMiXin
两个基类。之后在类中定义了一些参数,其中使用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仓库下载源代码使用 仓库连接 个人博客
下一节,我们将继续进行用户注册、登录功能的实现啦,尽请期待啦~~~