Flask項目實戰——2—(後臺用戶登錄驗證、錯誤登錄信息渲染到前端界面、鉤子函數和裝飾器、CSRF驗證保護、用戶名渲染和註銷功能、模板繼承)

1、後臺用戶登錄驗證

1、用戶登錄的操作是post提交方式:
在這裏插入圖片描述
將後臺用戶提交的form表單信息收集:創建cms/forms.py文件

後臺登錄用戶表單收集文件:forms.py文件

# -*- encoding: utf-8 -*-
"""
@File    : forms.py
@Time    : 2020/5/11 10:00
@Author  : chen

"""
# forms表單信息
from wtforms import Form, StringField, IntegerField
from wtforms.validators import Email, InputRequired, Length


class LoginForm(Form):
    email = StringField(validators=[Email(message="請輸入正確的郵箱"),  InputRequired(message="請輸入郵箱")])
    password = StringField(validators=[Length(3, 15, message='請輸入正確長度的密碼')])
    remember = IntegerField()                # 記住cookie操作  賦值爲0或1

forms.py文件收集到的後臺登錄用戶字段信息提交至views.py文件

驗證登錄用戶表單信息是否匹配數據庫中的信息:views.py

# -*- encoding: utf-8 -*-
"""
@File    : views.py
@Time    : 2020/5/11 9:59
@Author  : chen

"""
# 藍圖文件:實現模塊化應用,應用可以分解成一系列的藍圖   後端的類視圖函數寫在這個文件
from flask import Blueprint, render_template, views, session   # 定義類視圖,顯示模板文件
from flask import request, redirect, url_for          # 頁面跳轉redirect   request請求收集
# 導入form表單   .forms代表同級目錄下的forms.py
from .forms import LoginForm

# 導入模型  .models代表同級目錄下的models.py
from .models import CMS_User

cms_bp = Blueprint("cms", __name__, url_prefix='/cms/')     # URL前綴url_prefix


@cms_bp.route("/")                                          # 後臺界面
def index():
    return "cms index:後端類視圖文件"


# 定義類視圖,顯示模板文件
class LoginView(views.MethodView):
    def get(self):
        return render_template("cms/cms_login.html")
    
    # 用戶登錄操作驗證
    def post(self):
        # 收集表單信息
        login_form = LoginForm(request.form)
        if login_form.validate():
            # 數據庫驗證
            email = login_form.email.data
            password = login_form.password.data
            remember = login_form.remember.data
            
            # 查詢數據庫中的用戶信息
            user = CMS_User.query.filter_by(email=email).first()    # 郵箱唯一,用於查詢驗證用戶
            if user and user.check_password(password):              # 驗證用戶和密碼是否都正確
                session['user_id'] = user.id                        # 查詢到用戶數據時,保存session的id到瀏覽器
                if remember:                                        # 如果用戶點擊了remember選擇,在瀏覽器中進行數據持久化
                    session.permanent = True                        # 數據持久化,默認31天,需要設置session_key在config.py中
            
                # 登錄成功,跳轉到後臺首頁
                return redirect(url_for('cms.index'))               # 在藍圖中必須加cms   跳轉到index方法
            else:
                return "郵箱或密碼錯誤"                              # 登錄出錯,返回結果
        else:
            print(login_form.errors)
            return "表單驗證錯誤"
         
    
# 添加登錄路由
cms_bp.add_url_rule("/login/", view_func=LoginView.as_view('login'))    # view_func 命名操作名字,"/login/"路由地址

views.py文件中有一個密碼驗證的方法check_password(password),需要在模型文件models.py中進行添加;
驗證用戶信息之後,包含session信息的加密方式,需要設置session_key在config.py中。

模型文件:models.py

# -*- encoding: utf-8 -*-
"""
@File    : models.py
@Time    : 2020/5/11 10:00
@Author  : chen

"""
# 定義後端用戶模型
from exts import db
from datetime import datetime
from werkzeug.security import generate_password_hash, check_password_hash         # 導入密碼加密,解密方法的庫


class CMS_User(db.Model):
    __tablename__ = 'cms_user'
    
    id = db.Column(db.Integer, primary_key=True, autoincrement=True)           # 主鍵  自增
    username = db.Column(db.String(150), nullable=False)                       # 非空
    # password = db.Column(db.String(150), nullable=False)
    _password = db.Column(db.String(150), nullable=False)                      # 密碼加密操作修改字段
    email = db.Column(db.String(50), nullable=False, unique=True)              # 非空、唯一
    join_time = db.Column(db.DateTime, default=datetime.now)                   # 默認當前時間
    
    # 修改密碼加密操作中的字段,在manage.py映射數據庫時候,使用字段還是保持相同
    def __init__(self, username, password, email):
        self.username = username
        self.password = password         # 調用該方法 返回下面的self._password數值,
        self.email = email
    
    # 密碼加密操作
    @property
    def password(self):                   # 密碼取值
        return self._password

    @password.setter                      # 密碼加密
    def password(self, raw_password):
        self._password = generate_password_hash(raw_password)

    # 用於驗證後臺登錄密碼是否和數據庫一致,raw_password是後臺登錄輸入的密碼
    def check_password(self, raw_password):
        result = check_password_hash(self.password, raw_password)   # 相當於用相同的hash加密算法加密raw_password,檢測與數據庫中是否一致
        return result    

配置文件:config.py

import os    # 導入隨機字符串用於加密session

SECRET_KEY = os.urandom(15)        # 產生隨機15位字符串加密

2、錯誤登錄信息渲染到前端界面

在後臺登錄的過程中,用戶信息與數據庫中數據不匹配的時候,產生的錯誤信息需要給用戶看到錯誤的種類,此時就需要將錯誤信息渲染到後臺登錄的前端界面上,即cms_login.html頁面。

views.py文件中將錯誤信息返回

# -*- encoding: utf-8 -*-
"""
@File    : views.py
@Time    : 2020/5/11 9:59
@Author  : chen

"""
# 藍圖文件:實現模塊化應用,應用可以分解成一系列的藍圖   後端的類視圖函數寫在這個文件
from flask import Blueprint, render_template, views, session   # 定義類視圖,顯示模板文件
from flask import request, redirect, url_for          # 頁面跳轉redirect   request請求收集
# 導入form表單   .forms代表同級目錄下的forms.py
from .forms import LoginForm

# 導入模型  .models代表同級目錄下的models.py
from .models import CMS_User

cms_bp = Blueprint("cms", __name__, url_prefix='/cms/')     # URL前綴url_prefix


@cms_bp.route("/")                                          # 後臺界面
def index():
    return "cms index:後端類視圖文件"


# 定義類視圖,顯示模板文件
class LoginView(views.MethodView):
    def get(self, message=None):                                         # message=None時候不傳輸信息到cms_login.html頁面
        return render_template("cms/cms_login.html", message=message)    # 針對post方法中同樣要返回到cms_login.html頁面進行代碼簡化
    
    # 用戶登錄操作驗證
    def post(self):
        # 收集表單信息
        login_form = LoginForm(request.form)
        if login_form.validate():
            # 數據庫驗證
            email = login_form.email.data
            password = login_form.password.data
            remember = login_form.remember.data
            
            # 查詢數據庫中的用戶信息
            user = CMS_User.query.filter_by(email=email).first()    # 郵箱唯一,用於查詢驗證用戶
            if user and user.check_password(password):              # 驗證用戶和密碼是否都正確
                session['user_id'] = user.id                        # 查詢到用戶數據時,保存session的id到瀏覽器
                if remember:                                        # 如果用戶點擊了remember選擇,在瀏覽器中進行數據持久化
                    session.permanent = True                        # 數據持久化,默認31天,需要設置session_key在config.py中
            
                # 登錄成功,跳轉到後臺首頁
                return redirect(url_for('cms.index'))               # 在藍圖中必須加cms   跳轉到index方法
            else:
                # return "郵箱或密碼錯誤"                              # 登錄出錯,返回結果
                # return render_template("cms/cms_login.html", message="郵箱或密碼錯誤")  # 登錄出錯,返回結果渲染到cms_login.html頁面
                return self.get(message="郵箱或密碼錯誤")             # 傳參到get方法中,多加一個傳輸錯誤信息的參數到方法中
        else:
            print(login_form.errors)                                 # forms.py中的錯誤信息  字典類型數據
            print(login_form.errors.popitem())                       # forms.py中的錯誤信息  元祖類型數據
            # return "表單驗證錯誤"                                   # 錯誤信息需要渲染到cms_login.html頁面
            return self.get(message=login_form.errors.popitem()[1][0])  # 字典類型數據信息提取
        
    
# 添加登錄路由
cms_bp.add_url_rule("/login/", view_func=LoginView.as_view('login'))    # view_func 命名操作名字,"/login/"路由地址

其中要注意的是get()方法的修改和login_form.errors表單錯誤信息的提取。

渲染頁面cms_login.html進行傳參message


<!DOCTYPE html>
<html lang="zh-CN">
  <head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <!-- 上述3個meta標籤*必須*放在最前面,任何其他內容都*必須*跟隨其後! -->
    <meta name="description" content="">
    <meta name="author" content="">
<!--    <link rel="icon" href="../../favicon.ico">-->

    <title>CMS後臺登錄界面</title>

    <!-- Bootstrap core CSS -->
    <link href="https://cdn.bootcss.com/bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet">

    <!-- Just for debugging purposes. Don't actually copy these 2 lines! -->
    <!--[if lt IE 9]><script src="../../assets/js/ie8-responsive-file-warning.js"></script><![endif]-->
    <script src="../../assets/js/ie-emulation-modes-warning.js"></script>

    <!-- Custom styles for this template -->
<!--    <link href="signin.css" rel="stylesheet">-->

      <!--   這裏引用自己的css模板文件 模板中引用靜態資源文件使用url_for,路徑filename='cms/css/signin.css')需要相對路徑中的絕對路徑-->
    <link href="{{ url_for('static', filename='cms/css/signin.css') }}" rel="stylesheet">
  </head>

  <body>

    <div class="container">

<!--    添加登錄方法method="post"    -->
      <form class="form-signin" method="post">
        <h2 class="form-signin-heading">請登錄</h2>
        <label for="inputEmail" class="sr-only">郵箱</label>
<!--    表單提交根據name="email"來接收後端數據      -->
        <input type="email" id="inputEmail" name="email" class="form-control" placeholder="郵箱地址" required autofocus>
        <label for="inputPassword" class="sr-only">密碼</label>
<!--    表單提交根據name="password"來接收後端數據      -->
        <input type="password" id="inputPassword" name="password" class="form-control" placeholder="填寫密碼" required>
        <div class="checkbox">
          <label>
              <!--    表單提交根據name="remember"來接收後端數據      -->
            <input type="checkbox" value="remember-me" name="remember"> 記住我
          </label>
        </div>
        <button class="btn btn-lg btn-primary btn-block" type="submit">立即登錄</button>
      </form>

<!--   views.py中的表單驗證輸出的錯誤參數message進行渲染到前端界面   先判斷message是否有信息,沒有就不進行渲染  -->
        {% if message %}
            <!--      style="text-align:center" class="text-danger" 居中、紅色字體樣式      -->
            <p style="text-align:center" class="text-danger"> {{ message }}</p>
        {% endif %}

    </div> <!-- /container -->

    <!-- IE10 viewport hack for Surface/desktop Windows 8 bug -->
    <script src="../../assets/js/ie10-viewport-bug-workaround.js"></script>
  </body>
</html>

這裏需要注意的是:先判斷message是否傳有信息,沒有就不進行渲染到界面中,運用到了if知識點。

3、鉤子函數和裝飾器

爲了實現判斷當前界面是否是登錄界面,不是就將url重定向到登錄界面這一功能,有兩種方法實現:鉤子函數、裝飾器;分別進行介紹。

鉤子函數

鉤子函數中有函數方法before_first_request:處理第一次請求之前執行
用於實現判斷當前界面是否是登錄界面,不是就將url重定向到登錄界面。

創建cms/hooks.py文件用於存放鉤子函數

# -*- encoding: utf-8 -*-
"""
@File    : hooks.py
@Time    : 2020/5/13 9:36
@Author  : chen

"""
from flask import request, session, url_for,redirect
from .views import cms_bp


# 鉤子函數 ,所有操作前執行該方法,判斷當前界面是否是登錄界面,不是就將url重定向到登錄界面
@cms_bp.before_request
def before_request():
    print(request.path)                                         # 輸出的是網頁url的後綴,即/cms/login/
    if not request.path.endswith(url_for('cms.login')):    # 判斷當前所在url是否是/cms/login/,不是代表不在後臺登錄界面
        user_id = session.get('user_id')                        # 登陸之後,獲取登錄時候記錄的session中的user_id
        if not user_id:                                         # 若沒有user_id,說明登錄不成功
            return redirect(url_for('cms.login'))          # 重定向到後臺登錄界面

裝飾器

裝飾器也可以實現上面的功能,但是比較複雜難懂,後面創建的路由函數也都要再加上這個裝飾器,比較麻煩,不建議使用。

創建cms/decorators.py文件用於存放裝飾器

# -*- encoding: utf-8 -*-
"""
@File    : decorators.py
@Time    : 2020/5/12 22:38
@Author  : chen

"""
# 裝飾器方法實現另一種判定後臺用戶當前界面是否是登錄界面,不是就重定向到登錄界面
from flask import session
from flask import redirect, url_for


def login_required(func):
    def inner(*args, **kwargs):              # 內層函數
        if 'user_id' in session:
            return func(*args, **kwargs)
        else:
            return redirect(url_for("cms.login"))
    return inner

需要注意的是,需要將上面的兩種方法選擇一個導入到cms/views.py文件中使用驗證(推薦使用鉤子函數,簡單方便),這裏注意導入的方式:

cms/views.py文件導入鉤子函數

# -*- encoding: utf-8 -*-
"""
@File    : views.py
@Time    : 2020/5/11 9:59
@Author  : chen

"""
# 藍圖文件:實現模塊化應用,應用可以分解成一系列的藍圖   後端的類視圖函數寫在這個文件
from flask import Blueprint, render_template, views, session   # 定義類視圖,顯示模板文件
from flask import request, redirect, url_for          # 頁面跳轉redirect   request請求收集
# 導入form表單   .forms代表同級目錄下的forms.py
from .forms import LoginForm

# 導入模型  .models代表同級目錄下的models.py
from .models import CMS_User

# 導入裝飾器:判斷當前界面是否是登錄界面,不是就將url重定向到登錄界面
from .decorators import login_required

cms_bp = Blueprint("cms", __name__, url_prefix='/cms/')     # URL前綴url_prefix

# 鉤子函數是在cms_bp創建之後才創建的,順序在cms_bp創建之後
from .hooks import before_request


@cms_bp.route("/")                                          # 後臺界面
# @login_required             # 裝飾器判定當前界面是否是登錄界面,但是需要每個路由函數都要加該裝飾器,比較麻煩,推薦使用鉤子函數
def index():
    return "cms index:後端類視圖文件"


# 定義類視圖,顯示模板文件   用戶登錄功能實現
class LoginView(views.MethodView):
    def get(self, message=None):                                         # message=None時候不傳輸信息到cms_login.html頁面
        return render_template("cms/cms_login.html", message=message)    # 針對post方法中同樣要返回到cms_login.html頁面進行代碼簡化
    
    # 用戶登錄操作驗證
    def post(self):
        # 收集表單信息
        login_form = LoginForm(request.form)
        if login_form.validate():
            # 數據庫驗證
            email = login_form.email.data
            password = login_form.password.data
            remember = login_form.remember.data
            
            # 查詢數據庫中的用戶信息
            user = CMS_User.query.filter_by(email=email).first()    # 郵箱唯一,用於查詢驗證用戶
            if user and user.check_password(password):              # 驗證用戶和密碼是否都正確
                session['user_id'] = user.id                        # 查詢到用戶數據時,保存session的id到瀏覽器
                if remember:                                        # 如果用戶點擊了remember選擇,在瀏覽器中進行數據持久化
                    session.permanent = True                        # 數據持久化,默認31天,需要設置session_key在config.py中
            
                # 登錄成功,跳轉到後臺首頁
                return redirect(url_for('cms.index'))               # 在藍圖中必須加cms   跳轉到index方法
            else:
                # return "郵箱或密碼錯誤"                              # 登錄出錯,返回結果
                # return render_template("cms/cms_login.html", message="郵箱或密碼錯誤")  # 登錄出錯,返回結果渲染到cms_login.html頁面
                return self.get(message="郵箱或密碼錯誤")             # 傳參到get方法中,多加一個傳輸錯誤信息的參數到方法中
        else:
            print(login_form.errors)                                 # forms.py中的錯誤信息  字典類型數據
            print(login_form.errors.popitem())                       # forms.py中的錯誤信息  元祖類型數據
            # return "表單驗證錯誤"                                   # 錯誤信息需要渲染到cms_login.html頁面
            return self.get(message=login_form.errors.popitem()[1][0])  # 字典類型數據信息提取
        
    
# 添加登錄路由
cms_bp.add_url_rule("/login/", view_func=LoginView.as_view('login'))    # view_func 命名操作名字,"/login/"路由地址

4、CSRF驗證保護

CSRF驗證保護是屬於WEB安全中的知識點:需要在form表單進行post提交的時候,我們添加一個CSRF的token,用於確認訪問者的身份確認,防止爬蟲和黑客。

在這裏插入圖片描述
程序主文件引入CSRF保護:
項目主文件,啓動入口bbs.py

# -*- encoding: utf-8 -*-
"""
@File    : bbs.py
@Time    : 2020/5/11 9:46
@Author  : chen

"""
# 項目主文件,啓動入口

# 前臺  front    管理前端界面的邏輯
# 後臺  cms      管理後端的操作
# 公有的文件 common

from flask import Flask
import config      # 配置文件庫
from exts import db    # 第三方庫導入db
from apps.cms.views import cms_bp          # 導入後端藍圖文件
from apps.front.views import front_bp      # 導入前端藍圖文件
from flask_wtf import CSRFProtect          # CSRF表單保護驗證

app = Flask(__name__)

CSRFProtect(app)                      # CSRF保護app

app.config.from_object(config)        # 添加配置

db.init_app(app)                      # 綁定app

app.register_blueprint(cms_bp)        # 後端藍圖文件註冊
app.register_blueprint(front_bp)        # 前端藍圖文件註冊


if __name__ == '__main__':
    app.run(debug=True, port=9999)

此時還需要在渲染的html文件中添加csrf_token信息用於驗證:

後臺顯示界面cms_login.html文件

<!DOCTYPE html>
<html lang="zh-CN">
  <head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <!-- 上述3個meta標籤*必須*放在最前面,任何其他內容都*必須*跟隨其後! -->
    <meta name="description" content="">
    <meta name="author" content="">
<!--    <link rel="icon" href="../../favicon.ico">-->

    <title>CMS後臺登錄界面</title>

    <!-- Bootstrap core CSS -->
    <link href="https://cdn.bootcss.com/bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet">

      <!--   這裏引用自己的css模板文件 模板中引用靜態資源文件使用url_for,路徑filename='cms/css/signin.css')需要相對路徑中的絕對路徑-->
    <link href="{{ url_for('static', filename='cms/css/signin.css') }}" rel="stylesheet">
  </head>

  <body>

    <div class="container">

<!--    添加登錄方法method="post"    -->
      <form class="form-signin" method="post">
<!--    CSRF保護驗證  type="hidden"頁面隱藏這個信息,只用於驗證保護    方法調用csrf_token()   -->
        <input type="hidden" name="csrf_token" value="{{ csrf_token() }}">

        <h2 class="form-signin-heading">請登錄</h2>
        <label for="inputEmail" class="sr-only">郵箱</label>
<!--    表單提交根據name="email"來接收後端數據      -->
        <input type="email" id="inputEmail" name="email" class="form-control" placeholder="郵箱地址" required autofocus>
        <label for="inputPassword" class="sr-only">密碼</label>
<!--    表單提交根據name="password"來接收後端數據      -->
        <input type="password" id="inputPassword" name="password" class="form-control" placeholder="填寫密碼" required>
        <div class="checkbox">
          <label>
              <!--    表單提交根據name="remember"來接收後端數據      -->
            <input type="checkbox" value="remember-me" name="remember"> 記住我
          </label>
        </div>
        <button class="btn btn-lg btn-primary btn-block" type="submit">立即登錄</button>
      </form>

<!--   views.py中的表單驗證輸出的錯誤參數message進行渲染到前端界面   先判斷message是否有信息,沒有就不進行渲染  -->
        {% if message %}
            <!--      style="text-align:center" class="text-danger" 居中、紅色字體樣式      -->
            <p style="text-align:center" class="text-danger"> {{ message }}</p>
        {% endif %}

    </div> <!-- /container -->

    <!-- IE10 viewport hack for Surface/desktop Windows 8 bug -->
    <script src="../../assets/js/ie10-viewport-bug-workaround.js"></script>
  </body>
</html>

在這裏插入圖片描述

5、用戶名渲染和註銷功能

這裏需要添加幾個模板文件:
在這裏插入圖片描述
靜態文件:static/cms/css/cms_base.css文件

/*
 * Base structure
 */

/* Move down content because we have a fixed navbar that is 50px tall */
body {
    padding-top: 50px;
    overflow: hidden;
}

/*
 * Global add-ons
 */

.sub-header {
  padding-bottom: 10px;
  border-bottom: 1px solid #eee;
}

/*
 * Top navigation
 * Hide default border to remove 1px line.
 */
.navbar-fixed-top {
  border: 0;
}

/*
 * Sidebar
 */

/* Hide for mobile, show later */
.sidebar {
  display: none;
}
@media (min-width: 768px) {
  .sidebar {
    position: fixed;
    top: 51px;
    bottom: 0;
    left: 0;
    z-index: 1000;
    display: block;
    padding: 20px;
    overflow-x: hidden;
    overflow-y: auto; /* Scrollable contents if viewport is shorter than content. */
    background-color: #363a47;
    border-right: 1px solid #eee;
    margin-top: -1px;
  }
}

.nav-sidebar{
    padding: 5px 0;
    margin-left: -20px;
    margin-right: -20px;
}

.nav-sidebar > li{
    background: #494f60;
    border-bottom: 1px solid #363a47;
    border-top: 1px solid #666;
    line-height: 35px;
}

.nav-sidebar > li > a {
    background: #494f60;
    color: #9b9fb1;
    margin-left: 25px;
    display: block;
}

.nav-sidebar > li a span{
    float: right;
    width: 10px;
    height:10px;
    border-style: solid;
    border-color: #9b9fb1 #9b9fb1 transparent transparent;
    border-width: 1px;
    transform: rotate(45deg);
    position: relative;
    top: 10px;
    margin-right: 10px;
}

.nav-sidebar > li > a:hover{
    color: #fff;
    background: #494f60;
    text-decoration: none;
}

.nav-sidebar > li > .subnav{
    display: none;
}

.nav-sidebar > li.unfold{
    background: #494f60;
}

.nav-sidebar > li.unfold > .subnav{
    display: block;
}

.nav-sidebar > li.unfold > a{
    color: #db4055;
}

.nav-sidebar > li.unfold > a span{
    transform: rotate(135deg);
    top: 5px;
    border-color: #db4055 #db4055 transparent transparent;
}

.subnav{
    padding-left: 10px;
    padding-right: 10px;
    background: #363a47;
    overflow: hidden;
}

.subnav li{
    overflow: hidden;
    margin-top: 10px;
    line-height: 25px;
    height: 25px;
}

.subnav li.active{
    background: #db4055;
}

.subnav li a{
    /*display: block;*/
    color: #9b9fb1;
    padding-left: 30px;
    height:25px;
    line-height: 25px;
}

.subnav li a:hover{
    color: #fff;
}

.nav-group{
    margin-top: 10px;
}


.main {
  padding: 20px;
}
@media (min-width: 768px) {
  .main {
    padding-right: 40px;
    padding-left: 40px;
  }
}
.main .page-header {
  margin-top: 0;
}

/*
 * Placeholder dashboard ideas
 */

.placeholders {
  margin-bottom: 30px;
  text-align: center;
}
.placeholders h4 {
  margin-bottom: 0;
}
.placeholder {
  margin-bottom: 20px;
}
.placeholder img {
  display: inline-block;
  border-radius: 50%;
}

.main_content{
    margin-top: 20px;
}

.top-group{
    padding: 5px 10px;
    border-radius: 2px;
    background: #ecedf0;
    overflow: hidden;
}

靜態文件:static/cms/css/sigin.css文件

body {
  padding-top: 40px;
  padding-bottom: 40px;
  background-color: #eee;
}

.form-signin {
  max-width: 330px;
  padding: 15px;
  margin: 0 auto;
}
.form-signin .form-signin-heading,
.form-signin .checkbox {
  margin-bottom: 10px;
}
.form-signin .checkbox {
  font-weight: normal;
}
.form-signin .form-control {
  position: relative;
  height: auto;
  -webkit-box-sizing: border-box;
     -moz-box-sizing: border-box;
          box-sizing: border-box;
  padding: 10px;
  font-size: 16px;
}
.form-signin .form-control:focus {
  z-index: 2;
}
.form-signin input[type="email"] {
  margin-bottom: -1px;
  border-bottom-right-radius: 0;
  border-bottom-left-radius: 0;
}
.form-signin input[type="password"] {
  margin-bottom: 10px;
  border-top-left-radius: 0;
  border-top-right-radius: 0;
}

靜態文件:static/cms/js/cms_base.js文件

/**
 * Created by Administrator on 2016/12/17.
 */

$(function () {
    $('.nav-sidebar>li>a').click(function (event) {
        var that = $(this);
        if(that.children('a').attr('href') == '#'){
            event.preventDefault();
        }
        if(that.parent().hasClass('unfold')){
            that.parent().removeClass('unfold');
        }else{
            that.parent().addClass('unfold').siblings().removeClass('unfold');
        }
        console.log('coming....');
    });

    $('.nav-sidebar a').mouseleave(function () {
        $(this).css('text-decoration','none');
    });
});


$(function () {
    var url = window.location.href;
    if(url.indexOf('profile') >= 0){
        var profileLi = $('.profile-li');
        profileLi.addClass('unfold').siblings().removeClass('unfold');
        profileLi.children('.subnav').children().eq(0).addClass('active').siblings().removeClass('active');
    } else if(url.indexOf('resetpwd') >= 0){
        var profileLi = $('.profile-li');
        profileLi.addClass('unfold').siblings().removeClass('unfold');
        profileLi.children('.subnav').children().eq(1).addClass('active').siblings().removeClass('active');
    } else if(url.indexOf('resetemail') >= 0){
        var profileLi = $('.profile-li');
        profileLi.addClass('unfold').siblings().removeClass('unfold');
        profileLi.children('.subnav').children().eq(2).addClass('active').siblings().removeClass('active');
    } else if(url.indexOf('posts') >= 0){
        var postManageLi = $('.post-manage');
        postManageLi.addClass('unfold').siblings().removeClass('unfold');
    }else if(url.indexOf('boards') >= 0){
        var boardManageLi = $('.board-manage');
        boardManageLi.addClass('unfold').siblings().removeClass('unfold');
    }else if(url.indexOf('permissions') >= 0){
        var permissionManageLi = $('.permission-manage');
        permissionManageLi.addClass('unfold').siblings().removeClass('unfold');
    }else if(url.indexOf('roles') >= 0){
        var roleManageLi = $('.role-manage');
        roleManageLi.addClass('unfold').siblings().removeClass('unfold');
    }else if(url.indexOf('users') >= 0){
        var userManageLi = $('.user-manage');
        userManageLi.addClass('unfold').siblings().removeClass('unfold');
    }else if(url.indexOf('cmsuser_manage') >= 0){
        var cmsuserManageLi = $('.cmsuser-manage');
        cmsuserManageLi.addClass('unfold').siblings().removeClass('unfold');
    }else if(url.indexOf('cmsrole_manage') >= 0){
        var cmsroleManageLi = $('.cmsrole-manage');
        cmsroleManageLi.addClass('unfold').siblings().removeClass('unfold');
    }else if(url.indexOf('comments') >= 0) {
        var commentsManageLi = $('.comments-manage');
        commentsManageLi.addClass('unfold').siblings().removeClass('unfold');
    }
});

靜態文件:static/cms/js/resetpwd.js文件


$(function () {
    $("#submit").click(function (event) {
        // event.preventDefault
        // 是阻止按鈕默認的提交表單的事件
        event.preventDefault();

        var oldpwdE = $("input[name=oldpwd]");
        var newpwdE = $("input[name=newpwd]");
        var newpwd2E = $("input[name=newpwd2]");

        var oldpwd = oldpwdE.val();
        var newpwd = newpwdE.val();
        var newpwd2 = newpwd2E.val();

        // 1. 要在模版的meta標籤中渲染一個csrf-token
        // 2. 在ajax請求的頭部中設置X-CSRFtoken
        var lgajax = {
            'get':function(args) {
                args['method'] = 'get';
                this.ajax(args);
            },
            'post':function(args) {
                args['method'] = 'post';
                this.ajax(args);
            },
            'ajax':function(args) {
                // 設置csrftoken
                this._ajaxSetup();
                $.ajax(args);
            },
            '_ajaxSetup': function() {
                $.ajaxSetup({
                    'beforeSend':function(xhr,settings) {
                        if (!/^(GET|HEAD|OPTIONS|TRACE)$/i.test(settings.type) && !this.crossDomain) {
//                            var csrftoken = $('meta[name=csrf-token]').attr('content');
                            var csrftoken = $('input[name=csrf-token]').attr('value');
                            xhr.setRequestHeader("X-CSRFToken", csrftoken)
                        }
                    }
                });
            }
        };

        lgajax.post({
            'url': '/cms/resetpwd/',
            'data': {
                'oldpwd': oldpwd,
                'newpwd': newpwd,
                'newpwd2': newpwd2
            },
            'success': function (data) {
                console.log(data);
            },
            'fail': function (error) {
                console.log(error);
            }
        });
    });
});

模板文件:templates/cms/cms_index.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>標題</title>
    <script src="http://cdn.bootcss.com/jquery/3.1.1/jquery.min.js"></script>
    <link href="http://cdn.bootcss.com/bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet">
    <script src="http://cdn.bootcss.com/bootstrap/3.3.7/js/bootstrap.min.js"></script>

<!--  關聯本地的cms_base.css樣式 後臺管理界面CMS的樣式 -->
    <link rel="stylesheet" href="{{ url_for('static', filename='cms/css/cms_base.css') }}">
<!--  關聯本地的cms_base.js樣式 後臺管理界面CMS的樣式 -->
    <script src="{{ url_for('static', filename='cms/js/cms_base.js') }}"></script>
</head>
<body>
     <nav class="navbar navbar-inverse navbar-fixed-top" role="navigation">
      <div class="container-fluid">
        <div class="navbar-header">
          <button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar" aria-expanded="false" aria-controls="navbar">
            <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="#">論壇CMS管理系統</a>
        </div>
        <div id="navbar" class="navbar-collapse collapse">
          <ul class="nav navbar-nav navbar-right">
        <!--       從數據庫中調用用戶名,g對象全局調用g.cms_user      -->
            <li><a href="#">{{ g.cms_user }}<span>[超級管理員]</span></a></li>

        <!--  用戶註銷,關聯到views.py中的@cms_bp.route("/logout/")路由,重定向到該路由      -->
            <li><a href="{{ url_for('cms.logout') }}">註銷</a></li>
          </ul>
          <form class="navbar-form navbar-right">
            <input type="text" class="form-control" placeholder="查找...">
          </form>
        </div>
      </div>
    </nav>

    <div class="container-fluid">
      <div class="row">
          <div class="col-sm-3 col-md-2 sidebar">
              <ul class="nav-sidebar">
                <li class="unfold"><a href="#">首頁</a></li>
                <li class="profile-li">
                    <a href="#">個人中心<span></span></a>
                    <ul class="subnav">
                        <li><a href="#">個人信息</a></li>
                        <li><a href="#">修改密碼</a></li>
                        <li><a href="#">修改郵箱</a></li>
                    </ul>
                </li>

                <li class="nav-group post-manage"><a href="#">帖子管理</a></li>
                <li class="comments-manage"><a href="#">評論管理</a></li>
                <li class="board-manage"><a href="#">板塊管理</a></li>

                <li class="nav-group user-manage"><a href="#">用戶管理</a></li>
                <li class="role-manage"><a href="#">組管理</a></li>

                <li class="nav-group cmsuser-manage"><a href="#">CMS用戶管理</a></li>
                <li class="cmsrole-manage"><a href="#">CMS組管理</a></li>
            </ul>
          </div>
          <div class="col-sm-9 col-sm-offset-3 col-md-10 col-md-offset-2 main">
            <h1>BBS論壇</h1>
            <div class="main_content">
                歡迎來到BBS論壇
            </div>
          </div>
      </div>
    </div>
</body>
</html>

還需要對藍圖文件apps/cms/views.py進行修改:

  • 1、session中的用戶信息需要傳遞到apps/cms/hooks.py中進行判斷;
  • 2、對用戶的註銷操作進行函數編寫,同時傳遞關聯到templates/cms/cms_index.html中的註銷屬性;
# -*- encoding: utf-8 -*-
# -*- encoding: utf-8 -*-
"""
@File    : views.py
@Time    : 2020/5/11 9:59
@Author  : chen

"""
# 藍圖文件:實現模塊化應用,應用可以分解成一系列的藍圖   後端的類視圖函數寫在這個文件
from flask import Blueprint, render_template, views, session   # 定義類視圖,顯示模板文件
from flask import request, redirect, url_for          # 頁面跳轉redirect   request請求收集
# 導入form表單   .forms代表同級目錄下的forms.py
from .forms import LoginForm

# 導入模型  .models代表同級目錄下的models.py
from .models import CMS_User

# 導入裝飾器:判斷當前界面是否是登錄界面,不是就將url重定向到登錄界面
from .decorators import login_required

cms_bp = Blueprint("cms", __name__, url_prefix='/cms/')     # URL前綴url_prefix

# 鉤子函數是在cms_bp創建之後才創建的,順序在cms_bp創建之後
from .hooks import before_request


@cms_bp.route("/")                                          # 後臺界面
# @login_required             # 裝飾器判定當前界面是否是登錄界面,但是需要每個路由函數都要加該裝飾器,比較麻煩,推薦使用鉤子函數
def index():
    # return "cms index:後端類視圖文件"
    return render_template('cms/cms_index.html')       # 登陸之後進入CMS後臺管理界面,路徑寫全cms/cms_index.html


# 用戶註銷登錄
@cms_bp.route("/logout/")                              # 需要關聯到cms/cms_index.html中的註銷屬性
def logout():
    # session清除user_id
    del session['user_id']
    # 重定向到登錄界面
    return redirect(url_for('cms.login'))             # 重定向(redirec)爲把url變爲重定向的url


# 定義個人中心的路由
@cms_bp.route("/profile/")
def profile():
    return render_template("cms/cms_profile.html")   # 模板渲染(render_template)則不會改變url,模板渲染是用模板來渲染請求的url


# 定義類視圖,顯示模板文件   用戶登錄功能實現
class LoginView(views.MethodView):
    def get(self, message=None):                                         # message=None時候不傳輸信息到cms_login.html頁面
        return render_template("cms/cms_login.html", message=message)    # 針對post方法中同樣要返回到cms_login.html頁面進行代碼簡化
    
    # 用戶登錄操作驗證
    def post(self):
        # 收集表單信息
        login_form = LoginForm(request.form)
        if login_form.validate():
            # 數據庫驗證
            email = login_form.email.data
            password = login_form.password.data
            remember = login_form.remember.data
            
            # 查詢數據庫中的用戶信息
            user = CMS_User.query.filter_by(email=email).first()    # 郵箱唯一,用於查詢驗證用戶
            if user and user.check_password(password):              # 驗證用戶和密碼是否都正確
                session['user_id'] = user.id                        # 查詢到用戶數據時,保存session的id到瀏覽器
                # session['user_name'] = user.username                # 將數據庫中的user.username保存到session中,在hooks.py中判斷
                # session['user_email'] = user.email                  # 將數據庫中的email保存到session中,方便html調用信息
                # session['user_join_time'] = user.join_time          # 將數據庫中的join_time保存到session中,方便html調用信息
                
                if remember:                                        # 如果用戶點擊了remember選擇,在瀏覽器中進行數據持久化
                    session.permanent = True                        # 數據持久化,默認31天,需要設置session_key在config.py中
            
                # 登錄成功,跳轉到後臺首頁
                return redirect(url_for('cms.index'))               # 在藍圖中必須加cms   跳轉到index方法
            else:
                # return "郵箱或密碼錯誤"                              # 登錄出錯,返回結果
                # return render_template("cms/cms_login.html", message="郵箱或密碼錯誤")  # 登錄出錯,返回結果渲染到cms_login.html頁面
                return self.get(message="郵箱或密碼錯誤")             # 傳參到get方法中,多加一個傳輸錯誤信息的參數到方法中
        else:
            print(login_form.errors)                                 # forms.py中的錯誤信息  字典類型數據
            print(login_form.errors.popitem())                       # forms.py中的錯誤信息  元祖類型數據
            # return "表單驗證錯誤"                                   # 錯誤信息需要渲染到cms_login.html頁面
            return self.get(message=login_form.errors.popitem()[1][0])  # 字典類型數據信息提取
        
    
# 添加登錄路由
cms_bp.add_url_rule("/login/", view_func=LoginView.as_view('login'))    # view_func 命名操作名字,"/login/"路由地址

session中的用戶信息需要傳遞到apps/cms/hooks.py中進行判斷:
修改apps/cms/hooks.py文件

# -*- encoding: utf-8 -*-
"""
@File    : hooks.py
@Time    : 2020/5/13 9:36
@Author  : chen

"""
from flask import request, session, url_for, redirect, g   # g對象全局變量gloabl,方便調用
from .views import cms_bp
from .models import CMS_User


# 鉤子函數 ,所有操作前執行該方法,判斷當前界面是否是登錄界面,不是就將url重定向到登錄界面
@cms_bp.before_request
def before_request():
    print(request.path)                                         # 輸出的是網頁url的後綴,即/cms/login/
    if not request.path.endswith(url_for('cms.login')):         # 判斷當前所在url是否是/cms/login/,不是代表不在後臺登錄界面
        user_id = session.get('user_id')                        # 登陸之後,獲取登錄時候記錄的session中的user_id
        if not user_id:                                         # 若沒有user_id,說明登錄不成功
            return redirect(url_for('cms.login'))               # 重定向到後臺登錄界面

    # 判斷user_id是否登陸過,登錄之後就返回用戶名到CMS後臺管理系統
    if 'user_id' in session:
        user_id = session.get('user_id')                        # 調用session中user_id
        user = CMS_User.query.get(user_id)                      # 通過user_id查詢到用戶對象,方便前端界面調用對象中的字段屬性
        if user:
            g.cms_user = user                                   # 賦值給g對象,全局變量g.cms_user用於渲染到後臺管理界面cms_index.html

# 上面的代碼相對於下面的來說比較簡單,下面的是將對象中的字段屬性單獨來調用並修改爲全局變量,上面只是將完整的一個對象變成全局變量
'''
    # 判斷user_id是否登陸過,登錄之後就返回用戶名到CMS後臺管理系統
    if 'user_id' in session:                              # user_id在session中,說明cms用戶已經登錄了
        user_name = session.get('user_name')              # 從session中調用user_username
        user_email = session.get('user_email')            # 從session中調用user_email,用於設置爲全局變量,渲染到cms_profile.html中
        user_join_time = session.get('user_join_time')    # 從session中調用user_join_time,用於設置爲全局變量,渲染到cms_profile.html中

        if user_name:
            g.cms_user = user_name                        # 賦值給g對象,全局變量g.cms_user用於渲染到後臺管理界面cms_index.html
            g.cms_email = user_email                      # 設置爲全局變量,渲染到cms_profile.html中
            g.cms_join_time = user_join_time              # 設置爲全局變量,渲染到cms_profile.html中
'''    

添加新的用戶用於測試數據顯示是否正常:
在這裏插入圖片描述
在這裏插入圖片描述

7、模板繼承

創建新的模板文件templates/cms/cms_base.html文件

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>{% block title %}

    {% endblock %}</title>
    <script src="http://cdn.bootcss.com/jquery/3.1.1/jquery.min.js"></script>
    <link href="http://cdn.bootcss.com/bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet">
    <script src="http://cdn.bootcss.com/bootstrap/3.3.7/js/bootstrap.min.js"></script>

<!--  關聯本地的cms_base.css樣式 後臺管理界面CMS的樣式 -->
    <link rel="stylesheet" href="{{ url_for('static', filename='cms/css/cms_base.css') }}">
<!--  關聯本地的cms_base.js樣式 後臺管理界面CMS的樣式 -->
    <script src="{{ url_for('static', filename='cms/js/cms_base.js') }}"></script>

    {% block head %}

    {% endblock %}

</head>
<body>
     <nav class="navbar navbar-inverse navbar-fixed-top" role="navigation">
      <div class="container-fluid">
        <div class="navbar-header">
          <button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar" aria-expanded="false" aria-controls="navbar">
            <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="#">論壇CMS管理系統</a>
        </div>
        <div id="navbar" class="navbar-collapse collapse">
          <ul class="nav navbar-nav navbar-right">
        <!--       從數據庫中調用用戶名,g對象全局調用g.cms_user對象  .username是該對象的一個字段屬性      -->
            <li><a href="#">{{ g.cms_user.username }}<span>[超級管理員]</span></a></li>

        <!--  用戶註銷,關聯到views.py中的@cms_bp.route("/logout/")路由,重定向到該路由      -->
            <li><a href="{{ url_for('cms.logout') }}">註銷</a></li>
          </ul>
          <form class="navbar-form navbar-right">
            <input type="text" class="form-control" placeholder="查找...">
          </form>
        </div>
      </div>
    </nav>

    <div class="container-fluid">
      <div class="row">
          <div class="col-sm-3 col-md-2 sidebar">
              <ul class="nav-sidebar">
                <li class="unfold"><a href="#">首頁</a></li>
                <li class="profile-li">
                    <a href="#">個人中心<span></span></a>
                    <ul class="subnav">

                        <!--          url重定向到/cms/profile/下   這個路由在views.py中定義了       -->
                        <li><a href="{{ url_for('cms.profile') }}">個人信息</a></li>
                        <li><a href="#">修改密碼</a></li>
                        <li><a href="#">修改郵箱</a></li>
                    </ul>
                </li>

                <li class="nav-group post-manage"><a href="#">帖子管理</a></li>
                <li class="comments-manage"><a href="#">評論管理</a></li>
                <li class="board-manage"><a href="#">板塊管理</a></li>

                <li class="nav-group user-manage"><a href="#">用戶管理</a></li>
                <li class="role-manage"><a href="#">組管理</a></li>

                <li class="nav-group cmsuser-manage"><a href="#">CMS用戶管理</a></li>
                <li class="cmsrole-manage"><a href="#">CMS組管理</a></li>
            </ul>
          </div>
          <div class="col-sm-9 col-sm-offset-3 col-md-10 col-md-offset-2 main">
            <h1>{% block page_title %}

            {% endblock %}</h1>
            <div class="main_content">
                {% block content %}

                {% endblock %}
            </div>
          </div>
      </div>
    </div>
</body>
</html>

基礎模板文件創建後,其他的cms_xxx.html文件就可以進行模板繼承,不要大量的代碼來渲染頁面了

修改模板文件templates/cms/cms_index.html文件

<!--  繼承模板文件cms/cms_base.html  簡化代碼 -->
{% extends 'cms/cms_base.html' %}

<!-- 頁面標題 -->
{% block title %}
    CMS管理系統
{% endblock %}

<!--  標題  -->
{% block page_title %}
    歡迎來到CMS管理系統
{% endblock %}

<!-- 內容 -->
{% block content %}
    <!-- 引用相同的內容  -->
    {{ self.page_title() }}
{% endblock %}

模板文件templates/cms/cms_profile.html文件

<!--  繼承模板文件cms/cms_base.html  簡化代碼 -->
{% extends 'cms/cms_base.html' %}

<!-- 頁面標題 -->
{% block title %}
    個人信息
{% endblock %}

<!--  標題  -->
{% block page_title %}
    {{self.title()}}
{% endblock %}


{% block content %}
    <!--  將全局變量的對象在這個block模塊中命名爲user  -->
    {% set user = g.cms_user %}
    <table class="table table-bordered">
            <tr>
                <td>用戶名:</td>
                <!--  調用全局變量對象的username字段屬性  user替代g.cms_user       -->
                <td>{{ user.username }}</td>
            </tr>
            <tr>
                <td>郵箱:</td>
                <td>{{ user.email }}</td>
            </tr>
            <tr>
                <td>角色:</td>
                <td>暫未實現</td>
            </tr>
            <tr>
                <td>權限:</td>
                <td>暫未實現</td>
            </tr>
            <tr>
                <td>加入時間:</td>
                <td>{{ user.join_time }}</td>
            </tr>
    </table>
{% endblock %}

因爲需要在templates/cms/cms_profile.html文件中調用到登錄信息中的session的信息,所以需要針對apps/cms/views.py文件和apps/cms/hooks.py進行修改

視圖文件apps/cms/views.py

# -*- encoding: utf-8 -*-
"""
@File    : views.py
@Time    : 2020/5/11 9:59
@Author  : chen

"""
# 藍圖文件:實現模塊化應用,應用可以分解成一系列的藍圖   後端的類視圖函數寫在這個文件
from flask import Blueprint, render_template, views, session   # 定義類視圖,顯示模板文件
from flask import request, redirect, url_for          # 頁面跳轉redirect   request請求收集
# 導入form表單   .forms代表同級目錄下的forms.py
from .forms import LoginForm

# 導入模型  .models代表同級目錄下的models.py
from .models import CMS_User

# 導入裝飾器:判斷當前界面是否是登錄界面,不是就將url重定向到登錄界面
from .decorators import login_required

cms_bp = Blueprint("cms", __name__, url_prefix='/cms/')     # URL前綴url_prefix

# 鉤子函數是在cms_bp創建之後才創建的,順序在cms_bp創建之後
from .hooks import before_request


@cms_bp.route("/")                                          # 後臺界面
# @login_required             # 裝飾器判定當前界面是否是登錄界面,但是需要每個路由函數都要加該裝飾器,比較麻煩,推薦使用鉤子函數
def index():
    # return "cms index:後端類視圖文件"
    return render_template('cms/cms_index.html')       # 登陸之後進入CMS後臺管理界面,路徑寫全cms/cms_index.html


# 用戶註銷登錄
@cms_bp.route("/logout/")                              # 需要關聯到cms/cms_index.html中的註銷屬性
def logout():
    # session清除user_id
    del session['user_id']
    # 重定向到登錄界面
    return redirect(url_for('cms.login'))             # 重定向(redirec)爲把url變爲重定向的url


# 定義個人中心的路由
@cms_bp.route("/profile/")
def profile():
    return render_template("cms/cms_profile.html")   # 模板渲染(render_template)則不會改變url,模板渲染是用模板來渲染請求的url


# 定義類視圖,顯示模板文件   用戶登錄功能實現
class LoginView(views.MethodView):
    def get(self, message=None):                                         # message=None時候不傳輸信息到cms_login.html頁面
        return render_template("cms/cms_login.html", message=message)    # 針對post方法中同樣要返回到cms_login.html頁面進行代碼簡化
    
    # 用戶登錄操作驗證
    def post(self):
        # 收集表單信息
        login_form = LoginForm(request.form)
        if login_form.validate():
            # 數據庫驗證
            email = login_form.email.data
            password = login_form.password.data
            remember = login_form.remember.data
            
            # 查詢數據庫中的用戶信息
            user = CMS_User.query.filter_by(email=email).first()    # 郵箱唯一,用於查詢驗證用戶
            if user and user.check_password(password):              # 驗證用戶和密碼是否都正確
                session['user_id'] = user.id                        # 查詢到用戶數據時,保存session的id到瀏覽器
                # session['user_name'] = user.username                # 將數據庫中的user.username保存到session中,在hooks.py中判斷
                # session['user_email'] = user.email                  # 將數據庫中的email保存到session中,方便html調用信息
                # session['user_join_time'] = user.join_time          # 將數據庫中的join_time保存到session中,方便html調用信息
                
                if remember:                                        # 如果用戶點擊了remember選擇,在瀏覽器中進行數據持久化
                    session.permanent = True                        # 數據持久化,默認31天,需要設置session_key在config.py中
            
                # 登錄成功,跳轉到後臺首頁
                return redirect(url_for('cms.index'))               # 在藍圖中必須加cms   跳轉到index方法
            else:
                # return "郵箱或密碼錯誤"                              # 登錄出錯,返回結果
                # return render_template("cms/cms_login.html", message="郵箱或密碼錯誤")  # 登錄出錯,返回結果渲染到cms_login.html頁面
                return self.get(message="郵箱或密碼錯誤")             # 傳參到get方法中,多加一個傳輸錯誤信息的參數到方法中
        else:
            print(login_form.errors)                                 # forms.py中的錯誤信息  字典類型數據
            print(login_form.errors.popitem())                       # forms.py中的錯誤信息  元祖類型數據
            # return "表單驗證錯誤"                                   # 錯誤信息需要渲染到cms_login.html頁面
            return self.get(message=login_form.errors.popitem()[1][0])  # 字典類型數據信息提取
        
    
# 添加登錄路由
cms_bp.add_url_rule("/login/", view_func=LoginView.as_view('login'))    # view_func 命名操作名字,"/login/"路由地址

視圖文件apps/cms/hooks.py

# -*- encoding: utf-8 -*-
"""
@File    : hooks.py
@Time    : 2020/5/13 9:36
@Author  : chen

"""
from flask import request, session, url_for, redirect, g   # g對象全局變量gloabl,方便調用
from .views import cms_bp
from .models import CMS_User


# 鉤子函數 ,所有操作前執行該方法,判斷當前界面是否是登錄界面,不是就將url重定向到登錄界面
@cms_bp.before_request
def before_request():
    print(request.path)                                         # 輸出的是網頁url的後綴,即/cms/login/
    if not request.path.endswith(url_for('cms.login')):         # 判斷當前所在url是否是/cms/login/,不是代表不在後臺登錄界面
        user_id = session.get('user_id')                        # 登陸之後,獲取登錄時候記錄的session中的user_id
        if not user_id:                                         # 若沒有user_id,說明登錄不成功
            return redirect(url_for('cms.login'))               # 重定向到後臺登錄界面

    # 判斷user_id是否登陸過,登錄之後就返回用戶名到CMS後臺管理系統
    if 'user_id' in session:
        user_id = session.get('user_id')                        # 調用session中user_id
        user = CMS_User.query.get(user_id)                      # 通過user_id查詢到用戶對象,方便前端界面調用對象中的字段屬性
        if user:
            g.cms_user = user                                   # 賦值給g對象,全局變量g.cms_user用於渲染到後臺管理界面cms_index.html

# 上面的代碼相對於下面的來說比較簡單,下面的是將對象中的字段屬性單獨來調用並修改爲全局變量,上面只是將完整的一個對象變成全局變量
'''
    # 判斷user_id是否登陸過,登錄之後就返回用戶名到CMS後臺管理系統
    if 'user_id' in session:                              # user_id在session中,說明cms用戶已經登錄了
        user_name = session.get('user_name')              # 從session中調用user_username
        user_email = session.get('user_email')            # 從session中調用user_email,用於設置爲全局變量,渲染到cms_profile.html中
        user_join_time = session.get('user_join_time')    # 從session中調用user_join_time,用於設置爲全局變量,渲染到cms_profile.html中

        if user_name:
            g.cms_user = user_name                        # 賦值給g對象,全局變量g.cms_user用於渲染到後臺管理界面cms_index.html
            g.cms_email = user_email                      # 設置爲全局變量,渲染到cms_profile.html中
            g.cms_join_time = user_join_time              # 設置爲全局變量,渲染到cms_profile.html中
'''   

實現效果如下:
在這裏插入圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章