小白都能看懂的实战教程 手把手教你Python Web全栈开发 (DAY 2)

2020年06月08日 阅读数:47
这篇文章主要向大家介绍小白都能看懂的实战教程 手把手教你Python Web全栈开发 (DAY 2),主要内容包括基础应用、实用技巧、原理机制等方面,希望对大家有所帮助。

小白都能看懂的实战教程 手把手教你Python Web全栈开发 Flask(Python Web)实战系列之在线论坛系统 第二讲

这是小白都能看懂的实战教程 手把手教你Python Web全栈开发 的第二讲,若是文中有基础知识不太熟悉的话,能够看博主前几期的博客:javascript

博主博客文章内容导航(实时更新)
更多优质文章推荐:css

本项目全部源码在GitHub开源,GitHub地址为:OnlineForumPlatform
有须要源码能够前去查看,喜欢的话能够star一下
在作完准备工做以后,咱们就正式的开始开发了,在这本系列的第二讲中,博主将带领你实如今线论坛系统的导航条、注册、登陆和主页功能,在实现的同时会讲解各个功能实现的原理,手把手的教你进入Python Web全栈开发,一个字一个字的代码完成本项目。html

2.1 导航栏实现

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

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

<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中:python

<nav class="navbar navbar-default">
  <div class="container-fluid">
    <!-- Brand and toggle get grouped for better mobile display -->
    <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 navigation</span>
        <span class="icon-bar"></span>
        <span class="icon-bar"></span>
        <span class="icon-bar"></span>
      </button>
      <a class="navbar-brand" href="#">Brand</a>
    </div>

    <!-- Collect the nav links, forms, and other content for toggling -->
    <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="#">Link</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="#">Action</a></li>
            <li><a href="#">Another action</a></li>
            <li><a href="#">Something else here</a></li>
            <li role="separator" class="divider"></li>
            <li><a href="#">Separated link</a></li>
            <li role="separator" class="divider"></li>
            <li><a href="#">One more separated link</a></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">Submit</button>
      </form>
      <ul class="nav navbar-nav navbar-right">
        <li><a href="#">Link</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="#">Action</a></li>
            <li><a href="#">Another action</a></li>
            <li><a href="#">Something else here</a></li>
            <li role="separator" class="divider"></li>
            <li><a href="#">Separated link</a></li>
          </ul>
        </li>
      </ul>
    </div><!-- /.navbar-collapse -->
  </div><!-- /.container-fluid -->
</nav>

插入以后咱们打开网页就能够看到以下效果:
在这里插入图片描述mysql

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

<!DOCTYPE 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部分进行了删除#}
            <!-- Collect the nav links, forms, and other content for toggling -->
            <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="#">Link</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="#">Action</a></li>
                    <li><a href="#">Another action</a></li>
                    <li><a href="#">Something else here</a></li>
                    <li role="separator" class="divider"></li>
                    <li><a href="#">Separated link</a></li>
                    <li role="separator" class="divider"></li>
                    <li><a href="#">One more separated link</a></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">Submit</button>
              </form>
              <ul class="nav navbar-nav navbar-right">
                <li><a href="#">Link</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="#">Action</a></li>
                    <li><a href="#">Another action</a></li>
                    <li><a href="#">Something else here</a></li>
                    <li role="separator" class="divider"></li>
                    <li><a href="#">Separated link</a></li>
                  </ul>
                </li>
              </ul>
            </div><!-- /.navbar-collapse -->
          </div><!-- /.container-fluid -->
        </nav>
    </div>
    <div>
        {% block content %}
{#        其余页面重写页面内容的地方#}
        {% endblock %}
    </div>
</body>
</html>

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

2.2 注册功能实现

2.2.1 注册功能实现-数据库

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

在这里插入图片描述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)
);

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

2.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 header</small></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>Register</small></h1>
    </div>
    <div id="register_form">
        <form method="post">
          <div class="form-group">
            <label for="exampleInputEmail1">Email address</label>
            <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为:

<!DOCTYPE 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部分进行了删除#}
            <!-- Collect the nav links, forms, and other content for toggling -->
            <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="#">Link</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="#">Action</a></li>
                    <li><a href="#">Another action</a></li>
                    <li><a href="#">Something else here</a></li>
                    <li role="separator" class="divider"></li>
                    <li><a href="#">Separated link</a></li>
                    <li role="separator" class="divider"></li>
                    <li><a href="#">One more separated link</a></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">Submit</button>
              </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="#">Action</a></li>#}
{#                    <li><a href="#">Another action</a></li>#}
{#                    <li><a href="#">Something else here</a></li>#}
{#                    <li role="separator" class="divider"></li>#}
{#                    <li><a href="#">Separated link</a></li>#}
{#                  </ul>#}
{#                </li>#}
              </ul>
            </div><!-- /.navbar-collapse -->
          </div><!-- /.container-fluid -->
        </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>Register</small></h1>
    </div>
    <div id="register_form">
        <form method="post">
          <div class="form-group">
            <label for="exampleInputEmail1">Email address</label>
            <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 %}

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

2.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()

这样咱们的注册功能就所有实现啦!能够先去注册一个本身的测试帐号,方便咱们后面的各项功能测试呦!

2.3 登陆功能实现

下面咱们就来实现登陆功能

2.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>Login</small></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 address</label>
            <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,用于标记当前页面。

修改完成以后咱们的登陆页面效果为:
在这里插入图片描述

2.3.2 登陆功能实现-后端

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

2.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

这样咱们的登陆的基本功能就实现了,你们能够去测试下,若是登陆正常则会返回首页,若是登陆失败则会显示各类各样的提示。下面咱们就要实现登陆的另外一个功能,登陆状态保持,咱们登陆以后如何让系统一直保持咱们的登陆状态呢?

2.3.2.1 登陆功能实现-后端-登陆状态保持

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

# 登陆状态保持
@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="#">Action</a></li>
        <li><a href="#">Another action</a></li>
        <li><a href="#">Something else here</a></li>
        <li role="separator" class="divider"></li>
        <li><a href="#">Separated link</a></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的所有代码:这里的注销的连接尚未写,在等会咱们实现了注销的功能以后再在这里添加连接。

<!DOCTYPE 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部分进行了删除#}
            <!-- Collect the nav links, forms, and other content for toggling -->
            <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="#">Link</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="#">Action</a></li>
                    <li><a href="#">Another action</a></li>
                    <li><a href="#">Something else here</a></li>
                    <li role="separator" class="divider"></li>
                    <li><a href="#">Separated link</a></li>
                    <li role="separator" class="divider"></li>
                    <li><a href="#">One more separated link</a></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">Submit</button>
              </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="#">Action</a></li>
                        <li><a href="#">Another action</a></li>
                        <li><a href="#">Something else here</a></li>
                        <li role="separator" class="divider"></li>
                        <li><a href="#">Separated link</a></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><!-- /.navbar-collapse -->
          </div><!-- /.container-fluid -->
        </nav>
    </div>
    <div>
        {% block content %}
{#        其余页面重写页面内容的地方#}
        {% endblock %}
    </div>
</body>
</html>

2.4 注销功能实现

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

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

2.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 Platform</small></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')

下面来让咱们看一下主页的效果图:(博客直男审美,各位能够本身设计主页的效果)在这里插入图片描述
好了,第二讲就到这里,咱们第三讲再见!第三讲咱们将继续实现论坛的功能,该到了实现论坛真正功能的时候,这一讲主要是各个系统都通用的功能:导航条、登陆、注册、注销、主页。
咱们下棋见,若是以为写得不过,给个点赞关注支持下博客,谢谢你们。