原文: http://www.catonlinepy.tech/
聲明: 原創不易,未經許可,不得轉載
1. 你將學會什麼
今天的課程主要給大家介紹一款非常好用的Flask插件—Flask_Login,該插件主要用來管理用戶的登陸狀態。通過今天的學習,你將學會如何維護用戶的登錄、登出狀態。教程中的代碼都會託管到github上,貓姐一再強調,在學習本課內容時一定要親自動手實現代碼,遇到問題再到github上查看代碼,如果實在不知道如何解決,可以在日誌下方留言。
2. 使用Flask_Login
2.1 項目目錄結構的創建
當用戶登錄某個應用時,應用需要記住該用戶的登陸狀態。在開發過程中,如果我們自己造輪子,去實現管理用戶登錄狀態的代碼,就會浪費大量時間。但現實中,我們往往需要聚焦在業務上的開發,Flask_Login就是前人造好的管理用戶登錄狀態的輪子,並且這個插件上手起來毫無難度。照舊,在正式使用Flask_Login插件之前,還是先建立今天的項目目錄,如下:
# 進入到虛擬環境目錄,激活虛擬環境
maojie@Thinkpad:~/flask-plan/$ source miao_venv/bin/activate
# 到flask-course-primary目錄下創建第五天的課程day5目錄
(miao_venv) maojie@Thinkpad:~/flask-plan/flask-course-primary$ mkdir day5
# 進入day5目錄
(miao_venv) maojie@Thinkpad:~/flask-plan/flask-course-primary$ cd day5
# 新建userauth_demo目錄
(miao_venv) maojie@Thinkpad:~/flask-plan/flask-course-primary/day5$ mkdir userauth_demo
# 進入到userauth_demo目錄
(miao_venv) maojie@Thinkpad:~/flask-plan/flask-course-primary/day5$ cd userauth_demo/
# 在userauth_demo目錄中新建__init__.py文件
(miao_venv) maojie@Thinkpad:~/flask-plan/flask-course-primary/day5/userauth_demo$ touch __init__.py
# 在userauth_demo包中新建routes.py路由文件
(miao_venv) maojie@Thinkpad:~/flask-plan/flask-course-primary/day5/userauth_demo$ touch routes.py
# 在userauth_demo包中新建models.py文件
(miao_venv) maojie@Thinkpad:~/flask-plan/flask-course-primary/day5/userauth_demo$ touch models.py
# 在userauth_demo包中新建forms.py文件
(miao_venv) maojie@Thinkpad:~/flask-plan/flask-course-primary/day5/userauth_demo$ touch forms.py
# 在userauth_demo包中新建config.py文件
(miao_venv) maojie@Thinkpad:~/flask-plan/flask-course-primary/day5/userauth_demo$ touch config.py
# 在userauth_demo包中新建templates目錄
(miao_venv) maojie@Thinkpad:~/flask-plan/flask-course-primary/day5/userauth_demo$ mkdir templates
# 進入到templates目錄
(miao_venv) maojie@Thinkpad:~/flask-plan/flask-course-primary/day5/userauth_demo$ cd templates/
# 在templates目錄中新建layout.html
(miao_venv) maojie@Thinkpad:~/flask-plan/flask-course-primary/day5/userauth_demo/templates$ touch layout.html
# 在templates目錄中新建login.html
(miao_venv) maojie@Thinkpad:~/flask-plan/flask-course-primary/day5/userauth_demo/templates$ touch login.html
# 在templates目錄中新建register.html
(miao_venv) maojie@Thinkpad:~/flask-plan/flask-course-primary/day5/userauth_demo/templates$ touch register.html
# 在templates目錄中新建index.html
(miao_venv) maojie@Thinkpad:~/flask-plan/flask-course-primary/day5/userauth_demo/templates$ touch index.html
# 在day5目錄下新建run.py文件
(miao_venv) maojie@Thinkpad:~/flask-plan/flask-course-primary/day5/$ touch run.py
最終,我們得到今天項目的目錄結構如下(使用tree命令得到):
(miao_venv) maojie@Thinkpad:~/flask-plan/flask-course-primary$ tree day5
day5
├── run.py
└── userauth_demo
├── config.py
├── database.db
├── forms.py
├── __init__.py
├── models.py
├── routes.py
└── templates
├── index.html
├── layout.html
├── login.html
└── register.html
安裝Flask_Login插件的方式與其它插件的安裝方式一樣,使用pip命令,如下:
# 注意,一定要在虛擬環境中
(miao_venv) maojie@Thinkpad:~/flask-plan/flask-course-primary/day5$ pip install Flask_Login
然後在__init__.py文件中對Flask_Login進行配置,大家首先把第4天__init__.py文件中的內容複製過來,然後添加如下代碼:
# ...
from flask_login import LoginManager
app = Flask(__name__)
# ...
login_manager = LoginManager(app)
# ...
2.2 準備用戶的登陸模型
在正式創建登錄模型之前,我們先介紹一下config.py文件,該文件的作用存放與應用相關的所有配置信息。在之前的教程中,我們直接將配置信息寫到了__init__.py文件中,這樣不利於複雜 web應用程序的開發管理,後續章節的配置內容都將會放到config.py文件中,配置代碼如下所示:
# config.py文件中的內容
import os
basedir = os.path.abspath(os.path.dirname(__file__))
class Config(object):
SQLALCHEMY_DATABASE_URI = 'sqlite:///' + os.path.join(basedir, 'database.db')
# 使用表單,對Flask_WTF進行配置
SECRET_KEY = 'miaojie is great!'
__init__.py文件只需添加如下代碼便可導入config.py文件中所有配置信息:
# __init__.py文件中的內容
# ..
from userauth_demo.config import Config
app = Flask(__name__)
app.config.from_object(Config)
# ..
建立用戶的登陸模型是創建數據庫表的過程,在第4課中,我們談到了如何去建立用戶模型,這裏不再詳細講解,直接使用即可。要想使用Flask_Login插件,在模型中需要實現下面幾個方法(函數),如下:
方法 說明
is_authenticated() 如果用戶已經登錄,必須返回 True ,否則返回 False
is_active() 如果允許用戶登錄,必須返回 True ,否則返回 False 。如果要禁用賬戶,可以返回 False
is_anonymous() 對普通用戶必須返回 False
get_id() 必須返回用戶的唯一標識符,使用 Unicode 編碼字符串
但是Flask_Login提供了一個更簡便的方法—UserMixin類,開發人員只需要讓User類繼承UserMixin類即可完成對User表的登錄管理,UserMixin包含這4個方法的默認實現(因此上面這張表的內容大家完全不用關心)。由於後面的代碼會越來越複雜,爲了使代碼更易管理,所有建立數據庫模型的代碼將放在models.py文件中。如下所示,在models.py中建立User模型:
# 在models.py文件中的內容
#!coding:utf8
from userauth_demo import db
from flask_login import UserMixin
# User繼承UserMixin類
class User(UserMixin, db.Model):
__tablename__ = 'user'
id = db.Column(db.Integer, primary_key=True)
username = db.Column(db.String(20), unique=True, nullable=False)
email = db.Column(db.String(120), unique=True, nullable=False)
password = db.Column(db.String(60), nullable=False)
def __repr__(self):
return f"User('{self.username}','{self.email}','{self.password}')"
# 在這個模型中,還添加了email字段,因爲在用戶登陸的時候,不僅可以輸入用戶名登陸,也可以輸入email登陸
第4課中已經講了數據庫的遷移,大家可以直接按照第4課的5.3小結內容進行操作即可,同樣在操作之前,需要設置環境變量,環境變量在第1課的3節最後已經講過如何設置了。
最後,Flask_Login插件要求程序有一個回調函數,可以加載指定ID的用戶。在models.py文件中增加如下代碼:
# ..
# 從userauth_demo包中導入login_manager實例
from userauth_demo import login_manager
# 回調函數
@login_manager.user_loader
def load_user(user_id):
# print調試時用
#print(user_id)
return User.query.get(int(user_id))
# ..
2.3 添加登陸表單
表單在第三課中已經講到過,現在將表單直接拿過來用,所有建立表單的代碼都將放在forms.py文件中,在forms.py文件中添加登陸表單內容:
# 在forms.py文件中的內容
from flask_wtf import FlaskForm
from wtforms.fields.html5 import EmailField
from wtforms import PasswordField, BooleanField, SubmitField
from wtforms.validators import DataRequired, Length
class LoginForm(FlaskForm):
# 表單包括郵箱,密碼和是否記住密碼及一個提交按鈕
email = EmailField(u'郵箱', validators=[DataRequired(), Length(min=2, max=20)])
password = PasswordField(u'密碼', validators=[DataRequired()])
remember = BooleanField(u'記住我')
submit = SubmitField(u'登陸')
在表單中email字段使用了WTForms提供的Length()驗證函數,確保用戶輸入的email長度在2到20的範圍內,PasswordField可使Jinja2生成type="password"的<input>標籤;BooleanField可使Jinjia2生成true或false的可選框;SubmiteField可使Jinjia2生成type="submit"的<input>標籤。
2.4 表單的渲染
在第二課中已經講到過模板的繼承概念,今天的課程會繼續用到這一概念,這裏就不重複描述了(大家如果不清楚,可回去再看看第2天的課程)。在templates/layout.html中輸入如下html代碼:
<!-- layou.html文件中的內容 -->
<html>
<head>
{% if title %}
<title>{{ title }}-喵星在線</title>
{% else %}
<title>喵星在線</title>
{% endif %}
</head>
<header>
<div>
<a href="/">主頁</a>
<a href="/login">登陸</a>
</div>
</header>
<body>
<!-- 渲染flash消息 -->
{% with messages = get_flashed_messages(with_categories=true) %}
{% if messages %}
{% for category, message in messages %}
<div class="alert alert-{{ category }}">
{{ message }}
</div>
{% endfor %}
{% endif %}
{% endwith %}
{% block content %}
{% endblock %}
</body>
</html>
然後通過子模板index.html繼承基模板layout.html,在templates/index.html中輸入如下代碼:
<!-- index.html文件中的內容 -->
{% extends "layout.html" %}
<!--重新填寫content塊的內容-->
{% block content %}
<h1>你好,喵星在線!</h1>
{% endblock %}
此外,我們還需要一個登錄頁面,讓我們可以輸入郵箱和密碼,在templates/login.html中輸入如下代碼:
<!-- login.html文件中的內容 -->
{% extends "layout.html" %}
{% block content %}
<h2>登陸</h2>
<form action="" method="post">
{{html_form.hidden_tag() }}
<div>
{{ html_form.email.label }}<br />
{{ html_form.email() }}
{% if html_form.email.errors %}
<div>
{% for error in html_form.email.errors %}
<span>{{ error }}</span>
{% endfor %}
</div>
{% endif %}
</div>
<div>
{{ html_form.password.label }}<br />
{{ html_form.password() }}
</div>
<div>
{{ html_form.remember.label }}
{{ html_form.remember() }}
</div>
<div>
{{ html_form.submit() }}
</div>
</form>
{% endblock content %}
上面已經完成login.html頁面前臺form表單的顯示(渲染)工作,這時就需要在視圖函數中(python文件)將代表表單的類傳遞到前端模板文件(html文件)中,下面在routes.py中完成視圖函數的編寫:
# routes.py文件中的內容
#!coding:utf8 # 文件中有中文,必須在代碼最前面註明utf8編碼,否則程序拉起時會報錯
from flask import render_template, redirect, url_for, flash
from userauth_demo import app
from userauth_demo.forms import LoginForm # 從userauth_demo.forms中導入LoginForm類
from flask_login import current_user
from userauth_demo.models import User
# 添加視圖函數渲染主頁內容
@app.route('/')
def index():
return render_template('index.html', title='第五天')
# 在視圖函數中將form表單傳到前端html文件中去
@app.route('/login', method=['GET','POST'])
def login():
if current_user.is_authenticated:
return redirect(url_for('index'))
# 對類LoginForm的實例
form = LoginForm()
if form.validate_on_submit():
if form.email.data == '[email protected]' and form.password.data == 'miaojie':
flash('你已經登陸成功', 'success') # flash消息的渲染
return redirect(url_for('index')) # 如果登陸成功,則重定向到主頁
# 將視圖函數中的變量form傳到模板login.html文件中去
return render_template('login.html', title='第五天', html_form=form)
current_user是由Flask_Login導入的,它在視圖函數和模板中可以直接使用。它表示的是當前登陸的用戶,如果用戶未登陸,則表示用戶是匿名用戶,is_authenticated則會返回False。當在表單中填好內容後,點擊提交按鈕時,validate_on_submit發送的是post請求,if判斷語句中填入的是假數據,因爲數據還沒有寫入到數據庫,無法使用數據庫將用戶的郵箱和密碼讀取出來,後面會講到用戶的註冊,將用戶的信息寫到數據庫中。
在run.py文件中輸入如下代碼,將web程序拉起:
from userauth_demo import app # 從userauth_demo包中導入app實例
if __name__ == "__main__":
app.run(debug=True, port=5005) # app實例調用自己的run函數
# 在app.run裏面可以指定端口號,當下次再運行python run.py時,可能提示端口號被佔用,此時,可以修改run.py文件中的端口號
現在,所有代碼已編寫完成,通過python run.py將web程序拉起,開始驗證結果,在瀏覽器中輸入http://127.0.0.1:5005/login,效果圖如下所示:
在登陸表單中輸入用戶名及密碼,點提交按鈕後的效果圖:
3. 上篇完,未完待續...
由於第5天的課程涉及登錄和註冊兩方面的知識,內容比較多,因此貓姐將第5天的課程分爲上、下兩篇進行講解,這裏我們完成上篇登錄功能的實現,下篇我們將接着講解註冊功能,以及註冊後登錄功能的實現。