iT邦幫忙

2021 iThome 鐵人賽

DAY 24
0
Modern Web

Flask系列 第 24

Day 24 實作 user_bp (2)

  • 分享至 

  • xImage
  •  

前言

今天要繼續 user_bp,今天會把驗證的部分處理掉。

/register

理論上我們現在應該要寫一個資料庫的函式來處理新增使用者,但我們之前在寫 manage.py 的時候已經處理過了,所以就把舊的拿來用就好。

我們直接進入 HTML 的部分。

register.html

{% extends "base.html" %}

{% block title %}Register{% endblock %}

{% block content %}
<form action="/register" method="post">
    {{ form.csrf_token }}
    {{ form.username }}
    {{ form.password }}
    {{ form.repeat_password }}
    {{ form.email }}
    {{ form.submit }}
</form>
{% endblock %}

同樣地,他也是直接繼承 base.html。接著我們又使用了等等會從 render_template 送進來的 form,他當然就是 RegisterForm,所以我們乖乖把他的每一個欄位都填上去,當然也不要忘了 csrf_token

於是我們很快來到了路徑本身,基本上該引入的新東西昨天都引入了,額外需要的只有 RegisterForm 和在 app/database/ 裡面的 add_user

views.py

@user_bp.route("/register", methods=["GET", "POST"])
def register_page():
    if current_user.is_active:
        flash("You have logined.", category="info")
        return redirect(url_for("user.dashboard_page"))
    else:
        form = RegisterForm()
        if request.method == "GET":
            return render_template("register.html", form=form)
        if request.method == "POST":
            if form.validate_on_submit():
                username = form.username.data
                password = form.password.data
                email = form.email.data
                if add_user(username, password, email):
                    flash("Register successfully.", category="success")
                    return redirect(url_for("user.login_page"))
                else:
                    flash("The username or the email has been used.", category="alert")
                    return redirect(url_for("user.register_page"))
            else:
                for _, errors in form.errors.items():
                    for error in errors:
                        flash(error, category="alert")
                return redirect(url_for("user.register_page"))

跟昨天類似,我們先判斷使用者有沒有登入,如果沒有的話就處理這個頁面該處理的東西。接下來當表單驗證通過後,就繼續處理,把表單資料的使用者名稱、密碼、電子郵件抓出來,然後送給資料庫處理,處理過後如果傳回 False,那就代表這個使用者名稱或是電子郵件用過了,就 flash 出錯誤訊息;如果通過的話,那就重新導向到登入頁面給使用者登入,不過當然也可以在註冊結束後就直接用 login_user 把它登入。

/setting

接下來要進入使用者設定的頁面,但進入路徑之前,我們必須先寫一點資料庫的東西。

def user_to_dict(user_objects: list):
    li = []
    for user in user_objects:
        d = dict()
        d["id"] = user.id
        d["username"] = user.username
        d["email"] = user.email
        d["is_admin"] = user.is_admin
        d["introduction"] = user.introduction
        d["register_time"] = user.register_time.strftime("%Y-%m-%d %H:%M:%S")
        li.append(d)
    return li

def render_user_data(user_id):
    if user := Users.query.filter_by(id=user_id).first():
        return user_to_dict([user])[0]
    else:
        return False

def update_user_data(user_id, password=None, email=None, is_admin=None):
    filter = Users.query.filter_by(id=user_id)
    if filter.first():
        data = {}
        if password:
            data["password"] = generate_password_hash(password)
        if email:
            data["email"] = email
        if is_admin != None:
            data["is_admin"] = is_admin
        try:
            filter.update(data)
            db.session.commit()
            return True
        except:
            return "Username or email is used."
    else:
        return "The user does not exist."

裡面的 user_to_dict 是一個輔助的函式,基本上就是把使用者資料從 Users 物件換成 dict。render_user_data 則是透過 user_id 抓出使用者,接著再用剛剛的函式把他轉成 dict 然後傳回去。update_user_data 是用來更新使用者資訊的,邏輯上來說,我們會看它傳入了甚麼東西,然後一個一個把它更新,如果遇到 password 的話就要先 generate_password_hash,最後如果 update 失敗的話就代表使用者名稱或是電子郵件已經被使用 (unique constraint) (也有可能不是,但此處方便起見就先當是,可以自己寫一個 check 比較好),那就回傳一條錯誤訊息。

這邊比較要注意的是他更新的方法,我們可以注意到我們更新的東西是 query (但被 filter_by 過),而不是一個物件或是一個 list 的物件,如果再把它變成物件之後 (.all()),那就會錯誤。他更新需要使用 update 這個函式,他的參數是一個 dict,所以也可以直接把 kwargs 丟進去 (如果有使用的話)。

接下來要看到 HTML 的部分。

user_setting.html

{% extends "base.html" %}

{% block title %}Setting{% endblock %}

{% block content %}
<form action="/setting" method="post">
    {{ form.csrf_token }}
    {{ form.password }}
    {{ form.email }}
    {{ form.submit }}
</form>
{% endblock %}

一樣很簡單,這個 form 當然就是 UserSettingForm,可以去複習一下他的欄位。

最後就進入路徑了,廢話不多說直接來看。

views.py

@user_bp.route("/setting", methods=["GET", "POST"])
@login_required
def user_setting_page():
    data = render_user_data(current_user.id)
    form = UserSettingForm(email=data["email"])
    if request.method == "GET":
        return render_template("user_setting.html", form=form)
    if request.method == "POST":
        if form.validate_on_submit():
            password = form.password.data
            email = form.email.data
            if (
                msg := update_user_data(current_user.id, password=password, email=email)
            ) == True:
                flash("OK.", category="success")
            else:
                flash(msg, category="alert")
        else:
            for _, errors in form.errors.items():
                for error in errors:
                    flash(error, category="alert")
        return redirect(url_for("user.setting_page"))

在最一開始我們先抓出使用者的資料,接著我們建立 form,但這次我們加入了 email=data["email"],用這個方法就可以直接改掉 email 欄位的值,這樣就可以讓 email 直接顯示在前端,使用者如果不改的話就直接案送出即可。

後面基本上就跟之前類似,比較不同的是這次我們會直接把 update_user_data 的回傳值當成錯誤訊息然後 flash 出去。

base.html

在今天的最後,我們要回到 base.html,然後修改他的 nav。最一開始在寫的時候我們只是放了註解在那邊,但是沒有判斷到底甚麼時候要用甚麼 nav,今天我們就要改掉這個部分。


<nav>
        {% if current_user.is_anonymous %}
        <span><a href="/login">Login</a></span>
        <span><a href="/register">Register</a></span>
        {% else %}
        <span><a href="/dashboard">Dashboard</a></span>
        <span><a href="/posts">All posts</a></span>
        <span><a href="/post/add">Add post</a></span>
        <span><a href="/setting">Setting</a></span>
        {% if current_user.is_admin %}
        <span><a href="/admin_dashboard/posts">Admin Dashboard for posts</a></span>
        <span><a href="/admin_dashboard/comments">Admin Dashboard for comments</a></span>
        <span><a href="/manage_user">Manage User</a></span>
        {% endif %}
        {% endif %}
</nav>

我們直接使用了 current_user 這個變數,他不需要傳入,他本來就在,也跟我們在 flask 裡面用到的相同。一開始我們先判斷這個使用者是不是匿名,如果是的話那就只顯示 /login/register,如果不是的話,就顯示 dashboard 等等,然後就再判斷他是不是管理員,如果是的話就再多顯示一些管理員的選項。

做完這些之後,就可以再打開網頁,然後看看他是不是順利判斷使用者的角色。


上一篇
Day 23 實作 user_bp (1)
下一篇
Day 25 實作 user_bp (3)
系列文
Flask30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言