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中
'''
實現效果如下: