今天要繼續 user_bp
,今天會把驗證的部分處理掉。
理論上我們現在應該要寫一個資料庫的函式來處理新增使用者,但我們之前在寫 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
把它登入。
接下來要進入使用者設定的頁面,但進入路徑之前,我們必須先寫一點資料庫的東西。
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
,然後修改他的 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 等等,然後就再判斷他是不是管理員,如果是的話就再多顯示一些管理員的選項。
做完這些之後,就可以再打開網頁,然後看看他是不是順利判斷使用者的角色。