使用Flask框架製作網站─公告欄網頁程式

前情提要

之前我們的 Flask 公告欄網站已經完成網頁的製作了,在這一篇文章我們要完成網站程式的建置,也就是系列文章第一篇中完成的app.py,在這個章節中我們會建立資料庫,完成資料庫運作的程式,下篇文章會與公告欄網頁串聯,並測試產品運作狀態。

上次我們的app.py做到這邊:

from flask import Flask #引入Flask模組

app = Flask(__name__) #告訴Flask我們現在要執行一個叫做app的網站程式,__name__是Flask內的模組名稱

@app.route("/") #描述app網站程式的路徑,"/"表示是首頁
def home(): #定義首頁要運作的功能
    return "Hello World. I am Finrodchen!" #回傳Hello訊息

if __name__ == "__main__":
    app.run() #設定若__name__這個模組被指定為主要網站模組時,則啟動這app這個網站,執行app中的功能

工作流程

1) 資料庫 kanban.db 2) 建立新增公告功能 3) 建立刪除公告功能 4) 建立更新公告功能 5) 本機建立資料庫

需要的模組

  • flask (網站框架)
  • flask_sqlalchemy (適用於flask框架的資料庫擴充模組)
  • datetime

引入模組:

from flask import Flask, render_template, request, redirect
#render_template 用於渲染網頁,將base.html的設定分享其他網頁
#request和redirect則是用來將接收或送出網頁的資料,擔任資料庫與前端網頁的溝通橋樑

from flask_sqlalchemy import SQLAlchemy
from datetime import datetime

資料庫 kanban.db

app.py中我們要先建立儲存公告內容的資料庫,首先我們要設定資料庫的名稱和儲存位置,接著將資料庫在app.py中定義為db

app = Flask(__name__) #告訴Flask我們現在要執行一個叫做app的網站程式,__name__是Flask內的模組名稱

app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///kanban.db'

# 設定資料庫格式為SQLite,儲存在根目錄下,名稱為kanban.db

db = SQLAlchemy(app)

# 將app裡由SQLAlchemy建立的資料庫定義為db

接著我們定義資料庫中的第一個資料表為Todo,接著定義資料表中各欄位要儲存的資料及屬性。

class Todo(db.Model):
    id = db.Column(db.Integer, primary_key = True)
    #第一欄定義為ID,整數,用於排序

    priority = db.Column(db.String(200), nullable = False)
    #第二欄定義為優先順序,字串,長度限制為200位元

    title = db.Column(db.String(200), nullable = False)
    #第三欄定義為任務標題,字串,長度限制為200位元

    content = db.Column(db.String(400), nullable = False)
    #第四欄定義為任務內容,字串,長度限制為200位元

    people = db.Column(db.String(200), nullable = False)
    #第四欄定義為任務負責人,字串,長度限制為200位元

    completed = db.Column(db.Integer, default = 0)
    #第五欄定義為任務完成狀態,整數,0或1

    date_created = db.Column(db.DateTime, default = datetime.utcnow)
    #第六欄定義為任務建立時間,日期,預設為建立任務裝置的本地時間

    def __repr__(self):
        return '<Task %r>' % self.id
    #將網頁收到的資料寫入資料庫中

新增公告功能

接下來要把主要功能都建立起來,首先是新增公告的功能:

新增公告的功能將直接出現在首頁,因此我們要在@app.route("/")下面定義首頁的程式。

@app.route("/", methods=['POST', 'GET'])

# 在首頁運作程式,網頁中會使用到POST和GET兩種機制

def index():
    if request.method == 'POST':
    #收到來自網頁的POST時,執行以下程式

        task_priority = request.form['priority']
        task_title = request.form['title']
        task_content = request.form['content']
        task_people = request.form['people']
        #從表格中對應的欄位收集資料存到變數中

        new_task = Todo(priority=task_priority, title=task_title, content=task_content, people=task_people)
        #將以上變數存到db對應的欄位中

        try:
            db.session.add(new_task)
            db.session.commit()
            return redirect('/')
            #將任務資料存入db,並顯示於首頁中

        except:
            return "Fail to add new issue to your task."
            #如果db寫入失敗了顯示錯誤訊息
    else:
        tasks = Todo.query.order_by(Todo.date_created).all()
        return render_template('index.html', tasks=tasks)
        #沒有POST新任務的話,就顯示目前資料中的任務
        #首頁使用的模板是index.html

刪除公告功能

接著是刪除公告功能,刪除功能建立在首頁裡面,不需要使用網頁模板,點選後就會刪除對應列的公告。

@app.route("/delete/<int:id>")
#在/delete/<int:id>按鈕執行以下程式

def delete(id):
    task_to_delete = Todo.query.get_or_404(id)
    #將資料庫收到刪除要求的任務存到變數中

    try:
        db.session.delete(task_to_delete)
        db.session.commit()
        return redirect("/")
        #從資料庫中刪除任務,將結果回傳到首頁

    except:
        return "Deleteing problem."
        #如果db刪除失敗了顯示錯誤訊息

更新公告功能

更新公告功能需要用到另一個update.html網頁,網頁中的表格會先讀取目前公告的內容,使用者修正送出後改寫資料庫中的資料。

@app.route("/update/<int:id>", methods=['POST', 'GET'])

# 在/update/<int:id>按鈕執行以下程式,網頁中會使用到POST和GET兩種機制

def update(id):
    task = Todo.query.get_or_404(id)
    #將資料庫收到更新要求的任務存到變數中

    if request.method == "POST":
        task.priority = request.form['priority']
        task.title = request.form['title']
        task.content = request.form['content']
        task.people = request.form['people']
        #從表格中對應的欄位收集資料存到變數中

        try:
            db.session.commit()
            return redirect("/")
            #更新資料庫,將結果回傳到首頁

        except:
            return "Updating issue."
            #如果db更新失敗了顯示錯誤訊息
    else:
        return render_template('update.html', task=task)
        #更新頁面使用的模板是update.html

最後再加上Flask框架的執行程式碼就完成啦!

if __name__ == "__main__":
    app.run() #設定若__name__這個模組被指定為主要網站模組時,則啟動這app這個網站,執行app中的功能

本機建立資料庫

程式完成之後,我們必須先在本機建立資料庫kanban.db,才能開始進行測試:

首先我們要進到Python cosole中,並切換目錄到app.py所在的資料夾:

Python 3.7.7 (default, May  6 2020, 11:45:54) [MSC v.1916 64 bit (AMD64)] :: Anaconda, Inc. on win32

Type "help", "copyright", "credits" or "license" for more information.
>>> 

接著輸入from app import db,從app.py中引入資料庫架構。

最後輸入db.create_all(),就完成資料庫建立了。

>>> from app import db
>>> db.create_all()
>>> 

可以看到在資料夾中新增了一個kanban.db檔案。

到這邊就完成網站程式的部分了,下一篇文章我們會回頭修改index.htmlupdate.html兩個網頁,加入與資料庫互動的功能,完成後便可以在本機進行測試了!

完整程式碼

from flask import Flask, render_template, request, redirect

# render_template 用於渲染網頁,將base.html的設定分享其他網頁

# request和redirect則是用來將接收或送出網頁的資料,擔任資料庫與前端網頁的溝通橋樑

from flask_sqlalchemy import SQLAlchemy
from datetime import datetime

app = Flask(__name__) #告訴Flask我們現在要執行一個叫做app的網站程式,__name__是Flask內的模組名稱

app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///kanban.db'

# 設定資料庫格式為SQLite,儲存在根目錄下,名稱為kanban.db

db = SQLAlchemy(app)

# 將app裡由SQLAlchemy建立的資料庫定義為db

class Todo(db.Model):
    id = db.Column(db.Integer, primary_key = True)
    #第一欄定義為ID,整數,用於排序

    priority = db.Column(db.String(200), nullable = False)
    #第二欄定義為優先順序,字串,長度限制為200位元

    title = db.Column(db.String(200), nullable = False)
    #第三欄定義為任務標題,字串,長度限制為200位元

    content = db.Column(db.String(400), nullable = False)
    #第四欄定義為任務內容,字串,長度限制為200位元

    people = db.Column(db.String(200), nullable = False)
    #第四欄定義為任務負責人,字串,長度限制為200位元

    completed = db.Column(db.Integer, default = 0)
    #第五欄定義為任務完成狀態,整數,0或1

    date_created = db.Column(db.DateTime, default = datetime.utcnow)
    #第六欄定義為任務建立時間,日期,預設為建立任務裝置的本地時間

    def __repr__(self):
        return '<Task %r>' % self.id
    #將網頁收到的資料寫入資料庫中

@app.route("/", methods=['POST', 'GET'])

# 在首頁運作程式,網頁中會使用到POST和GET兩種機制

def index():
    if request.method == 'POST':
    #收到來自網頁的POST時,執行以下程式

        task_priority = request.form['priority']
        task_title = request.form['title']
        task_content = request.form['content']
        task_people = request.form['people']
        #從表格中對應的欄位收集資料存到變數中

        new_task = Todo(priority=task_priority, title=task_title, content=task_content, people=task_people)
        #將以上變數存到db對應的欄位中

        try:
            db.session.add(new_task)
            db.session.commit()
            return redirect('/')
            #將任務資料存入db,並顯示於首頁中

        except:
            return "Fail to add new issue to your task."
            #如果db寫入失敗了顯示錯誤訊息
    else:
        tasks = Todo.query.order_by(Todo.date_created).all()
        return render_template('index.html', tasks=tasks)
        #沒有POST新任務的話,就顯示目前資料中的任務
        #首頁使用的模板是index.html

@app.route("/delete/<int:id>")

# 在/delete/<int:id>按鈕執行以下程式

def delete(id):
    task_to_delete = Todo.query.get_or_404(id)
    #將資料庫收到刪除要求的任務存到變數中

    try:
        db.session.delete(task_to_delete)
        db.session.commit()
        return redirect("/")
        #從資料庫中刪除任務,將結果回傳到首頁

    except:
        return "Deleteing problem."
        #如果db刪除失敗了顯示錯誤訊息

@app.route("/update/<int:id>", methods=['POST', 'GET'])

# 在/update/<int:id>按鈕執行以下程式,網頁中會使用到POST和GET兩種機制

def update(id):
    task = Todo.query.get_or_404(id)
    #將資料庫收到更新要求的任務存到變數中

    if request.method == "POST":
        task.priority = request.form['priority']
        task.title = request.form['title']
        task.content = request.form['content']
        task.people = request.form['people']
        #從表格中對應的欄位收集資料存到變數中

        try:
            db.session.commit()
            return redirect("/")
            #更新資料庫,將結果回傳到首頁

        except:
            return "Updating issue."
            #如果db更新失敗了顯示錯誤訊息
    else:
        return render_template('update.html', task=task)
        #更新頁面使用的模板是update.html

if __name__ == "__main__":
    app.run() #設定若__name__這個模組被指定為主要網站模組時,則啟動這app這個網站,執行app中的功能