第5天上篇:在Flask應用中使用用戶認證—Flask_Login

原文: 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天的課程分爲上、下兩篇進行講解,這裏我們完成上篇登錄功能的實現,下篇我們將接着講解註冊功能,以及註冊後登錄功能的實現。

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