iT邦幫忙

2021 iThome 鐵人賽

DAY 25
0
Modern Web

Flask系列 第 25

Day 25 實作 user_bp (3)

前言

我們今天還是沒有離開 user_bp,我們要來弄寫文章的頁面,也就是 markdown 上場的日子。

/post/add

首先一樣要先來處理一下資料庫的部分,需要一個加入新貼文的函式。

def add_post(user_id, title, description, content):
    post = Posts(user_id, title, description, content)
    try:
        db.session.add(post)
        db.session.commit()
        return True
    except:
        return False

之前都是使用 Users 這個物件,這次要換成用 Posts 了。他會需要傳入一些貼文的基本資料,然後把他加入資料庫,如果有錯誤的話就代表他標題重複了 (空值的部分已經讓 Flask-WTF 處理掉了)。

接下來是 HTML 的部分,他有一些些跟一般表單不一樣的地方。

write.html

{% extends "base.html" %}

{% block title %}Post{% endblock %}

{% block content %}
{{ pagedown.include_pagedown() }}
<form action="{{ route }}" method="post">
    {{ form.csrf_token }}
    {{ form.title }}
    {{ form.description }}
    {{ form.content }}
    {{ form.submit }}
</form>
{% endblock %}

他有一個 {{ pagedown.include_pagedown() }},這是用來引入外部 JS 來及時顯示 markdown 用的。要注意它是 pagedown 的東西,而不是 form,而這個 pagedown 就是一開始在 create_app 裡面初始化用的那個。接下來會看到有一個 route 變數,這樣 render_template 的時候就可以傳入表單的提交路徑。

接下來就跟之前差不多了,比較可以看一下的是 form.content,我們用的是最直接簡單的方法,但也可以稍微調整他,有興趣的話可以去他的GitHub repo 看看範例。

最後就要來看路徑了,基本上跟之前的結構都一樣。

views.py

@user_bp.route("/post/add", methods=["GET", "POST"])
@login_required
def add_post_page():
    form = AddPostForm()
    if request.method == "GET":
        return render_template("write.html", form=form, route="/post/add")
    if request.method == "POST":
        if form.validate_on_submit():
            title = form.title.data
            description = form.description.data
            content = form.content.data
            if add_post(current_user.id, title, description, content):
                flash("Add post.")
                return redirect(url_for("user.dashboard_page"))
            else:
                flash("The title has been used.")
                return redirect(url_for("user.add_post_page"))
        else:
            for _, errors in form.errors.items():
                for error in errors:
                    flash(error, category="alert")
            return redirect(url_for("user.add_post_page"))

差不多就是把欄位都抓出來,然後 add_post,沒問題就跳到 dashboard,有問題就繼續待在這裡。但是在 render_template 的時候,我們加入了一個 route,就是剛剛 write.html 裡面的,決定表單要送去哪。

/post/edit

寫完文章就要來改文章,所以接下來我們來處理編輯文章的頁面。一樣,先來看看資料庫。


def edit_post(post_id, title, description, content):
    post = Posts.query.filter_by(id=post_id)
    try:
        data = {"title": title, "description": description, "content": content}
        post.update(data)
        db.session.commit()
        return True
    except:
        return False


def render_post(post_id):
    post = Posts.query.filter_by(id=post_id).first()
    return {
        "id": post.id,
        "author_id": post.author_id,
        "title": post.title,
        "description": post.description,
        "content": post.content,
        "comments": [
            {
                "author_id": comment.author_id,
                "content": comment.content,
            }
            for comment in post.comments
        ],
    }

這裡有 edit_postrender_post 兩個函式,前者是用來編輯文章,也就是把現有的文章抓出來再修改內容,所以用 post_id 當參數;而後者是把文章轉成 dict 送出來。

依照慣例,我們應該要來寫 HTML,但這次不需要,因為我們可以直接拿剛剛的 write.html 來用。所以我們可以直接進入路徑的部分,可想而知,也會跟剛剛十分類似。

@user_bp.route("/post/edit/<int:post_id>", methods=["GET", "POST"])
@login_required
def edit_post_page(post_id):
    post_data = render_post(post_id)
    form = AddPostForm(
        title=post_data["title"],
        description=post_data["description"],
        content=post_data["content"],
    )
    if request.method == "GET":
        return render_template("write.html", form=form, route=f"/post/edit/{post_id}")
    if request.method == "POST":
        if form.validate_on_submit():
            title = form.title.data
            description = form.description.data
            content = form.content.data
            if edit_post(post_id, title, description, content):
                flash("Post edited.")
                return redirect(url_for("user.dashboard_page"))
            else:
                flash("The title has been used.")
                return redirect(url_for("user.edit_post_page"))
        else:
            for _, errors in form.errors.items():
                for error in errors:
                    flash(error, category="alert")
            return redirect(url_for("user.edit_post_page"))

這裡有比較不同的部分是在最前面我們先用剛剛的 render_post 把貼文的資料送出來,然後在建立表單的時候就直接把資料傳進去,就像昨天弄使用者設定頁面一樣。接下來就十分類似,傳不同的 route 給 jinja 處理,後面就都一樣了。這邊的 url_for 多了一個 post_id 的參數,會有他是因為這個編輯文章的頁面有一個 post_id 的變數,如果不指定的話,flask 會無所適從,然後就噴錯誤了。

References

Create dynamic URLs in Flask with url_for()
Flask-PageDown


上一篇
Day 24 實作 user_bp (2)
下一篇
Day 26 實作 user_bp (4)
系列文
Flask30

尚未有邦友留言

立即登入留言