今天我們要來處理 dashboard 的部分,但僅限於貼文的,留言的要留到明天。
第一個要來看的是 dashboard,有些東西還沒有寫好 (像是刪除之類的),會等寫完這邊再回去看。
一樣,先來看看資料庫方面的程式碼。
def get_posts(user_id=None, start=None, end=None):
query = Posts.query
if user_id:
query = query.filter_by(author_id=user_id)
if start:
query = query.filter(Posts.time > start)
if end:
query = query.filter(Posts.time < end)
posts = [post.id for post in query.all()]
posts = list(map(render_post, posts))
return posts
這邊我們讓他接收一些過濾器去做篩選,然後把過濾過的貼文都抓出來,再丟進昨天寫的 render_post
讓他轉成 dict。這邊值得注意一下的是我們有 filter
也有 filter_by
,後者是我們熟悉的,而前者也一樣是篩選器,只是它裡面放的是判斷式,所以如果之前的 filter_by
想要改成 filter
,那就要把 =
改成 ==
。
接下來進入 HTML,這次會比前面複雜一些,因為要加上顯示貼文。
dashboard.html
{% extends "base.html" %}
{% block title %}Dashboard{% endblock %}
{% block content %}
<form action="/dashboard" method="post">
{{ form.csrf_token }}
{{ form.start }}
{{ form.end }}
{{ form.submit }}
</form>
<div>
{% for post in posts %}
<div>
{{ post['author_id'] }}
<a href="/post/{{ post['id'] }}">{{ post['title'] }}</a>
{{ post['description'] }}
{{ post['time'] }}
<a href="/post/edit/{{ post['id'] }}">edit</a>
<a href="/post/delete/{{ post['id'] }}">delete</a>
</div>
{% endfor %}
</div>
{% endblock %}
最上面我們一樣放了表單,是 filter
那個。然後在下面我們用到了 posts
,這是剛剛 get_posts
弄出來的,所以會是一個有很多貼文 dict 的 list,我們就一個一個抓出來,然後把它的資訊呈現出來。我們放了很多連結,現在只有編輯的部分昨天實作了,剩下的我們會等等完成。
最後來到路徑本身,他也相對複雜。
@user_bp.route("/dashboard", methods=["GET", "POST"])
@login_required
def dashboard_page():
filter_args = {}
if start := request.cookies.get("start"):
filter_args["start"] = datetime.datetime.strptime(start, "%Y-%m-%d")
if end := request.cookies.get("end"):
filter_args["end"] = datetime.datetime.strptime(end, "%Y-%m-%d")
form = DashboardFilterForm(**filter_args)
if request.method == "GET":
posts = get_posts(user_id=None, **filter_args)
return render_template("dashboard.html", posts=posts, form=form)
if request.method == "POST":
response = make_response(redirect(url_for("user.dashboard_page")))
if form.validate_on_submit():
cookies = []
if form.start.data:
cookies.append(("start", form.start.data.strftime("%Y-%m-%d")))
if form.end.data:
cookies.append(("end", form.end.data.strftime("%Y-%m-%d")))
response.delete_cookie("start")
response.delete_cookie("end")
for cookie in cookies:
response.set_cookie(*cookie)
else:
for _, errors in form.errors.items():
for error in errors:
flash(error, category="alert")
return response
在判斷 request.method
前,我們有一小串跟其他路徑不一樣的程式碼,它是用來取出 cookie 的,因為我們會把使用者指定的 filter 存在 cookie 裡面。以現在的狀況來看這基本上是多餘的,直接把過濾過的貼文送出去就好,但如果未來我們加入分頁的功能,那就沒有這麼好處理了,所以我們在這邊選擇用 cookie。這個從 cookie 抓出來的 dict 除了要給 get_posts
用之外,也要給 DashboardFilterForm
當作預設值,這樣使用者就可以看到他設定的過濾器是什麼。後面的部分基本上就是處理 cookie,要把表單送過來的東西都轉成 cookie。
這裡會有一個小問題發生在處理日期結束的地方,假設我們說結束時間是 2021-01-02
,但事實上在資料庫時間比大小的時候 2021-01-02
的資料不會跑出來,因為我們說的結束時間會被當成 2021-01-02 00:00:00
,這樣就會讓那些 2021-01-02
的資料被過濾掉,平常我自己會直接手動加一天,不過為了簡潔一點,我這邊就跳過這個部分。
接著我們來看看刪除文章也一樣從資料庫的部分看起。
def delete_post(user_id, post_id):
post = Posts.query.filter_by(id=post_id).first()
if post.author_id == user_id:
db.session.delete(post)
db.session.commit()
return True
else:
return False
他有 user_id
、post_id
兩個參數,會需要前者是因為我們不能讓莫名其妙的人亂刪別人的文章,所以必須確定要刪除這篇文章的人的確是其所有者。我們刪除的方法是用 db.session.delete()
,裡面的參數是物件,而不是 query
,所以要記得 .first()
。
而他不會有 HTML,來看路徑就知道為什麼了。
@user_bp.route("/post/delete/<int:post_id>", methods=["GET"])
@login_required
def delete_post_page(post_id):
if delete_post(current_user.id, post_id):
flash("OK.")
else:
flash("Failed.")
return redirect(url_for("user.dashboard"))
他的路徑非常簡單,基本上就是把 post_id
轉送給剛剛的 delete_post
,最後再重新導向到 dashboard。