一、开发环境的安装与配置

1.1 Python3.10以及PyCharm的安装

浏览器输入Python官方网址:https://www.python.org/,点击下载进入下载界面,选择相对应的版本,本项目采用的是win64位的Python3.10版本。安装PyCharm步骤与上述一致,仅网址不同,本项目采用的是Pycharm2021专业版。

1.2MySQL8.0的安装

MySQL的安装与上述如出一辙,在浏览器地址栏输入MySQL官方网址,进入下载界面,选择相对应版本。本次项目采用的是win64位的MySQL8.0版本。 

二.设计任务及理解

2.1设计任务

本项目的任务是基于flask模拟雨课堂教学系统的实现,前端页面的设计需参考雨课堂的界面,设计实现课程班级模块中的“我听的课”功能,能够创建课程信息,并提交。设计实现资源库中上传课件的功能,并显示课程上传时间,以及右上角数量汇总信息,同时可以实现删除的效果。

2.2 理解

对于本次项目,我对设计任务的理解的内容有:首先是本项目要基于flask框架,其次就是前端的页面,即使设计任务里没有提及到关于用户登录和注册的功能,为了设计的严谨性,这一功能是要添加上的;紧接着就是实现主页中的功能,可以连接MySQL,在MySQL中创建数据库,并与后端相连,再将数据传入前端页面中,可将用户名和班级信息进行绑定,从而达到设计的严谨性。

三、所用第三方库简介

3.1 Flask框架简介及所需要的库

Flask是一个非常轻量级的框架,提供了搭建Web服务的必要组件,Flask具有良好的扩展性,可以使用其他开源的Flask扩展插件。Flask框架里主要包含Jinja2模板引擎、路由、视图、静态文件和蓝图等。在本次项目中用到的有render_template、request、session、redirect、Jinja2模板引擎、路由、视图以及静态文件。

3.2 pymysql库

pymysql是在pycharm上使用的第三方包,在安装pymysql之前,首先必须要确保电脑上安装了MySQL。pymysql是作为MySQL客户端来操作数据,它的操作流程如图1所示。

如何用python中的flask模块制作一个网页 python flask html_sql

图1 pymysql操作流程

四、设计方案与实现

4.1 项目结构

本次软件设计我们的项目结构如图2所示,static文件夹分别存放CSS文件、JS文件、图片以及上传的课件。templates文件夹存放模块文件,模板即在Flask中允许响应给用户看的网页,Flask中的模板是依赖于Jinja2的模板系统。默认情况下,Flask会在程序文件夹中的templates的子文件夹中搜索模板,我们需要手动创建templates文件夹。本次项目中,templates中共有4个模板文件,用于登录界面、注册界面、主界面以及上传界面的展示。app.py文件为后端的接口,里面配置了视图函数和路由。userdata.py文件为业务逻辑模块,用于连接数据库以及编写了相对应的方法。

如何用python中的flask模块制作一个网页 python flask html_sql_02

 图2 项目结构图

4.2 userdata.py与app.py设计方案

userdata模块中我们定义了三个类。User类(父类)主要用于用户的登录和注册,并将注册的数据保存到数据库中,以及查询用户所在的班级;Course_class类(子类)主要用于查询同一班级下的班级成员以及用户加入班级;Course类(子类)主要用于查询用户听的可以及添加课程。app.py模块中,我们主要定义了7个接口即视图函数,具体如图3所示,每个视图函数还会配置对应的路由。同时为了保护用户的隐私安全,我们还在该模块中设置了秘钥,否则将会抛出异常。

如何用python中的flask模块制作一个网页 python flask html_数据库_03

图3 app.py视图函数

4.3 数据库的设计

在本次项目中,我们新建了用于存储用户数据的数据库“yeketang”,该数据库如下图4所示,图中有三张表分别为user用户表、class班级表、以及course用户课程表。user表中有username、password字段;class表中有username、classid字段;course表中有username、courseid、coursename字段。

如何用python中的flask模块制作一个网页 python flask html_sql语句_04

 图4 数据库搭建

4.4 登录和注册的功能实现

接下来我们对登录和注册功能的实现,首先如图5所示为userdata模块下的User类中的登录和注册的流程图。在登录login()方法中,我们通过执行SQL语句来实现此功能。我们在用fetchone()函数获取数据库读取到的结果,返回的数据类型为元组类型,随后添加if条件语句,判断用户输入的密码是否在这个元组中,如果在元组中则返回1。如图6所示为app接口模块下的登录注册流程图。在HTTP中,常见的请求方法有GET和POST。GET请求中的参数包含在URL里面,数据可以在URL中看到,而POST的请求的URL不会包含这些数据;同时GET请求提交的数据最多只有1024字节,而POST方式没有限制。因此为了防止用户输入的密码泄露,我们在login()接口中,我们添加了if条件语句进行判断,如果是GET方法,我们就展示页面;如果是POST方法,就通过request.form.get()函数获取到前端页面输入的值,接着创建实例化对象并调用登录login()方法,流程如图8所示。最后我们添加一个if条件判断语句,判断login()方法返回的值是否为1,如果为1则说明登录成功,我们便将页面重定向至index主界面,否则就返回“账号密码错误,请重新登录”。对于注册功能的实现,与上述登录功能基本类似。首先在接口模块中先判断HTTP的请求方法。GET方法就展示页面,在POST方法下,获取到前端注册页面的username、password,接着创建实例化对象,并将username、password传入对象中,再调用register()方法执行SQL语句,将username、password存入数据库中,最后再将页面重定向至登录界面。

如何用python中的flask模块制作一个网页 python flask html_sql_05

图5 userdata模块登录注册流程图

如何用python中的flask模块制作一个网页 python flask html_python_06

图6 app模块登录注册流程 

userdata.py

# 定义用户类class User:    def __init__(self, username, password):        self.username = username        self.password = password    # 登录账号    def login(self):        # ping()使用该方法 ping(reconnect=True) ,那么可以在每次连接之前,会检查当前连接是否已关闭,如果连接关闭则会重新进行连接。        db.ping(reconnect=True)        # 编写sql语句,用来查询前端输入的用户名与数据库中user表对应的密码        sql = "select password from user where username='" + self.username + "'"        # 执行sql语句        cursor.execute(sql)        # 将数据从数据库读出 类型为元组类型        results = cursor.fetchone()        # 判断前端输入的密码是否与数据库密码一致,一致则返回1        if self.password in results:            return 1        # 关闭数据库        db.close()    # 注册账号    def register(self):        # ping()使用该方法 ping(reconnect=True) ,那么可以在每次连接之前,会检查当前连接是否已关闭,如果连接关闭则会重新进行连接。        db.ping(reconnect=True)        # 插入sql语句        sql_0 = "INSERT INTO user(username,password) VALUES(%s,%s)"        sql = sql_0 % (repr(self.username), repr(self.password))        # 执行sql语句        cursor.execute(sql)        # 提交到数据库执行        db.commit()        # 关闭数据库        db.close()

 app.py

# 登录接口@app.route('/login', methods=['GET', 'POST'])def login():    if request.method == 'GET':        return render_template('login.html')    if request.method == 'POST':        username = request.form.get('username')  # 接收来自前台的账号        password = request.form.get('password')  # 接收来自前台的密码        user = ud.User(username, password)        logins = user.login()        if logins == 1:            # 将用户名存储至用户会话中,用户会话是一种私有存储,默认情况下,会保存在cookie中。            session['username'] = username            return redirect('/index')        else:            return '账户密码错误,请重新登录'# 注册接口@app.route('/register', methods=['GET', 'POST'])def register():    # 判断是get请求还是post请求    if request.method == 'GET':        return render_template('register.html')    if request.method == 'POST':        username = request.form.get('username')        password = request.form.get('password')        user = ud.User(username, password)        user.register()        return redirect('/login')

4.5 展示用户班级及班级成员功能的实现

接着我们对展示用户班级及班级成员的功能进行实现,具体流程如图7所示。首先我们需要通过用户会话来获取到用户在登录界面输入的用户名,调用show_user()方法时,还需要判断用户是否已经加入班级,定义一个变量results用于接收查询到的结果。如果数据库查不到记录,我们将results赋值为None,并返回results;反之我们将查询到的记录赋值给results并转为字符串,将结果返回,展示班级成员的方法与上述类似,这里就不展开说明了。最后在主接口中,判断返回的结果是否为None,是则添加班级,否则展示。接着再将后端数据传入前端数据。同时我们前端页面运用Jinja2模板进行判断,根据结果是否为None,展示不同的内容。

如何用python中的flask模块制作一个网页 python flask html_flask_07

图7 用户班级流程图

userdata.py

# 查询用户所在班级的方法    def show_user(self):        # ping()使用该方法 ping(reconnect=True) ,那么可以在每次连接之前,会检查当前连接是否已关闭,如果连接关闭则会重新进行连接。        db.ping(reconnect=True)        # 插入sql语句        sql = "SELECT classid FROM class WHERE username='" + self.username + "'"        #执行sql语句,对该用户在class表中没有记录抛出异常,并将返回的结果赋值为None        try:            # 执行sql语句            cursor.execute(sql)            # 将数据从数据库读出 类型为元组类型 转成字符串            results = ''.join(cursor.fetchone())            #关闭数据库            db.close()            return results        except:            results=None            db.close()            return results# 定义课程班级类 班级类继承用户类class Course_class(User):    def __init__(self, username, password, class_id):        super().__init__(username, password)        self.class_id = class_id    # 查询用户所在的班级成员的方法    def show_class_member(self):        # ping()使用该方法 ping(reconnect=True) ,那么可以在每次连接之前,会检查当前连接是否已关闭,如果连接关闭则会重新进行连接。        db.ping(reconnect=True)        if self.class_id==None:            results1=[]        else:            # 插入sql语句            sql = "SELECT username FROM class WHERE classid='" + self.class_id + "'"            # 执行sql语句            cursor.execute(sql)            # 将数据从数据库读出 类型为多个元组            results = cursor.fetchall()            # 定义一个空列表            results1 = []            # 遍历元组中每个元素,将每个元素转化为字符串并添加至列表中            for item in results:                results1.append(''.join(item))            # 关闭数据库            db.close()        # 返回结果        return results1    #加入班级    def add_class(self):        # ping()使用该方法 ping(reconnect=True)        db.ping(reconnect=True)        #编写sql语句        sql_0 = "INSERT INTO class(username,classid) VALUES(%s,%s)"        sql = sql_0 % (repr(self.username), repr(self.class_id))        cursor.execute(sql)        # 提交到数据库执行        db.commit()        # 关闭数据库        db.close()

 app.py代码见下一部分

4.6 展示用户课程及添加课程功能实现

接下来我们对用户课程及添加课程功能进行实现。该功能实现要比上述展示用户班级功能要简单的多。因为课程可以重复添加多次。用户可以学很多的课程,用户也可以不学任何的课程;而对于班级而言,用户仅可以添加一次。所以图中对于展示用户课程以及添加课程功能并没有对此进行判断。由于我们需要在前端页面展示用户的课程号以及课程名,我们将会数据库中返回的元组转成字典,再将字典添加到列表中,最后再模板文件中用Jinja2模板,通过for循环遍历列表,展示到前端页面中。

app.py

# 主界面接口(显示用户登录的用户名即个人信息)@app.route('/index', methods=['GET', 'POST'])  # 这里写入的是接口名称,即URL地址def main_interface():    # 从用户会话中获取到用户名    username = session.get('username')    # 获取用户在前端输入的课程号    course_id = request.form.get('course_id')    # 获取用户在前端输入的课程名    course_name = request.form.get('course_name')    # 创建实例化对象user    user = ud.User(username, '')    # 调用方法,获取用户的班级号    class_id = user.show_user()    # 创建Course_class类中的实例化对象    course_class = ud.Course_class(username, '', class_id)    # 对班级号进行判断,如果班级号为空,并且是POST请求,则将前端数据赋值给该用户    if class_id == None:        if request.method == 'POST':            class_id1 = request.form.get('class-id')            # 赋值完成后,重新创建Course_class类实例化对象            course_class1 = ud.Course_class(username, '', class_id1)            # 调用方法,将班级号加到数据库中            course_class1.add_class()            return redirect('/index')    # 调用展示班级成员的方法    class_member = course_class.show_class_member()    # 创建Course类中的实例化对象    course = ud.Course(username, '', course_id, course_name)    if request.method == 'POST':        course.add_course()    course_list = course.search_course()    # 将用户名 班级号 班级成员 课程列表传入前端    return render_template('index.html', username=username, class_id=class_id, class_member=class_member,                           course_list=course_list)

userdata.py

# 定义课程类 课程类继承于用户类class Course(User):    def __init__(self, username, password, course_id, course_name):        super().__init__(username, password)        self.course_id = course_id        self.course_name = course_name    # 定义查询课程的方法    def search_course(self):        # ping()使用该方法 ping(reconnect=True) ,那么可以在每次连接之前,会检查当前连接是否已关闭,如果连接关闭则会重新进行连接。        db.ping(reconnect=True)        # 插入sql语句        sql = "SELECT courseid,coursename FROM course WHERE username='" + self.username + "'"        # 执行sql语句        cursor.execute(sql)        # 将数据从数据库读出 类型为多个元组        results = cursor.fetchall()        # 定义一个空列表        course_list = []        # 将返回的元组通过遍历转成字典,最后再将字典存入列表中        for items in results:            results1 = [items]            for item in results1:                course_dict = {item[0]: item[1]}                course_list.append(course_dict)        # 关闭数据库        db.close()        return course_list    # 添加课程并加入到数据库    def add_course(self):        # ping()使用该方法 ping(reconnect=True)        db.ping(reconnect=True)        # 插入sql语句        sql_0 = "INSERT INTO course(username,courseid,coursename) VALUES(%s,%s,%s)"        sql = sql_0 % (repr(self.username), repr(self.course_id), repr(self.course_name))        # 执行sql语句        cursor.execute(sql)        # 提交到数据库执行        db.commit()        # 关闭数据库        db.close()

4.7 上传文件及删除文件功能实现

最后我们对上传文件及删除文件功能进行实现,该功能大致流程示意图如图13所示。首先前端页面选择本地磁盘下需要上传的文件,点击上传按钮。在上传接口中我们通过request.file获取到相应的文件,将其存放在static文件夹的uploads文件夹中。接着通过os.walk()函数遍历uploads文件夹下的所有文件,将其存放到列表中,遍历该列表,通过os.path.getctime来获取每个文件的创建时间即上传时间,将其保存至列表中。最后将两个列表通过zip()函数打包至前端页面,前端页面再将其遍历,右上角的数量汇总信息则通过列表的长度来显示。删除文件是在展示上传文件时,每条记录后面添加一个“删除”的a标签,该标签的URL地址为每条记录的文件名。在app接口模块添加相关的视图函数,路由的URL为该文件名。获取需要删除的文件名调用os.remove()函数将其删除,删除完之后再重新,流程如图8所示。至此,本次项目所有功能都已经实现。

如何用python中的flask模块制作一个网页 python flask html_python_08

图8 上传文件与删除文件

app.py

@app.route('/upload', methods=['POST'])def upload_file():    file_list = []    file_time = []    if request.method == 'POST':        # 获得前端上传的文件        f = request.files['file']        # 将其保存在static下的uploads文件夹中        f.save(path.join(app.config['uploads'], f.filename))        # 对uploads文件夹下所有的文件进行遍历        for item in os.walk(app.config['uploads']):            # os.walk函数执行后,会产生(root,dirs,files)的三元组            # root所指的是当前正在遍历的这个文件夹的本身的地址            # dirs是一个 list,内容是该文件夹中所有的目录的名字(不包括子目录)            # files 同样是 list,内容是该文件夹中所有的文件(不包括子目录)            for items in item[2]:                file_list.append(items)        for items1 in file_list:            # 获取上传文件时的时间,并对其格式化            a = os.path.getctime(app.config['uploads'] + f'/{items1}')            b = time.localtime(a)            c = time.strftime("%Y-%m-%d %H:%M:%S", b)            file_time.append(c)        # 将两个列表打包,方便前端遍历展示        zip_list = zip(file_list, file_time)        return render_template('upload.html', zip_list=zip_list, file_list=file_list)# 删除上传的文件@app.route("/delete/<file>")def delete_file(file):    file_list = []    file_time = []    # 需要删除的文件路径    path = app.config['uploads'] + f"/{file}"    # 删除文件    os.remove(path)    for item in os.walk(app.config['uploads']):        for items in item[2]:            file_list.append(items)    for items1 in file_list:        a = os.path.getctime(app.config['uploads'] + f'/{items1}')        b = time.localtime(a)        c = time.strftime("%Y-%m-%d %H:%M:%S", b)        file_time.append(c)    zip_list = zip(file_list, file_time)    return render_template('upload.html', zip_list=zip_list, file_list=file_list)if __name__ == '__main__':    app.run()

upload.html 

<table border=1>                <tr>                    <td colspan=3 style="text-align: center;">资源库</td>                <tr>                    <td>文件名</td>                    <td>上传时间</td>                    <td>操作</td>                </tr>                {% for item,items in zip_list %}                    <tr>                        <td>{{ item }}</td>                        <td>{{ items }}</td>                        <td><a href="/delete/{{ item }}">删除</a></td>                    </tr>                {% endfor %}            </table>

五、设计结果分析

在上面的叙述所我们已经完成了对该项目所有功能的实现,现在让我们运行程序,查看实际的效果。首先我们点击运行,打开网址进入登录界面,为了运行结果的严谨性,我们选择注册账号,输入账号1404和密码123123,点击注册跳转至登录界面,数据库user表中刚刚注册的账号已被加入数据库中,接着输入我们注册的账号和密码并点击登录进入主界面。班级号显示为None,页面显示没有加入相应的班级。我们点击加入班级,输入班级号1002,并点击添加课程输入课程号B01以及课程名Python程序设计,点击提交。相关的信息都已被添加至页面中,接着我们点击侧边栏资源库页面,点击上传文件。上传5个文件并点击删除,将其全部删除,项目运行如图9所示。从图9可以看出此次运行,所有功能都已实现,运行效果很好。

如何用python中的flask模块制作一个网页 python flask html_flask_09

图9 项目运行图