今天要继续 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 等等,然後就再判断他是不是管理员,如果是的话就再多显示一些管理员的选项。
做完这些之後,就可以再打开网页,然後看看他是不是顺利判断使用者的角色。
<<: Day 09: 【番外篇】关於写 Code 这件事 (待改进中... )
>>: Day 09: Creational patterns - Prototype
Day 14 - 安装与执行 YOLO 在 介绍影像辨识的处理流程 - Day 10 有提到 YOL...
「鲑鱼均,因为一场鲑鱼之乱被主管称为鲑鱼世代,广义来说以年龄和脸蛋分类的话这应该算是一种 KNN 的...
惨 ...
Hello, 各位 iT邦帮忙 的粉丝们大家好~~~ 本篇是 Re: 从零开始用 Xamarin 技...
今日文章目录 ToDoList 需求 事前准备 参考资料 ToDoList 需求 Q: 需要有哪些...