收藏!最详细的Python全栈开发指南 看完这篇你还不会Python全栈开发 你来打我!!!

Python Web全栈开发入门实战教程教程

   大家好,我叫亓官劼(qí guān jié ),这个《Python Web全栈开发入门实战教程教程》是一个零基础的实战教程,手把手带你开发一套系统,带你了解Python web全栈开发。写这篇文章的初衷就是想给想入门Python Web开发还没入门的小伙伴们一个详细的,真正零基础的一个开发教程,可以让小伙伴们在入门的途中少走很多弯路。
   在服务端端开发上,主流还是Java开发,Python的话虽说开发快速、便捷,但是在一些地方还是有缺陷的。但是Python Web有一个非常好的优点就是方便、快捷、易学、易用,可以让小伙伴们快速的入门Web开发。
  之前有不少小伙伴来问我,怎么开发Web系统啊?毕业论文的中期检查马上就要来了,我的毕业设计还一动没动,我完全不会Web系统的开发,我该怎么办?,虽说大学四年我们或多或少的都学过各种各样的语音,但是项目开发做的却很少,很多学校开了点项目开发的也,最后也都是上了点理论,开发?那是不可能开发的,最后还是一套试卷定成绩,期末背背就是高分,导致很多小伙伴们到了写毕设的时候,连个简简单单的web系统都不会开发。这篇文章也可以给小伙伴们一个参考吧,如果毕设的时候开发web系统的时候无从下手,可以来尝试一下Python Web,这篇文章将会真正零基础的,每步都进行一个详细的说明,带你完成一个小项目,实现各种各样的一个功能,给你提供一些思路。
  写这篇文章的另一个初衷就是,想给想入门Python Web开发的同学或者想学习Web开发的同学,提供一个练手的项目,一套完整的项目实现过程,避免刚入门时的茫然无措。我当时大二刚刚开始做Web开发时,由于学校里面做开发的比较少吧,自己一个人摸索,找网上的各种教程,在教程里他们总是会忽略一些细节,然后我实践运行的时候就是频频报错,一个小小的BUG就要折腾很久,还记得当时项目完成,部署到CentOS服务器上的时候,使用的Nginx+UWSGI进行部署,当时直接跑UWSGI可以跑,直接跑Nginx也可以,两个加起来就是一直错误,各种百度、谷歌,也找不到问题所在,最后折腾了半个月,在一篇很小的文章上看到了UWSGI的一个接口文件(安装的时候不是自动生成的,需要自己创建),然后一个很小的bug吧,折腾了半个月。所以这篇文章,也是想给刚入门的小白提供一个参考,一个少走弯路的方法吧!
大家一起加油,时光荏苒,未来可期!

  写在最前面,本教程为小白教程,很多进阶的内容都没有做添加,后期项目会添加很多需要的功能,届时会放到文章的最后,或者在Flask专栏中,有需要的可以自取,或者看其他的文章,文档也一样。例如现在登录时的图像验证码功能的文章已在专栏中发布,有需要的可以自己添加。本文章截止目前11.7万字,中有一般的代码,所有代码都是实时运行通过的。
  本文原创为CSDN博主亓官劼,原文链接为:收藏!最详细的Python全栈开发指南 看完这篇你还不会Python全栈开发 你来打我!!!,请大家支持原创,拒绝抄袭。

博主的站内博客导航:博客文章内容导航(实时更新)
更多精彩博客文章推荐

  • 小白都能看懂的实战教程 手把手教你Python Web全栈开发 (DAY 1)
  • 小白都能看懂的实战教程 手把手教你Python Web全栈开发 (DAY 2)
  • 小白都能看懂的实战教程 手把手教你Python Web全栈开发 (DAY 3)
  • 小白都能看懂的实战教程 手把手教你Python Web全栈开发 (DAY 4)
  • 小白都能看懂的实战教程 手把手教你Python Web全栈开发 (DAY 5)
  • 小白都能看懂的实战教程 手把手教你Python Web全栈开发 (DAY 6)
  • 小白都能看懂的实战教程 手把手教你Python Web全栈开发 (DAY 7)
  • 一本教你如何在前端实现富文本编辑器
  • 小白都能看得懂的教程 一本教你如何在前端实现markdown编辑器
  • Flask学习(基本语法)
  • 一文教会你Bootstrap,让你也可以快速建站
  • 一文教你如何白嫖JetBrains全家桶(IDEA/PtChram/CLion)免费正版

本篇文章目录

  • Python Web全栈开发入门实战教程教程
    • @[TOC](本篇文章目录)
    • 一、开发环境 & 项目简介
    • 二、新建一个Flask项目
      • 2.1 创建项目
      • 2.2 安装本项目所需要的一些插件
      • 2.3 创建数据库
    • 三、Flask基础知识部分
      • 3.1 Flask程序的基本构造
      • 3.2 路由器请求方法限定
      • 3.3 动态路由
      • 3.4 Jinja2模板引擎
      • 3.4 传参时,列表和字典的使用:
      • 3.5 控制语句的使用
      • 3.6 过滤器的基本使用
      • 3.7 使用flash传递消息
      • 3.8 使用WTF实现web表单
        • 3.8.1 WTForms支持的HTML标准字段
        • 3.8.2 WTForms验证函数
        • 3.8.3Flask-WTF扩展的安装
        • 3.8.4使用Flask-WTF创建表单示例
      • 3.9 Flask-SQLAlchemy拓展
        • 3.9.1 Flask-SQLAlchemy拓展安装方法:
        • 3.9.2 配置数据库:
        • 3.9.3创建数据库类,映射数据库表
        • 3.9.4在视图中操作数据库查询
        • 3.9.5 常用的SQLAlchemy查询过滤器
        • 3.9.6常用SQLAlchemy查询执行方法
      • 3.10 使用werkzeug计算密码散列值
      • 3.11 文件的上传、下载
        • 3.11.1 上传文件
        • 3.11.2 在后台接收文件并保存
        • 3.11.3 在后台保存文件
      • 3.12 Flask 蓝图
        • 3.12.1 蓝图的基本用法
    • 四、导航栏、注册、登录、注销、主页功能的实现
      • 4.1 导航栏实现
      • 4.2 注册功能实现
        • 4.2.1 注册功能实现-数据库
        • 4.2.2 注册功能实现-前端
        • 4.2.3 注册功能实现-后端
      • 4.3 登录功能实现
        • 4.3.1 登录功能实现-前端
        • 4.3.2 登录功能实现-后端
          • 4.3.2.1 登录功能实现-后端-验证登录信息
          • 4.3.2.2 登录功能实现-后端-登录状态保持
      • 4.4 注销功能实现
      • 4.5 主页实现
    • 五、发布帖子、论坛页面实现
      • 5.1 发布帖子的功能实现
        • 5.1.1 发布帖子的实现-前端
        • 5.1.2 发布帖子的实现-数据库
        • 5.1.3 发布帖子的实现-后端
      • 5.2 论坛页面的实现
        • 5.2.1 论坛页面的实现-前端
        • 5.2.2 论坛页面的实现-后端
    • 六、帖子详情、回复帖子、访问过滤功能实现
      • 6.1 帖子详情页面、帖子回复实现
        • 6.1.1 帖子详情页面实现-后端
        • 6.1.2 帖子详情页面实现-前端
        • 6.1.3 帖子回复实现-前端
        • 6.1.3 帖子回复实现-后端
      • 6.2访问过滤功能实现
    • 七、个人中心相关页面功能实现
      • 7.1个人中心页面实现
        • 7.1.1 个人中心页面-后端
        • 7.1.2 个人中心页面-前端
      • 7.2 修改密码功能实现
        • 7.2.1 修改密码功能实现-前端
        • 7.2.1 修改密码功能实现-后端
      • 7.3 查看发布帖子功能实现-后端
        • 7.3.1 查看发布帖子功能实现-后端
        • 7.3.2 查看发布帖子功能实现-前端
    • 八、资源专区相关功能实现
      • 8.1资源上传功能实现
        • 8.1资源上传功能实现-前端
        • 8.1.2资源上传功能实现-数据库端
        • 8.1.3资源上传实现-后端
      • 8.2 资源专区功能实现
        • 8.2.1 资源专区功能实现-后端
        • 8.2.2资源专区功能实现-前端
        • 8.2.3 实现文件在线查看功能
        • 8.2.4 实现文件下载功能
    • 九、项目迁移
      • 9.1 生成插件文件版本
      • 9.2生成数据库表结构和数据库数据迁移

  如果嫌本篇文章太长,不好拉动的话,可以使用上面的目录今天跳转,或者查看博主之前分篇幅写的博客,相对简短一些,博客地址为:

  • 小白都能看懂的实战教程 手把手教你Python Web全栈开发 (DAY 1)
  • 小白都能看懂的实战教程 手把手教你Python Web全栈开发 (DAY 2)
  • 小白都能看懂的实战教程 手把手教你Python Web全栈开发 (DAY 3)
  • 小白都能看懂的实战教程 手把手教你Python Web全栈开发 (DAY 4)
  • 小白都能看懂的实战教程 手把手教你Python Web全栈开发 (DAY 5)
  • 小白都能看懂的实战教程 手把手教你Python Web全栈开发 (DAY 6)
  • 小白都能看懂的实战教程 手把手教你Python Web全栈开发 (DAY 7)

  本文原创为CSDN博主亓官劼,原文链接为:收藏!最详细的Python全栈开发指南 看完这篇你还不会Python全栈开发 你来打我!!!,请大家支持原创,拒绝抄袭。

一、开发环境 & 项目简介

本项目源码在GitHub上进行开源,本项目的GitHub地址为:OnlineForumPlatform,大家可以去clone全部源码,喜欢的话,也欢迎大家star一下。
   我们这里使用的是Python Web的Flask框架进行开发,使用的集成开发环境为Pycharm,使用的开发语言为Python3,前端为HTML/CSS/JavaScript/Bootstrap,数据库为MySQL,使用SQL语句进行描述。
  我们这次实战采用的项目是一个在线论坛系统,字如其名,就是开发一个在线的论坛系统。需要实现各个的功能如下:

  • 注册:注册账号
  • 登录:登录账号进入系统,如果登录普通用户,则只有普通用户的权限,如果是管理员账号,则有管理员账号权限。
  • 查看论坛问题列表:查看在线论坛系统中所有已发布的问题.
  • 发布问题:
    • 发布自己的问题,等待他人回答
    • 支持富文本输入
    • 支持Markdown输入
  • 问题详情页面:显示当前问题的讨论、回复。
  • 回答问题:回答他人问题。
  • 个人中心:
    • 显示个人账号信息
    • 可以修改个人账号信息
    • 显示个人发帖情况
  • 资源专区
    • 查看资源列表(已有资源)
    • 在线预览资源
    • 下载资源
    • 上传资源
        项目目前所涉及的功能就是这些,同时还有一些网页安全的一些措施,例如对访问客户的过滤等等功能。这就是我们这个实战项目的一个大致的功能介绍。

博主的站内博客导航:博客文章内容导航(实时更新)
更多精彩博客文章推荐

  • 一本教你如何在前端实现富文本编辑器
  • 小白都能看得懂的教程 一本教你如何在前端实现markdown编辑器
  • 一文教会你Bootstrap,让你也可以快速建站
  • 一文教你如何白嫖JetBrains全家桶(IDEA/PtChram/CLion)免费正版

二、新建一个Flask项目

  既然是开发项目,我们第一步当然是先新建一个项目啦!本来打算第一章写我们的Flask基础知识部分,但是又怕有的小伙伴不会创建我们的Flask项目,索性我们就保姆式教学到底,我们从新建项目开始。

2.1 创建项目

这里使用Pycharm专业版作为开发工具,如果还没有Pycharm的同学可以下载安装一个,如果没有激活的话,可以查看一文教你如何白嫖JetBrains全家桶(IDEA/PtChram/CLion)免费正版
在这里插入图片描述
这里选择Flask项目,我这里的项目名称为OnlineForumPltform,使用virtualenv虚拟环境,模板引擎为Jinja2。
在这里插入图片描述
这样一个Flask基本项目就创建好啦。创建完成之后页面为这样:
在这里插入图片描述
我们点击运行,测试一下第一个hello world:
在这里插入图片描述
运行之后,我们访问运行栏中的地址,默认的地址为:http://127.0.0.1:5000
页面返回Hello World!就表示我们第一个已经完成了第一步!
在这里插入图片描述
下面我们来创建一些我们下面开发所需要的文件和文件夹。
在这里插入图片描述
还有requirements.txt文件是存放本项目所有插件和版本信息的文本,README.md文件是本项目的说明文件。

  本文原创为CSDN博主亓官劼,原文链接为:收藏!最详细的Python全栈开发指南 看完这篇你还不会Python全栈开发 你来打我!!!,请大家支持原创,拒绝抄袭。

2.2 安装本项目所需要的一些插件

点开设置,进行安装插件:
在这里插入图片描述
点击+进入添加插件页面,这里我们先添加一个PyMysql,其他的等我们需要的时候再进行添加。
在这里插入图片描述

2.3 创建数据库

这里我们先创建一个空的数据库,然后我们使用PyCharm连接这个数据库,方便我们后面进行数据库相关的操作。
(我这里是mac系统,mysql只安装了命令行版本,如果是安装图像界面版本的可以直接进行创建).终端进入mysql的命令为mysql -u root -p然后输入密码即可进入。

Enter password:
Welcome to the MySQL monitor.  Commands end with ; or \g.
Your MySQL connection id is 8
Server version: 8.0.16 MySQL Community Server - GPL

Copyright (c) 2000, 2019, Oracle and/or its affiliates. All rights reserved.

Oracle is a registered trademark of Oracle Corporation and/or its
affiliates. Other names may be trademarks of their respective
owners.

Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.

mysql> create database OnlineForumPlatform;
Query OK, 1 row affected (0.06 sec)

mysql>

即可创建成功。

创建完成之后我们使用PyChram来连接数据库。
在这里插入图片描述
选择mysql数据库,然后我们填写用户名root和密码,然后填写创建的数据库名称即可,填写完成之后点击测试连接
在这里插入图片描述
测试连接正确之后,我们点击OK即可
在这里插入图片描述
然后我们在pycharm右面的Database就可以看到我们的数据库了
在这里插入图片描述
  好了,到目前为止我们项目所需要的一些基本的插件和数据库都创建完成啦~大家都创建的顺利吗?下面我们先插播一章基础的Flask基础知识讲解,然后就正式的到我们的项目实战环节啦!如果Flask基础知识部分已经了解的,可以直接跳到我们的四,开始我们的项目实战部分啦!在文章顶部有我们文章内的目录,可以直接进行跳转。

  本文原创为CSDN博主亓官劼,原文链接为:收藏!最详细的Python全栈开发指南 看完这篇你还不会Python全栈开发 你来打我!!!,请大家支持原创,拒绝抄袭。


博主的站内博客导航:博客文章内容导航(实时更新)
更多精彩博客文章推荐

  • 一本教你如何在前端实现富文本编辑器
  • 小白都能看得懂的教程 一本教你如何在前端实现markdown编辑器
  • 一文教会你Bootstrap,让你也可以快速建站
  • 一文教你如何白嫖JetBrains全家桶(IDEA/PtChram/CLion)免费正版

三、Flask基础知识部分

  磨刀不误砍柴工,既然是真正的零基础,我们就先来温习一下Flask的一些基础的语法,如果已经了解的这部分的话,可以直接跳到第四章呦~

3.1 Flask程序的基本构造

  首先就是我们的Flask程序的一个基本构造,我们首先需要从flask模块中导入我们的Flask,这是我们框架的主要模块,然后我们还需要导入我们的render_template,这是用来渲染我们的模板的,将我们的HTML页面返回给请求者。
  在前面我新建的时候已经有一个默认的app.py,是return一个Hello World,各位小伙伴们都运行成功了吗?这里我们返回的是一个Html文件,在这里说一句,我们的HTML文件一般都是存放在我们的templates文件夹中,我们就可以直接在这里返回呦,大家可以创建一个demo.html,然后使用下面这个语看看能否返回你的页面呢?

# 从flask中导入Flask和render_template
from flask import Flask,render_template

app = Flask(__name__)

@app.route('/')
def index():
    return render_template('demo.html')

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

3.2 路由器请求方法限定

  在route的参数中设置method的值,默认是GET方法,但是我们很多涉及到表单的页面为了数据的安全性,我们需要使用POST方法去创建,所以我们这里就需要设置我们的请求方式啦methods=['GET', 'POST'],完整的样例为:

@app.route('/', methods=['GET', 'POST'])
def index():
    return render_template('demo.html')

3.3 动态路由

  有时候我们需要一个动态的参数,然后我们根据参数来返回一个对应的页面,这个时候就需要使用到动态路由啦!例如下面这个路由,他需要一个参数交错name,然后他会返回一个hello + { 你输入的name},大家可以测试一下,是不是能正确的返回你的名字。这里当你在王志中只输入http://127.0.0.1:5000/user/则会报错,因为没有输入name参数。

@app.route('/user/', methods=['GET', 'POST'])
def user(name):
    return 'hello {}'.format(name)

其中参数可以通过类型进行限定,例如:int:name即只接受int类型的参数

3.4 Jinja2模板引擎

  由于我们的Web系统是需要一个动态的网页,可能有部分内容数据是需要计算出来的,或者是从后端服务器的数据库中获取的,所以我们需要使用Jinja2模板引擎来对我们的模板进行一个渲染。在函数中传递参数,在HTML页面中,利用{{ }}获取传递的参数,例如我们下面这个例子,在服务器端向前端传输一个变量user_var,其中变量名称为html_var,然后我们在前端进行获取变量,并且显示。需要注意的是我们在传递参数的时候,等号前面的传递的参数名(也就是我们在前端获取的时候所需要使用的名),在等号后面的是我们当地的参数名,也就是服务器中的参数,一般来说,我们两名的命名是一样的。
app.py

from flask import Flask,render_template

app = Flask(__name__)

@app.route('/user', methods=['GET', 'POST'])
def user():
    user_var="程序设计类辅助实验教学平台"
    return render_template('demo3.html', html_var=user_var)

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

demo3.html


<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Titletitle>
head>
<body>
<h1>This is a Demo3 Pageh1>
<h2>It is only using for testh2>
这是传输过来的变量:{{ html_var }}
body>
html>

显示效果:
收藏!最详细的Python全栈开发指南 看完这篇你还不会Python全栈开发 你来打我!!!_第1张图片
在模板中注释符号为{# #},例如{# This is a annotation #}

3.4 传参时,列表和字典的使用:

app.py

from flask import Flask,render_template

app = Flask(__name__)


@app.route('/', methods=['GET', 'POST'])
def user():
    user_var="程序设计类辅助实验教学平台"
    list_test=['这是list的1','这是list的2','这是list的3','这是list的4','这是list的5']
    dict_test={
        'name': "这是测试name",
        'key': "这是测试key"
    }
    return render_template('demo3.html', html_var=user_var,list_test=list_test,dict_test=dict_test)


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

demo3.html


<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Titletitle>
head>
<body>
<h1>This is a Demo3 Pageh1>
<h2>It is only using for testh2>
这是传输过来的变量:{{ html_var }}<br>
<h2>列表使用:h2>
{{ list_test }}<br>
{{ list_test.1 }}<br>
{{ list_test[2] }}<br>
<h2>dict使用:h2>
{{ dict_test }}<br>
{{ dict_test.name }}<br>
{{ dict_test['key']}}<br>
body>
html>

页面效果:
收藏!最详细的Python全栈开发指南 看完这篇你还不会Python全栈开发 你来打我!!!_第2张图片

3.5 控制语句的使用

在{% %}中进行写Python控制语句,这里使用的语法规范是Python的,在结束控制的是要使用end例如for命令结束的时候,我们要写一行{% endfor %}

{% for num in list_test %}
    {% if num > 2 %}
    {{ num }}<br>
    {% endif %}
{% endfor %}

  本文原创为CSDN博主亓官劼,原文链接为:收藏!最详细的Python全栈开发指南 看完这篇你还不会Python全栈开发 你来打我!!!,请大家支持原创,拒绝抄袭。

3.6 过滤器的基本使用

{{ var_test|upper }}

常见的过滤器

过滤器名 说明
safe 渲染时不转义
capitalize 把值的首字母大写,其余小写
lower 把值转换成小写形式
upper 把值转换成大写形式
title 把值中每个单词的首字母都转换成大写
trim 把值的首位空格去掉
striptags 渲染之前把值中所有的HTML标签删掉

3.7 使用flash传递消息

app.py

from flask import Flask,render_template,request,flash

app = Flask(__name__)
app.secret_key='this is a secret key'


@app.route('/', methods=['GET', 'POST'])
def user():
    #request请求对象,包括请求方式,数据等
    #flash需要对内容加密,因此要设置secret_key,做加密消息混淆
    if request.method == 'POST':
        username=request.form.get('username')
        password1=request.form.get('password1')
        password2=request.form.get('password2')
        if not all([username,password1,password2]):
            #return '参数不完整'
            flash('参数不完整')
        elif password1 == password2:
            #return 'true'
            flash("true")
        else:
            #return 'false'
            flash("false")
    return render_template('demo3.html')



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

demo3.html


<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Titletitle>
head>
<body>
<form method="post">
    <label>用户名:label><input type="text" name="username"><br>
    <label>密码:label><input type="password" name="password1"><br>
    <label>确认密码:label><input type="password" name="password2"><br>
    <input type="submit" value="提交"><br>

    {% for message in get_flashed_messages() %}
        {{ message }}
    {% endfor %}
form>
body>
html>

显示效果:
收藏!最详细的Python全栈开发指南 看完这篇你还不会Python全栈开发 你来打我!!!_第3张图片

3.8 使用WTF实现web表单

这里可以使用Flask-WTF扩展也可以直接使用我们HTML的

表单,都可以。

3.8.1 WTForms支持的HTML标准字段

字段类型 说明
BooleanField 复选框,值为True和False
DateField 文本字段,值为datetime.date格式
DareTimeField 文本字段,值为datetime.datetime格式
FileField 文件上传字段
HiddenField 隐藏的文本字段
MultipleFileField 多文本上传字段
FieldList 一组指定类型的字段
FloatField 文本字段,值为浮点数
FormField 把一个表单作为字段嵌入另一个表单
IntegerField 文本字段,值为整数
PasswordField 密码文本字段
RadioField 一组单选按钮
SelectField 下拉列表
SelectMultipleField 下拉列表,可选择多个值
SubmitField 表单提交按钮
StringField 文本字段
TextAreaField 多行文本字段

3.8.2 WTForms验证函数

验证函数 说明
DateRequired 确保转换类型后字段中有数据
Email 验证电子邮件地址
EqualTo 比较两个字段的值,常用于要求输入两次密码进行确认的时候
InputRequired 确保转换类型前字段中有数据
IPAddress 验证IPv4网络地址
Length 验证输入字符串的长度
MacAddress 验证MAC地址
NumberRange 验证输入的值在数字范围之内
Optional 允许字段中没有输入,将跳过其他验证函数
Regexp 使用正则表达式验证输入值
URL 验证URL
UUID 验证UUID
AnyOf 确保输入值在一组可能的值中
NoneOf 确保输入值不在一组可能的值中

3.8.3Flask-WTF扩展的安装

收藏!最详细的Python全栈开发指南 看完这篇你还不会Python全栈开发 你来打我!!!_第4张图片
在PyCharm中打开Preferences
收藏!最详细的Python全栈开发指南 看完这篇你还不会Python全栈开发 你来打我!!!_第5张图片
打开Project选项板

收藏!最详细的Python全栈开发指南 看完这篇你还不会Python全栈开发 你来打我!!!_第6张图片
进行Project Interpreter设置
收藏!最详细的Python全栈开发指南 看完这篇你还不会Python全栈开发 你来打我!!!_第7张图片

点击添加‘+’,然后搜索Flask-WTF
收藏!最详细的Python全栈开发指南 看完这篇你还不会Python全栈开发 你来打我!!!_第8张图片
选择好版本之后,Install Package,拓展包就添加完成了
收藏!最详细的Python全栈开发指南 看完这篇你还不会Python全栈开发 你来打我!!!_第9张图片


在项目中导入Flask-WTF

from flask_wtf import FlaskForm

在项目中导入Flaks-WTF的HTML标准字段

from wtforms import StringField,PasswordField,SubmitField

在项目中导入Flask-WTF的验证函数

from wtforms.validators import DataRequired, EqualTo

在使用Flask-WTF拓展的时候,需要使用CSRF Token
在html页面中加入

{{ html_form.csrf_token() }}

3.8.4使用Flask-WTF创建表单示例

app.py

from flask import Flask, render_template, request, flash
from flask_wtf import FlaskForm
from wtforms import StringField, PasswordField, SubmitField
from wtforms.validators import DataRequired, EqualTo

app = Flask(__name__)

app.secret_key = "dsauhfabf"


class LoginForm(FlaskForm):
    username = StringField('用户名', validators=[DataRequired()])
    password1 = PasswordField('密码', validators=[DataRequired()])
    password2 = PasswordField('确认密码', validators=[DataRequired(), EqualTo('password1', "两次密码填入不一致")])
    submit = SubmitField('提交')


@app.route('/form', methods=['GET', 'POST'])
def login():
    login_form = LoginForm()
    if request.method == 'POST':
        username = request.form.get('username')
        password1 = request.form.get('password1')
        password2 = request.form.get('password2')
        if login_form.validate_on_submit():
            return 'success'
        else:
            flash("false")

    return render_template('demo3.html', html_form=login_form)


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

demo3.html


<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Titletitle>
head>
<body>
<form method="post">
    <br>
    {{ html_form.csrf_token() }}
    {{ html_form.username.label }}{{ html_form.username }}<br>
    {{ html_form.password1.label }}{{ html_form.password1 }}<br>
    {{ html_form.password2.label }}{{ html_form.password2 }}<br>
    {{ html_form.submit }}<br>

    {% for message in get_flashed_messages() %}
        {{ message }}
    {% endfor %}
form>
body>
html>

页面效果:
收藏!最详细的Python全栈开发指南 看完这篇你还不会Python全栈开发 你来打我!!!_第10张图片
收藏!最详细的Python全栈开发指南 看完这篇你还不会Python全栈开发 你来打我!!!_第11张图片
收藏!最详细的Python全栈开发指南 看完这篇你还不会Python全栈开发 你来打我!!!_第12张图片
收藏!最详细的Python全栈开发指南 看完这篇你还不会Python全栈开发 你来打我!!!_第13张图片

3.9 Flask-SQLAlchemy拓展

使用Flask-SQLAichemy可以处理Python对象,而不是数据实体。这里我们也可以在项目中直接使用sql语句进行操纵数据库。

3.9.1 Flask-SQLAlchemy拓展安装方法:

  • 通过PyCharm的project interpret 继续安装
  • 通过命令行进行安装
 pip install flask-sqlalchemy

在Flask-SQLAlchemy中,数据库使用URL进行指定,MySQL的URL格式为:

mysql://username:password@hostname/datebase

例如 mysql://root:[email protected]:3306/demoDatabse

安装pymysql
pip install pymysql

3.9.2 配置数据库:

from flask_sqlalchemy import SQLAlchemy
app.config['SQLALCHEMY_DATABASE_URI']= 'mysql+pymysql://root:[email protected]:3306/Demo'
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
db = SQLAlchemy(app)

3.9.3创建数据库类,映射数据库表

class ClassInfo(db.Model):
    __tablename__ = 'classinfo'
    id = db.Column(db.Integer(), primary_key=True)
    className = db.Column(db.String(20))
    classSize = db.Column(db.Integer())

注意,其中的类型的第一个字母要大写

  本文原创为CSDN博主亓官劼,原文链接为:收藏!最详细的Python全栈开发指南 看完这篇你还不会Python全栈开发 你来打我!!!,请大家支持原创,拒绝抄袭。

3.9.4在视图中操作数据库查询

通过数据库类的query对象中的方法进行查询

mylist = ClassInfo.query.all()
    print(mylist)
    for it in mylist:
        print(it)
    test = mylist[0].className
    return render_template('demo3.html', html_form=login_form, test=test)

3.9.5 常用的SQLAlchemy查询过滤器

过滤器 说明
filter() 把过滤器添加到原查询上,返回一个新查询
filter_by() 把等值过滤器添加到原查询上,返回一个新查询
limit() 使用指定的值限制原查询返回的结果数量,返回一个新查询
offset() 偏移原查询返回的结果,返回一个新查询
order_by() 根据指定条件对原查询进行排序,返回一个新查询
group_by() 根据指定调教对原查询结果进行分组,返回一个新查询

3.9.6常用SQLAlchemy查询执行方法

方法 说明
all() 以列表形式返回查询的所有结果
first() 返回查询的第一个结果,如果没有结果,则返回None
first_or_404() 返回查询的第一个结果,如果没有结果,则终止请求,返回4040错误响应
get() 返回指定主键对应的行,如果没有对应的行,则返回NOne
get_or_404 返回指定主键对应的行,如果没找到指定的主键,则终止请求,返回404错误响应
count() 返回查询结果的数量
paginate() 返回一个Paginate对象,包含指定范围内的结果

3.10 使用werkzeug计算密码散列值

  在项目中我们一般都需要进行注册、登录账户,而账户密码又是一个非常重要的用户信息,我们一般都需要对他进行加密处理,这里我们在werkzeug中的security模块实现了密码散列值的计算。共有两个函数generate_password_hash(password, method='pbkdf2:sha256', salt_length=8)
这个函数的输入为原始密码,返回密码散列值的字符串形式,供存入用户数据库。
check_password_hash(hash,password)
这个函数的两个参数为,数据库中存放的密码hash值,和用户输入的密码,如果一致,返回True,如果不一致,返回False。
这里是采用的128位的散列加密,所以我们在创建数据库表的时候要注意我们字段的类型。

3.11 文件的上传、下载

3.11.1 上传文件

html代码:


<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>上传文件title>
head>
<body>
<div>
    <form action="" method="post" enctype="multipart/form-data">
        <table>
                <tr>
                    <td>
                        选择你需要上传的文件
                    td>
                    <td>
                        <input type="file" name="file">
                    td>
                    <td>
                        <input type="submit">
                    td>
                tr>
        table>

    form>
div>
body>
html>

要注意,在中要设置属性enctype="multipart/form-data"

3.11.2 在后台接收文件并保存

在后台接受文件使用

uoload_file = request.files.get('file')

进行获取,get(‘’)中参数是前端页面中的input的name

3.11.3 在后台保存文件

可以使用save()进行保存

upload_file = request.files.get('file')
file_path = 'store'
wenjian.save(os.path.join(file_path,upload.filename))

可以secure_filename()生成随机文件名,在werkzeug.utils中,也可以手动设置文件名,加安全性

  本文原创为CSDN博主亓官劼,原文链接为:收藏!最详细的Python全栈开发指南 看完这篇你还不会Python全栈开发 你来打我!!!,请大家支持原创,拒绝抄袭。

3.12 Flask 蓝图

蓝图的作用是使Flask项目更加的模块化,结构更清晰。可以将相同的模块视图函数放在同一个蓝图下,同一个文件中,方便管理。

3.12.1 蓝图的基本用法

  • 在蓝图文件中导入Blueprint:
from flask import Blueprint
  • 初始化蓝图:例如user蓝图
user_bp = Blueprint('user',__name__)
  • 在主app文件中注册蓝图:
    例如user_bp蓝图在Blueprint/user.py文件中,注册函数为下
from flask import Flask
from flask import Blueprint
from Blueprint.user import user_bp
app = Flask(__name__)
app.register_blueprint(user_bp)
  • 给每个蓝图添加前缀
    要给某个蓝图下的所有route添加各自的前缀,可以在初始化蓝图的时候设置:例如给user_bp的每个route前添加/user前缀
from flask import Blueprint
user_bp = Blueprint('user',__name__,url_prefix='/user')

  到这里为止,我们第Flask基本知识部分就技术了,这里面涵盖了一些Flask的一些基本的语法和应用,下面我们就要开始正式的进入我们的实战系列教程了。

博主的站内博客导航:博客文章内容导航(实时更新)
更多精彩博客文章推荐

  • 一本教你如何在前端实现富文本编辑器
  • 小白都能看得懂的教程 一本教你如何在前端实现markdown编辑器
  • 一文教会你Bootstrap,让你也可以快速建站
  • 一文教你如何白嫖JetBrains全家桶(IDEA/PtChram/CLion)免费正版

四、导航栏、注册、登录、注销、主页功能的实现

  下面来到我们正式的实战环节了,在第四部分我们将一起实现导航栏、注册、登录、注销、主页功能,手把手的带你来完成这些功能的实现。
更多博主的文章可以查看博主的站内导航博客文章内容导航(实时更新)
本项目所有源码在GitHub开源,GitHub地址为:OnlineForumPlatform
  有需要源码可以前去查看,喜欢的话可以star一下。在做完准备工作之后,我们就正式的开始开发了。博主将带领你实现在线论坛系统的导航条、注册、登录和主页功能,在实现的同时会讲解各个功能实现的原理,手把手的教你进入Python Web全栈开发,一个字一个字的代码完成本项目。

4.1 导航栏实现

我们首先从导航栏开始开发,每个页面需要有导航栏,可以说一个非常常用的组件了。这里我们先创建一个hmtl页面base.html进行开发导航栏相关功能,将文件建立在templates文件夹中。
在这里先说明一下为什么要使用base.html这个名字,因为Jinja2模板是支持继承机制的,而导航栏又是几乎每个页面都需要使用到的一个组件,所以我们这里将导航栏这个文件base.html作为一个基类,其他所有的视图文件都继承自它,并在它的基础上进行重写相关的内容,实现各个视图的不同内容。
在创建完文件之后,由于我们这个项目前端UI部分使用Bootstrap进行快速建站,还有不会Bootstarp的同学可以看一下之前的这篇教程:什么?你还不会Bootstrap?一文教会你Bootstrap,让你也可以快速建站

我们这里选择一个3.4.0的版本,在html中引入:

<script src="https://cdn.bootcss.com/jquery/3.4.1/jquery.js">script>
<script src="https://cdn.bootcss.com/twitter-bootstrap/3.4.0/js/bootstrap.min.js">script>
<link href="https://cdn.bootcss.com/twitter-bootstrap/3.4.0/css/bootstrap.min.css" rel="stylesheet">

在引用完成之后,我们先去bootstrap样式库中找一个喜欢的导航条样式,然后加入到我们的base.html中。
我们这里使用这个样式的,直接加入到base.html中:

<nav class="navbar navbar-default">
  <div class="container-fluid">
    
    <div class="navbar-header">
      <button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#bs-example-navbar-collapse-1" aria-expanded="false">
        <span class="sr-only">Toggle navigationspan>
        <span class="icon-bar">span>
        <span class="icon-bar">span>
        <span class="icon-bar">span>
      button>
      <a class="navbar-brand" href="#">Branda>
    div>

    
    <div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1">
      <ul class="nav navbar-nav">
        <li class="active"><a href="#">Link <span class="sr-only">(current)span>a>li>
        <li><a href="#">Linka>li>
        <li class="dropdown">
          <a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false">Dropdown <span class="caret">span>a>
          <ul class="dropdown-menu">
            <li><a href="#">Actiona>li>
            <li><a href="#">Another actiona>li>
            <li><a href="#">Something else herea>li>
            <li role="separator" class="divider">li>
            <li><a href="#">Separated linka>li>
            <li role="separator" class="divider">li>
            <li><a href="#">One more separated linka>li>
          ul>
        li>
      ul>
      <form class="navbar-form navbar-left">
        <div class="form-group">
          <input type="text" class="form-control" placeholder="Search">
        div>
        <button type="submit" class="btn btn-default">Submitbutton>
      form>
      <ul class="nav navbar-nav navbar-right">
        <li><a href="#">Linka>li>
        <li class="dropdown">
          <a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false">Dropdown <span class="caret">span>a>
          <ul class="dropdown-menu">
            <li><a href="#">Actiona>li>
            <li><a href="#">Another actiona>li>
            <li><a href="#">Something else herea>li>
            <li role="separator" class="divider">li>
            <li><a href="#">Separated linka>li>
          ul>
        li>
      ul>
    div>
  div>
nav>

插入之后我们打开网页就可以看到如下效果:
在这里插入图片描述

然后我们在根据我们的需求,对这个导航条进行一定的更改,并且为base.html页面划分几个block。
暂时先修改为这样,后续我们加完导航之后,还需再对各个标定的页面进行一个修改。修改之后的完整代码为:


<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>
        {% block title %}
{#        其他页面重写标题的地方#}
        {% endblock %}
    title>
    {% block css %}
{#    其他页面引用样式或者js的地方#}
    {% endblock %}
    <script src="https://cdn.bootcss.com/jquery/3.4.1/jquery.js">script>
    <script src="https://cdn.bootcss.com/twitter-bootstrap/3.4.0/js/bootstrap.min.js">script>
    <link href="https://cdn.bootcss.com/twitter-bootstrap/3.4.0/css/bootstrap.min.css" rel="stylesheet">
head>
<body>
    <div class="navigation_bar">
        <nav class="navbar navbar-default">
          <div class="container-fluid">
{#              由于这里我们不需要使用商标,所以对Bran部分进行了删除#}
            
            <div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1">
              <ul class="nav navbar-nav">
                <li class="active"><a href="#">首页<span class="sr-only">(current)span>a>li>
                <li><a href="#">Linka>li>
                <li class="dropdown">
                  <a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false">Dropdown <span class="caret">span>a>
                  <ul class="dropdown-menu">
                    <li><a href="#">Actiona>li>
                    <li><a href="#">Another actiona>li>
                    <li><a href="#">Something else herea>li>
                    <li role="separator" class="divider">li>
                    <li><a href="#">Separated linka>li>
                    <li role="separator" class="divider">li>
                    <li><a href="#">One more separated linka>li>
                  ul>
                li>
              ul>
              <form class="navbar-form navbar-left">
                <div class="form-group">
                  <input type="text" class="form-control" placeholder="Search">
                div>
                <button type="submit" class="btn btn-default">Submitbutton>
              form>
              <ul class="nav navbar-nav navbar-right">
                <li><a href="#">Linka>li>
                <li class="dropdown">
                  <a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false">Dropdown <span class="caret">span>a>
                  <ul class="dropdown-menu">
                    <li><a href="#">Actiona>li>
                    <li><a href="#">Another actiona>li>
                    <li><a href="#">Something else herea>li>
                    <li role="separator" class="divider">li>
                    <li><a href="#">Separated linka>li>
                  ul>
                li>
              ul>
            div>
          div>
        nav>
    div>
    <div>
        {% block content %}
{#        其他页面重写页面内容的地方#}
        {% endblock %}
    div>
body>
html>

这里暂时加了3个block,分别是title:标题。css:引用css样式和js脚本。content:页面主题内容部分。
导航栏的剩下内容将会在项目开发的过程中逐渐完善,因为它涉及多个页面,下面我们进行下一项功能页面。

4.2 注册功能实现

4.2.1 注册功能实现-数据库

在实现注册功能的时候,我们首先就需要在数据库中创建一个表来存储我们的注册信息了。这个项目预设2端(普通用户端和管理员端),那么我们表中需要存储用户的信息有:用户名(这里使用邮箱),昵称,密码,用户的权限,注册时间,联系方式等信息,我们这里暂定收集用户名,昵称,密码,用户的权限,注册时间,联系方式,这6种信息用于注册,下面我们创建一个UserInformation表。这里可以使用pychram右面的Database使用图像界面创建,也可以使用命令行创建。

在这里插入图片描述SQL语句为:

create table UserInformation
(
	email varchar(128) not null,
	nickname nvarchar(100) default '未设置昵称' null,
	password varchar(128) not null,
	type int default '0' null,
	create_time datetime default '1999-9-9 9:9:9' not null,
	phone varchar(128) null,
	constraint UserInformation_pk
		primary key (email)
);

这样我们一个用于存储用户信息的表就创建好了,下面我们来设计一个注册的前端页面。

  本文原创为CSDN博主亓官劼,原文链接为:收藏!最详细的Python全栈开发指南 看完这篇你还不会Python全栈开发 你来打我!!!,请大家支持原创,拒绝抄袭。

4.2.2 注册功能实现-前端

在写前端页面之前,由于我们每个页面都是继承自base.html,因此我们可以写个extend.html来方便我们每次进行继承,在此基础上进行开发。
extend.html页面代码为:

{% extends 'base.html' %}

{% block title %}

{% endblock %}

{% block css %}

{% endblock %}

{% block content %}

{% endblock %}

下面我们就正式的开始写register.html页面了。
首先我们去bootstrap样式库中去找个页头,为页面添加一个大标题。

<div class="page-header">
  <h1>Example page header <small>Subtext for headersmall>h1>
div>

为了方便我们实时的调试我们的页面,我们先在app.py中为注册页面添加一个路由:

from flask import *

app = Flask(__name__)


@app.route('/')
def hello_world():
    return render_template('base.html')


# 注册页面
@app.route('/register')
def register():
    return render_template('register.html')


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

这样我们访问http://127.0.0.1:5000/register就可以实时查看我们页面的信息了,方便我们对UI部分进行调试,设计一个自己喜欢的UI。在引入页头之后,我们访问发现页面有点差强人意,左对齐一点也不好看,我们可以让他居中。我们在static/css文件夹中创建一个register.css来为register.html提供样式。创建完之后我们在register.html中引入这个css。

<link rel="stylesheet" href="/static/css/register.css">

下面我们就来选择一个表单进行设计一个注册页面。这里调整UI部分比较简单并且繁琐,所以就不一步一步的记录了,在调整完之后贴上完整的代码,给大家看一下整体的效果。

初步设计之后,register.css:

#page_header{
    text-align: center;
}

.register_content{
    /*调整边距,调到相对中间的位置*/
    margin:10% 25%;
}

#register_butt{
    text-align: center;
}

register.html:

{% extends 'base.html' %}

{% block title %}
注册
{% endblock %}

{% block css %}
<link rel="stylesheet" href="/static/css/register.css">
{% endblock %}

{% block content %}
<div class="register_content">
    <div class="page-header" id="page_header">
      <h1>注册<small>Registersmall>h1>
    div>
    <div id="register_form">
        <form method="post">
          <div class="form-group">
            <label for="exampleInputEmail1">Email addresslabel>
            <input type="email" class="form-control" name="email" id="exampleInputEmail1" placeholder="Email address">
          div>
          <div class="form-group">
            <label for="exampleInputEmail1">昵称label>
            <input type="text" class="form-control" name="nickname" id="exampleInputEmail1" placeholder="昵称">
          div>
          <div class="form-group">
            <label for="exampleInputPassword1">密码label>
            <input type="password" class="form-control" name="password_1" id="exampleInputPassword1" placeholder="密码">
          div>
            <div class="form-group">
            <label for="exampleInputPassword1">确认密码label>
            <input type="password" class="form-control" name="password_2" id="exampleInputPassword1" placeholder="确认密码">
          div>
          <div class="form-group">
            <label for="exampleInputEmail1">联系方式label>
            <input type="text" class="form-control" name="phone" id="exampleInputEmail1" placeholder="联系方式">
          div>
          <div id="register_butt">
              <button type="submit" class="btn btn-default">注册button>
              <button type="button" class="btn btn-default" onclick="location.href='#'">登录button>
          div>
        form>
    div>

div>
{% endblock %}

页面效果为:
在这里插入图片描述
整体还是简洁大方的,下面我们在针对注册页面修改一下导航条,在导航条中添加到注册页面的导航,并且添加一个block来标定上面选取的页面。
修改之后的base.html为:


<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>
        {% block title %}
{#        其他页面重写标题的地方#}
        {% endblock %}
    title>
    {% block css %}
{#    其他页面引用样式或者js的地方#}
    {% endblock %}
    <script src="https://cdn.bootcss.com/jquery/3.4.1/jquery.js">script>
    <script src="https://cdn.bootcss.com/twitter-bootstrap/3.4.0/js/bootstrap.min.js">script>
    <link href="https://cdn.bootcss.com/twitter-bootstrap/3.4.0/css/bootstrap.min.css" rel="stylesheet">
head>
<body>
    <div class="navigation_bar">
        <nav class="navbar navbar-default">
          <div class="container-fluid">
{#              由于这里我们不需要使用商标,所以对Bran部分进行了删除#}
            
            <div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1">
              <ul class="nav navbar-nav">
                <li class="{% block homepage_class %}{% endblock %}"><a href="#">首页<span class="sr-only">(current)span>a>li>
                <li><a href="#">Linka>li>
                <li class="dropdown">
                  <a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false">Dropdown <span class="caret">span>a>
                  <ul class="dropdown-menu">
                    <li><a href="#">Actiona>li>
                    <li><a href="#">Another actiona>li>
                    <li><a href="#">Something else herea>li>
                    <li role="separator" class="divider">li>
                    <li><a href="#">Separated linka>li>
                    <li role="separator" class="divider">li>
                    <li><a href="#">One more separated linka>li>
                  ul>
                li>
              ul>
              <form class="navbar-form navbar-left">
                <div class="form-group">
                  <input type="text" class="form-control" placeholder="Search">
                div>
                <button type="submit" class="btn btn-default">Submitbutton>
              form>
              <ul class="nav navbar-nav navbar-right">
                <li class="{% block register_class %}{% endblock %}"><a href="{{ url_for('register') }}">注册a>li>
                <li><a href="#">登录a>li>
{#                <li class="dropdown">#}
{#                  <a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false">Dropdown <span class="caret">span>a>#}
{#                  <ul class="dropdown-menu">#}
{#                    <li><a href="#">Actiona>li>#}
{#                    <li><a href="#">Another actiona>li>#}
{#                    <li><a href="#">Something else herea>li>#}
{#                    <li role="separator" class="divider">li>#}
{#                    <li><a href="#">Separated linka>li>#}
{#                  ul>#}
{#                li>#}
              ul>
            div>
          div>
        nav>
    div>
    <div>
        {% block content %}
{#        其他页面重写页面内容的地方#}
        {% endblock %}
    div>
body>
html>

同时在对register.html中进行添加block register_class,添加完成之后的register.html为:

{% extends 'base.html' %}

{% block title %}
注册
{% endblock %}

{% block css %}
<link rel="stylesheet" href="/static/css/register.css">
{% endblock %}

{% block content %}
<div class="register_content">
    <div class="page-header" id="page_header">
      <h1>注册<small>Registersmall>h1>
    div>
    <div id="register_form">
        <form method="post">
          <div class="form-group">
            <label for="exampleInputEmail1">Email addresslabel>
            <input type="email" class="form-control" name="email" id="exampleInputEmail1" placeholder="Email address">
          div>
          <div class="form-group">
            <label for="exampleInputEmail1">昵称label>
            <input type="text" class="form-control" name="nickname" id="exampleInputEmail1" placeholder="昵称">
          div>
          <div class="form-group">
            <label for="exampleInputPassword1">密码label>
            <input type="password" class="form-control" name="password_1" id="exampleInputPassword1" placeholder="密码">
          div>
            <div class="form-group">
            <label for="exampleInputPassword1">确认密码label>
            <input type="password" class="form-control" name="password_2" id="exampleInputPassword1" placeholder="确认密码">
          div>
          <div class="form-group">
            <label for="exampleInputEmail1">联系方式label>
            <input type="text" class="form-control" name="phone" id="exampleInputEmail1" placeholder="联系方式">
          div>
          <div id="register_butt">
              <button type="submit" class="btn btn-default">注册button>
              <button type="button" class="btn btn-default" onclick="location.href='#'">登录button>
          div>
        form>
    div>

div>
{% endblock %}

{% block register_class %}
active
{% endblock %}

调整之后的注册页面为:
在这里插入图片描述
当在注册页面时,会将注册的导航着重显示。同时在导航条上添加了注册和登录页面的导航。

4.2.3 注册功能实现-后端

下面我们就来实现注册的服务器端功能,将前端发送的信息检查,如果不正确则返回提示,如果正确则将用户信息存储到数据库中。
既然要使用到数据库,那我们就先来配置一下config.py,这个文件主要存放项目的配置信息。
这里我们设置一下SECRET_KEY和db。
config.py:

# encoding:utf-8
import os
import pymysql

DEBUG = False

SECRET_KEY = os.urandom(24)

db = pymysql.connect(host='localhost', user='root', password='password1q!', db='OnlineForumPlatform', port=3306)

配置完成之后我们在app.py中导入config.py,并且绑定配置。

from flask import *
import config

app = Flask(__name__)

# 从对象中导入config
app.config.from_object(config)

然后我们开始写注册的后端逻辑功能。本系列第一次写后端代码,这里做一个详细的说明,有Flask基础不错的同学可以直接跳到后面的整体代码。
由于我们路由默认的请求方式只有一种是GET请求,但是我们注册的话,为了安全,form表单一般使用POST请求,因此我们这里先设置请求方式:

@app.route('/register',methods=['GET','POST'])

然后我们再获取前端表单的信息:

email = request.form.get('email')
nickname = request.form.get('nickname')
password_1 = request.form.get('password_1')
password_2 = request.form.get('password_2')
phone = request.form.get('phone')

在获取完之后我们就需要对这些数据进行处理了,首先我们要检查数据是否完整,如果信息填写不完整肯定是不可以注册的,我们返回提示,这里我们使用flash传递提示回到注册页面。

        if not all([email,nickname,password_1,password_2,phone]):
            flash("信息填写不全,请将信息填写完整")
            return render_template('register.html')

既然使用flash进行传递消息,那我们就需要在前端将flash消息显示出来。我们将这段代码放到前端合适的一个位置,这个位置自己选择一个显眼的位置即可。这里我讲这个信息提示放在了页头的下方。

<span style=" font-size:20px;color: red" >
	{% for item in get_flashed_messages() %}
		{{ item }}
	{% endfor %}
span>

下面继续来完善我们的后端,信息填写完整之后,我们还需要验证两次密码输入的是否一致,如果不一致,则需要返回错误提示。

if password_1 != password_2:
	flash("两次密码填写不一致!")
	return render_template('register.html')

如果信息都填写正确了,那下面我们开始对密码进行加密,我们这里使用的是pbkdf2:sha256加密方式,对密码进行128位的散列加密,可以极大的保护用户信息的安全性。要使用这个加密,我们需要导入它:

from werkzeug.security import generate_password_hash, check_password_hash

然后我们使用generate_password_hash来加密我们的密码,由于password_1和password_2是一样的,那么我们只需要加密password_1即可:

password = generate_password_hash(password_1, method="pbkdf2:sha256", salt_length=8)

这样我们的准备工作就完成了,下面我们开始将信息存储到数据库中:
首先我们要将我们config文件中配置的db导入进来:

from config import db

然后我们来获取db的cursor,来进行相关数据库操作,这里我们使用Python直接操纵数据库,当然,大家也可以使用Flask-SQLAlchemy来进行操作数据库,这里就使用Python直接操纵数据库的方式进行。
首先我们要检查我们的数据库中email是否存在,即当前用户是否已经存在,我们使用email进行唯一表示用户,所以不允许重复,我们还需要检查一下email。

try:
	cur = db.cursor()
	sql = "select * from UserInformation where email = '%s'"%email
    db.ping(reconnect=True)
    cur.execute(sql)
 	result = cur.fetchone()
	if result is not None:
		flash("该Email已存在!")
 		return render_template('register.html')
except Exception as e:
	raise e

这里注册的用户类型我们肯定是不可以注册管理员的,不然这个管理员就是形同虚设了,我们这里通过register页面来注册的用户,我们统一都是普通用户,这里type置为0。然后创建时间我们使用服务器端来获取当前的时间。这里要使用time库,需要导入

import time
create_time = time.strftime("%Y-%m-%d %H:%M:%S")

然后将数据插入数据库中,同时在插入完成之后我们应该返回首页,并且为登录状态,由于我们这里首页和登录都还没有实现,所以这里我们先写一个空的首页路由,并且创建一个index.html,使用路由返回index.html页面。
index.html我们先这样空置,等在本讲的最后我们来实现这个首页。
index.html:

{% extends 'base.html' %}

{% block title %}
主页
{% endblock %}

{% block css %}

{% endblock %}

{% block content %}

{% endblock %}

完成注册服务器端功能之后的app.py全部代码为:

from flask import *
from werkzeug.security import generate_password_hash, check_password_hash
from config import db
import time
import config

app = Flask(__name__)

# 从对象中导入config
app.config.from_object(config)



@app.route('/')
def index():
    return render_template('index.html')


# 注册页面
@app.route('/register',methods=['GET','POST'])
def register():
    if request.method == 'GET':
        return render_template('register.html')
    if request.method == 'POST':
        email = request.form.get('email')
        nickname = request.form.get('nickname')
        password_1 = request.form.get('password_1')
        password_2 = request.form.get('password_2')
        phone = request.form.get('phone')
        if not all([email,nickname,password_1,password_2,phone]):
            flash("信息填写不全,请将信息填写完整")
            return render_template('register.html')
        if password_1 != password_2:
            flash("两次密码填写不一致!")
            return render_template('register.html')
        password = generate_password_hash(password_1, method="pbkdf2:sha256", salt_length=8)
        try:
            cur = db.cursor()
            sql = "select * from UserInformation where email = '%s'"%email
            db.ping(reconnect=True)
            cur.execute(sql)
            result = cur.fetchone()
            if result is not None:
                flash("该Email已存在!")
                return render_template('register.html')
            else:
                create_time = time.strftime("%Y-%m-%d %H:%M:%S")
                sql = "insert into UserInformation(email, nickname, password, type, create_time, phone) VALUES ('%s','%s','%s','0','%s','%s')" %(email,nickname,password,create_time,phone)
                db.ping(reconnect=True)
                cur.execute(sql)
                db.commit()
                cur.close()
                return redirect(url_for('index'))
        except Exception as e:
            raise e

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

这样我们的注册功能就全部实现啦!可以先去注册一个自己的测试账号,方便我们后面的各项功能测试呦!

4.3 登录功能实现

下面我们就来实现登录功能

4.3.1 登录功能实现-前端

登录的话,前端和我们的注册类似,上面一个页头,然后下面使用一个表单,并且设置一个block用来标记一下当前的页面即可,在注册的前端设计的时候已经有详细的说明,这里就不在赘述了,创建一个login.html,下面废话不过说,直接上代码:
首先我们在app.py中写一个空的路由,方便我们在前端页面中使用url_for来重定向页面:

# 注册页面
@app.route('/login')
def login():
    return render_template('login.html')

然后我们创建一个login.css来设置页面的样式:

#page_header{
    text-align: center;
}

.login_content{
    /*调整边距,调到相对中间的位置*/
    margin:10% 30%;
}

#login_butt{
    text-align: center;
}

login.html部分为:

{% extends 'base.html' %}

{% block title %}
登录
{% endblock %}

{% block css %}
<link rel="stylesheet" href="/static/css/login.css">
{% endblock %}

{% block content %}
<div class="login_content">
    <div class="page-header" id="page_header">
      <h1>登录<small>Loginsmall>h1>
    div>
    <div id="login_form">
        <form method="post">
            <span style=" font-size:20px;color: red" >
                {% for item in get_flashed_messages() %}
                {{ item }}
                {% endfor %}
            span>
          <div class="form-group">
            <label for="exampleInputEmail1">Email addresslabel>
            <input type="email" class="form-control" name="email" id="exampleInputEmail1" placeholder="Email address">
          div>
          <div class="form-group">
            <label for="exampleInputPassword1">密码label>
            <input type="password" class="form-control" name="password" id="exampleInputPassword1" placeholder="密码">
          div>
          <div id="login_butt">
              <button type="submit" class="btn btn-default">登录button>
              <button type="button" class="btn btn-default" onclick="location.href='{{ url_for('register') }}'">注册button>
          div>
        form>
    div>
div>
{% endblock %}

{% block login_class %}
active
{% endblock %}

在登录页面完成之后,我们同时修改一下导航条的内容,在base.html中,将首页的导航修改为:

 <li class="{% block login_class %} {% endblock %}"><a href="{{ url_for('login') }}">登录a>li>

如果不修改base的话,直接运行上面的html修改之后的页面会报错,因为在login.html中使用了{% block login_class %} {% endblock %},这个是刚刚在base.html中添加的block,用于标记当前页面。

修改完成之后我们的登录页面效果为:
在这里插入图片描述

4.3.2 登录功能实现-后端

登录功能的后端我们主要分为两部分,一个是接受表单并验证表单信息的正确性,并进行反馈,第二部分就是登录状态的保持,下面我们将分这两部分来实现后端的功能。

  本文原创为CSDN博主亓官劼,原文链接为:收藏!最详细的Python全栈开发指南 看完这篇你还不会Python全栈开发 你来打我!!!,请大家支持原创,拒绝抄袭。

4.3.2.1 登录功能实现-后端-验证登录信息

这里同样的,我们登录的表单为了安全性,一般都使用POST来发送,这里先设置允许的请求方式。

@app.route('/login',methods=['GET','POST'])

然后我们来获取前端的Email和密码:

# 登录页面
@app.route('/login',methods=['GET','POST'])
def login():
    if request.method == 'GET':
        return render_template('login.html')
    if request.method == 'POST':
        email = request.form.get('email')
        password = request.form.get('password')

然后为了防止出错,我们先验证数据的完整性:

if not all([email,password]):
    flash("请将信息填写完整!")
    return render_template('login.html')

然后我们来验证密码是否正确,首先我们先从数据库中获取当前登录email的密码,并验证它,如果密码不存,则说明该用户不存在,即未注册,我们返回提示。如果密码存在,那么我就获取密码,然后使用check_password_hash()函数来验证它,如果密码正确,我们将用户名(即email)放入session中,方便我们下面进行实现登录状态保持的功能。
代码为:

# 登录页面
@app.route('/login',methods=['GET','POST'])
def login():
    if request.method == 'GET':
        return render_template('login.html')
    if request.method == 'POST':
        email = request.form.get('email')
        password = request.form.get('password')
        if not all([email,password]):
            flash("请将信息填写完整!")
            return render_template('login.html')
        try:
            cur = db.cursor()
            sql = "select password from UserInformation where email = '%s'" % email
            db.ping(reconnect=True)
            cur.execute(sql)
            result = cur.fetchone()
            if result is None:
                flash("该用户不存在")
                return render_template('login.html')
            if check_password_hash(result[0],password):
                session['email'] = email
                session.permanent = True
                return redirect(url_for('index'))
            else:
                flash("密码错误!")
                return render_template('login.html')
        except Exception as e:
            raise e

这样我们的登录的基本功能就实现了,大家可以去测试下,如果登录正常则会返回首页,如果登录失败则会显示各种各样的提示。下面我们就要实现登录的另一个功能,登录状态保持,我们登录之后如何让系统一直保持我们的登录状态呢?

4.3.2.2 登录功能实现-后端-登录状态保持

要实现登录状态保持,我们这里可以使用上下文钩子函数来一直保持登录状态。

# 登录状态保持
@app.context_processor
def login_status():
    # 从session中获取email
    email = session.get('email')
    # 如果有email信息,则证明已经登录了,我们从数据库中获取登陆者的昵称和用户类型,来返回到全局
    if email:
        try:
            cur = db.cursor()
            sql = "select nickname,type from UserInformation where email = '%s'" % email
            db.ping(reconnect=True)
            cur.execute(sql)
            result = cur.fetchone()
            if result:
                return {'email':email,'nickname':result[0] ,'user_type':result[1]}
        except Exception as e:
            raise e
    # 如果email信息不存在,则未登录,返回空
    return {}

有了登录状态保持之后,我们就可以再次的修改导航条,将右侧的注册登录进行修改,当未登录时,显示登录注册,当已登录的时候,显示注销和{ 用户昵称 },用户昵称处我们可以使用下拉列表,等后期我们会在此添加功能。
修改之后的导航栏右面内容为:

{% if email %}
    <li class="{% block login_out_class %}{% endblock %}"><a href="{{ url_for('register') }}">注销a>li>
    <li class="dropdown">
      <a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false">{{ nickname }} <span class="caret">span>a>
      <ul class="dropdown-menu">
        <li><a href="#">Actiona>li>
        <li><a href="#">Another actiona>li>
        <li><a href="#">Something else herea>li>
        <li role="separator" class="divider">li>
        <li><a href="#">Separated linka>li>
      ul>
    li>
{% else %}
    <li class="{% block register_class %}{% endblock %}"><a href="{{ url_for('register') }}">注册a>li>
    <li class="{% block login_class %} {% endblock %}"><a href="{{ url_for('login') }}">登录a>li>
{% endif %}

这样我们登录之后的页面效果为:在这里插入图片描述
为了防止小伙伴不知道上面的这段代码到底在哪里修改,这里再贴一下修改后的base.html的全部代码:这里的注销的链接还没有写,在等会我们实现了注销的功能之后再在这里添加链接。


<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>
        {% block title %}
{#        其他页面重写标题的地方#}
        {% endblock %}
    title>
    {% block css %}
{#    其他页面引用样式或者js的地方#}
    {% endblock %}
    <script src="https://cdn.bootcss.com/jquery/3.4.1/jquery.js">script>
    <script src="https://cdn.bootcss.com/twitter-bootstrap/3.4.0/js/bootstrap.min.js">script>
    <link href="https://cdn.bootcss.com/twitter-bootstrap/3.4.0/css/bootstrap.min.css" rel="stylesheet">
head>
<body>
    <div class="navigation_bar">
        <nav class="navbar navbar-default">
          <div class="container-fluid">
{#              由于这里我们不需要使用商标,所以对Bran部分进行了删除#}
            
            <div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1">
              <ul class="nav navbar-nav">
                <li class="{% block homepage_class %}{% endblock %}"><a href="{{ url_for('index') }}">首页<span class="sr-only">(current)span>a>li>
                <li><a href="#">Linka>li>
                <li class="dropdown">
                  <a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false">Dropdown <span class="caret">span>a>
                  <ul class="dropdown-menu">
                    <li><a href="#">Actiona>li>
                    <li><a href="#">Another actiona>li>
                    <li><a href="#">Something else herea>li>
                    <li role="separator" class="divider">li>
                    <li><a href="#">Separated linka>li>
                    <li role="separator" class="divider">li>
                    <li><a href="#">One more separated linka>li>
                  ul>
                li>
              ul>
              <form class="navbar-form navbar-left">
                <div class="form-group">
                  <input type="text" class="form-control" placeholder="Search">
                div>
                <button type="submit" class="btn btn-default">Submitbutton>
              form>
              <ul class="nav navbar-nav navbar-right">
                {% if email %}
                    <li class=""><a href="{{ url_for('register') }}">注销a>li>
                    <li class="dropdown">
                      <a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false">{{ nickname }} <span class="caret">span>a>
                      <ul class="dropdown-menu">
                        <li><a href="#">Actiona>li>
                        <li><a href="#">Another actiona>li>
                        <li><a href="#">Something else herea>li>
                        <li role="separator" class="divider">li>
                        <li><a href="#">Separated linka>li>
                      ul>
                    li>
                {% else %}
                    <li class="{% block register_class %}{% endblock %}"><a href="{{ url_for('register') }}">注册a>li>
                    <li class="{% block login_class %} {% endblock %}"><a href="{{ url_for('login') }}">登录a>li>
                {% endif %}
              ul>
            div>
          div>
        nav>
    div>
    <div>
        {% block content %}
{#        其他页面重写页面内容的地方#}
        {% endblock %}
    div>
body>
html>

4.4 注销功能实现

下面我们就来完成用户的注销功能,其实注销功能实现很简单,我们这里就简单粗暴的将session清空即可达到注销的效果,清空之后我们重定向到首页即可。

# 用户注销
@app.route('/logout')
def logout():
    session.clear()
    return redirect(url_for(('index')))

4.5 主页实现

其实这里排版有一点小小的失误,我们应该第一个实现首页的,不过现在实现也并不影响。首页我们需要的功能很简单,找个漂亮唯美的背景图,中间加上几个"在线论坛系统"大字即可,一个漂漂亮亮的首页就完成了,因为我们目前首页不需要别的功能在,暂时就设置这样子就好了,我们先去随便找一个唯美的风景图。我们这里在百度图库中随便找了一个,然后把它下载放到我们的/static/img文件中。然后在我们之前创建的index.html中插入这张图片,再创建一个index.css,对样式进行调整。
在插入之后我们发现图片和上面导航栏会有一点点的空隙,看着还不舒服。这里我们需要为base.html创建一个base.css来设置一下导航条的样式,为他设置height = 52px;这样会无缝衔接,这个值也可以稍微大一点。然后我们在base.html中引入样式。

.navigation_bar{
    height: 52px;
}
    <link rel="stylesheet" href="/static/css/base.css">

这样之后我们的图片就可以无缝衔接了。我们的图片是作为一个大的div背景图片插入的,这样方便我们在上面进行显示标题。下面直接贴代码:
index.html:

{% extends 'base.html' %}

{% block title %}
主页
{% endblock %}

{% block css %}
<link rel="stylesheet" href="/static/css/index.css">
{% endblock %}

{% block content %}
<div class="index_content">
    <div class="index_title">
        <h1>在线论坛系统<small>Online Forum Platformsmall>h1>
    div>
div>
{% endblock %}

index.css:

.index_content{
    margin: 0;
    padding: 0;
    width: 100%;
    /*这里由于图片高为1200,所以直接设置了1200,大家可以可以根据自己的需求进行设置*/
    height: 1200px;
    background-image: url("/static/img/index.jpeg");
}
.index_title{
    text-align: center;
    padding: 20% 20%;
}
.index_title h1{
    font-style: italic;
    font-size: 60px;
    text-shadow: 0.15em 0.15em 0.1em #333;
    font-weight: bolder;
}

在app.py中,我们主页的路由暂时只需要返回页面即可:

# 主页
@app.route('/')
def index():
    return render_template('index.html')

下面来让我们看一下主页的效果图:(博客直男审美,各位可以自己设计主页的效果)
  好了,第四部分就讲到这里,下面我们将继续实现论坛的功能,该到了实现论坛真正功能的时候,这部分主要是各个系统都通用的功能:导航条、登录、注册、注销、主页。大家都顺利实现了吗?如果有什么问题可以评论区里进行询问,如果喜欢这篇文章的话,可以点赞关注支持下博主。


五、发布帖子、论坛页面实现

本项目所有源码在GitHub开源,GitHub地址为:OnlineForumPlatform
有需要源码可以前去查看,喜欢的话可以star一下

下面就开始第五部分的内容了,这部分将会带领大家一起实现论坛的主体功能,论坛的问题列表,帖子详情页面,发布帖子,回答问题等功能,让这个在线论坛系统成为一个“真正的论坛”,让他具有论坛的功能。


博主的站内博客导航:博客文章内容导航(实时更新)
更多精彩博客文章推荐

  • 一本教你如何在前端实现富文本编辑器
  • 小白都能看得懂的教程 一本教你如何在前端实现markdown编辑器
  • 一文教会你Bootstrap,让你也可以快速建站
  • 一文教你如何白嫖JetBrains全家桶(IDEA/PtChram/CLion)免费正版

5.1 发布帖子的功能实现

在这里的haul,由于问题列表等页面,没问题的话无法具体的进行显示,所以这里我们先开发发布问题的功能,然后再开发问题列表页面。

5.1.1 发布帖子的实现-前端

前端的话我们先实现一个富文本编辑器来实现信息的编辑,使我们编辑的文章具有格式化,如果不知道如何在前端嵌入富文本编辑器的话,可以看我之前的博客:一本教你如何在前端实现富文本编辑器:一本教你如何在前端实现富文本编辑器如果需要创建Markdown文本编辑器的话,也可以看博主的博客:小白都能看得懂的教程 一本教你如何在前端实现富文本编辑器
在此基础上,我们来设计我们的前端页面。
这里我们直接上代码:
post_issue.css:

.post_issue_content{
    margin-left: 20%;
    margin-right: 20%;
}

#page_header{
    text-align: center;
}

#issue_titile_div{
    height: 35px;
    margin: 0;
}

/*设置富文本编辑器默认的高度*/
#edui1_iframeholder{
    height: 200px;
}

#inputEmail3{
    margin-left: 0;
    padding-left: 0;
    width: 100%;
}

#post_issue_butt{
    margin-top: 60px;
    text-align: center;
    height: 35px;
}

#post_issue_butt button{
    height: 35px;
}

post_issue.html:

{% extends 'base.html' %}

{% block title %}
发布帖子
{% endblock %}

{% block css %}
    <link rel="stylesheet" href="/static/css/post_issue.css">
    
    
                    
                    

你可能感兴趣的:(#,Flask,python,小白都能看得懂的教程系列)