Flask

輕量級 Web 應用框架

入門入不了土

《建北電資 2026 聯合寒訓》

1 簡介 + 環境

2 前端

HTML, Jinja2, CSS

3 後端

SQLalchemy 

5 實作

4 前後端互動

WTForms

講師  Suzy (蘇西)

  • 北資一六 學術長
  • 2026 寒訓課程組組長

 

技能點:

  • Python
  • 機器學習  一點點 🤏
  • C++ 可以說是會跟不會差不多
  • 養了一隻 Labubu

成發問題可以標我

寶寶蘇西

心平氣和的蘇西

touching grass

hello

1 簡介

What is Flask

Flask 是一個使用 Python 語言開發的輕量級 Web 應用框架。近年來因為Python簡單易學的語法以及強大的生態系統,所以在Web開發領域也逐漸受到青睞。

  • 基本功能簡單但擴充性強
  • 自由度高

P.S. 據說它是一個裝酒的容器

常見 Python Web 框架

全棧框架 Full-stack

微框架 Micro-framework

現代化微框架 API-centric

內建強大的 Django ORM

需搭配 SQLalchemy 等數據庫

完整但較難上手

新手友善

效能高

Setup

1. 打開 VScode

2. 建立一個資料夾

pip install Flask

3. 建一個 python 檔案(ex: app.py)

4.

Hello World

from flask import Flask

app = Flask(__name__) #建立一個應用 (app)

@app.route('/') #'/' 是「根路徑」,定義主頁要顯示的內容 
def hello():
    return ("Hello, World!")

if __name__ == '__main__':
    app.run()
   
  • Flask 會預設在 port 5000
  • 執行 python app.py 後,在瀏覽器輸入 localhost:5000 應該就可以看到你的網頁了。

Hello World

if __name__ == "__main__":
    app.run(debug=True, port=5001)

debug 是指,當程式碼被更動並儲存後,網頁會自動更新。

Hello World

@app.route("/hello/<string:username>")
def hello(username):
    return f"Hello, {username}!"

可以透過 { } 插入特定值。(Jinja 語法,見後面介紹)

例如:localhost:5001/hello/suzy 就會輸出 “Hello, suzy!"

* 注意紙本講義勘誤

小實作 1

寫個東西然後成功的把它放到 localhost 上面

2 前端

@app.route('/')
def text():
    return '
    <html>
        <body><h1>Hello World</h1></body>
    </html>'

其實直接把 HTML 寫在 app.py 裡是可以的

但是當網頁比較複雜的時候

把 HTML 寫到另一個檔案可能比較合適

資料夾/
├── app.py
└── templates/
    └── index.html

在原本的資料夾下面創建一個 templates 資料夾

然後把 HTML 檔案放進去

from flask import Flask, render_template
app = Flask(__name__)

@app.route('/')
def index():
    return render_template('index.html')

if __name__ = "__main__":
    app.run(debug=True)

render_template 會自己去 templates 資料夾找到 html 並載入

Jinja 2: HTML 模板化

Flask 內建的模板引擎是 Jinja2。

它的主要功用是將 HTML 與 來自 Python 的資料結合,生成最終的文本檔案。

Jinja 2: HTML 模板化

若是網頁有很多地方都要寫 HTML 的話,我們可以用Jinja 寫一個基礎模板 (template) ,每次需要寫一個 HTML檔案時,就可以直接匯入模板,再按照需求更動裡面的部分內容即可,不用從頭開始寫。

Jinja 2 語法 - 流程控制

{% if sunny %}
<p> 今天天氣晴 </p>
{% else %}
<p> 天氣不好 </p>
{% endif %}

{% if 條件 %}

{% elif 條件 %}

{% else %}

{% endif %}

*勘誤:紙本講義上的程式碼漏掉 {% endif % }

@app.route('/')
def hello(sunny = True):
    return render_template('index.html')

index.html

app.py

Jinja 2 語法 - 迴圈

<h3>my list:</h3>
<ul>
    {% for fruit in fruits %}
        <li>{{ fruit }}</li>
    {% endfor %}
</ul>

{% for x in list %}

{{x}}

{% endfor %}

@app.route('/')
def show_fruits():
# python list
    fruit_list = ['蘋果', '香蕉', '西瓜', '草莓']
    return render_template('index.html', fruits=fruit_list)

index.html

app.py

Jinja 2 - HTML 模板

<!DOCTYPE html>
<html lang="en">
    <head>
        {% block head %} {% endblock %}

    </head>
    <body>
        {% block body %} {% endblock %}
    </body>
</html>

template.html

{% extends 'template.html' %}

{% block head %}
    <title>這是標題</title>
{% endblock %}

{% block body %}
    <h1>哈囉世界</h1>
{% endblock %}

 引用 template.html

 

CSS

讓網站好看一點

推薦的檔案路徑如下

資料夾/
├── app.py
└── templates/
    └── index.html
└── static/
    └── main.css

我也不會寫 CSS

CSS 實作沒想法的可以參考講義、網路上的模板或問AI

url_for

這裡介紹一個 Flask 裡面的 函式 url_for

  • 功能:透過函數名稱來反向找出對應的網址路徑
  • 經常搭配 redirect 使用
  • 優點:若更改路徑名稱,只要函式名稱沒有變,Flask 就還是可以幫你抓到正確的函式
from flask import Flask, render_template, redirect, url_for
# 記得 import 

@app.route('/admin')
def admin():
    # 如果沒權限,就把使用者踢回首頁
    return redirect(url_for('home'))

url_for

我們可以用這個方法連到 CSS 檔案

  • 功能:透過函數名稱來反向找出對應的網址路徑
  • 經常搭配 redirect 使用
  • 優點:若更改路徑名稱,只要函式名稱沒有變,Flask 就還是可以幫你抓到正確的函式
<link rel="stylesheet" href="{{url_for('static',filename='main.css')}}"

或者你也可以寫成這樣啦

<link rel="stylesheet" href="/static/main.css">

for 會寫 JS 的人:

載入 .js 檔案的方式是差不多的

<button id="Btn"> Click </button>

<script src="{{ url_for('static', filename='main.js') }}"></script>

小實作 2

讓你的網站上包含 >= 2 個頁面

並加入 CSS 模板

3 後端

前端(Front-end)與後端(Back-end)是網站與應用程式開發的兩大核心層次

 

前端專注於用戶可見的畫面、互動與視覺呈現,主要技術為 HTMLCSSJavaScript

 

後端則負責背後的運算邏輯、資料庫管理與伺服器運作,如 API 開發與資料處理。

簡言之,若網站要儲存大量資料或運算,我們大概還是需要個後端。

 SQL (Structured Query Language)

SQL(Structured Query Language,結構化查詢語言)是一種專門用來管理、操作及查詢「關聯式資料庫」的標準程式語言

OOP (Object Oriented Programming)

物件導向程式設計

ORM (Object-Relational Mapping)

物件關係映射

將資料庫中的「表格」映射為 Python 中的「類別(Class)」,將「資料列」映射為「物件(Instance)」。這樣一來,開發者可以用操作 Python 物件的方式來操作資料庫,而不需要寫原生的 SQL 語句

ORM 通常也有內建防止 SQL Injection (注入) 的機制,安全性高。

ORM (Object-Relational Mapping)

物件關係映射

圖片 from Google

SQLAlchemy

SQLAlchemy 是一款 Python Library,用於與關聯式資料庫進行互動。

  • 用 Python 語法撰寫 SQL Query,與資料庫進行互動。
  • 將 Python 物件映射到資料庫的 Table。可以直接用 OOP 的方式操作資料庫,無需直接使用 SQL 語言

SQLAlchemy

pip install flask-sqlalchemy

*注意:紙本講義上這裡好像有出錯

this is alchemy

在終端機輸入

from flask_sqlalchemy import SQLAlchemy

app.py 上面加上

SQLiteViewer

Vscode 可以裝這個擴充

(在Vscode 視窗左邊工具欄找到

然後搜尋)

URI (Uniform Resource Identifier)

統一資源識別碼

有點像是「地址」,用來識別某個唯一的資源

常見的 URL 其實是 URI 的一種

初始化資料庫

from flask import Flask, render_template
from flask_sqlalchemy import SQLAlchemy

app = Flask(__name__) 
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///items.db' 
				#代表在目前專案的目錄下建立一個名為 items.db 的檔案。
#URI = universal resource identifier
db = SQLAlchemy(app) # 建立 db 物件

建立資料庫

class User(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    username = db.Column(db.String(length=30), nullable=False, unique=True )  
    description = db.Column(db.String(length=100), nullable=False)
    # primary_key -> 用來定義每筆資料的 id
    # length -> 該欄位長度上限
    # nullable = False -> 該欄位不能留空
    # unique = True -> 不同筆資料間該欄位的內容不能重複(例如:name 必須是獨一無二的)

建立資料庫

class User(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    username = db.Column(db.String(length=30), nullable=False, unique=True )  
    description = db.Column(db.String(length=100), nullable=False)
    
    
    def __repr__(self):
        return f"Item('{self.id}', '{self.username}', '{self.description}')"
    # 注意這個函式是寫在 Class 裡面
	# 疑似紙本講義這裡有錯,自己注意一下(?)

如何手動建立一筆資料

flask shell

在終端機輸入

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

接著可以打

如何手動建立一筆資料

person1 = User(username = "小明", description = "一個人")
db.session.add(person1)
db.session.commit()

建立一筆資料的步驟

小實作 3

建立一個資料庫

並試著登入幾筆資料

4 前後端互動

如果要讓網站使用者輸入的資料可以登記到後端

要怎麼做

Cookies

Cookies 是伺服器傳送給瀏覽器的一小段文字資料。瀏覽器會把它存在用戶的瀏覽器(客戶端),並在下次你訪問同一個網站時,自動把這段資料帶回給伺服器。

講師選圖選很久噢

Sessions

由於 Cookies 存在用戶端不安全(容易被偽造),我們通常將敏感資料(如:User ID、購物車內容)存放在伺服器的 Session 中。

簡化過後,Session 的運作原理:

  1. 伺服器在後端開一個 Session,裡面放你的資料
  2. 伺服器只給瀏覽器一個 Session ID,並存放在 Cookie 中。
  3. 下次你來訪時,帶著 Session ID,伺服器就能找到你的個人資料。

 

圖片 by Google

WTForms (What the Forms)

WTForms是一個 Python 的函式庫,它提供了Python開發者在開發網站時,需要的表單呈現以及表單的驗證等功能。

pip install WTForms

Forms 的各種物件介紹

  • Forms 表單是 WTForms 最主要的核心容器。Forms 表單是 Fields 欄位的集合,可以選擇透過 python 字典 dictionary 來蒐集 Forms 的內容。

 

小知識— Python dictionary (字典)  語法

d = {key1: value1, key2: value2}

就是 key 跟 數值的配對而已啦

Forms 的各種物件介紹

  • 每個 Fields 欄位都代表某一種資料的類型,且 Fields 欄位限制使用者僅能夠輸入符合該資料類型的數據。e.g., IntegerField 與 StringField

 

  • 為了提供各種驗證規則,Fields 欄位包含了一系列的驗證方式。

初始化 Form

from wtforms import StringField, SubmitField
from wtforms.validators import DataRequired

app.config['SECRET_KEY']='mykey'
# 這邊先隨便設了一個 key 
if __name__ == '__main__':
    app.run(debug = True)

*注意:理論上應該要把 key 放在 .env 裡面,像這樣寫

import os
SECRET_KEY = os.getenv('SECRET_KEY')

app = Flask(__name__)
app.config['SECRET_KEY'] = SECRET_KEY
SECRET_KEY = '某個亂碼'

.env

app.py

建立一個 Form

class MyForm(FlaskForm):
    name = StringField('你的名字', validators=[DataRequired()])
    hobby = SelectField('你的興趣', choices=[('coding','打扣'),('gaming','遊戲'),('novels','小說')])
    others= TextAreaField()
    submit = SubmitField("確認")

建立 route

@app.route('/',methods=['GET','POST'])
def index():
    
    form = MyForm()
    if form.validate_on_submit():
        session['name'] = form.name.data
        session['hobby'] = form.hobby.data
        session['others'] = form.others.data
        return redirect(url_for('thankyou'))
    return render_template('home.html', form=form)

@app.route('/thankyou')
def thankyou():
    """thankyou頁"""
    return render_template('thankyou.html')

表單頁面

{% block content %}
<div class="container">
<form method="POST">
  {{form.hidden_tag()}}
 
    
  {{form.name.label(class='form-label')}} 
  {{form.name(class='form-control')}} 
  <br>
  
  {{form.hobby.label(class='form-label')}} 
  {{form.hobby(class='form-control')}}
  <br>
    
  {{form.others.label(class='form-label')}} 
  {{form.others(class='form-control')}}
  <br>

  {{form.submit(class='btn btn-primary')}}
</form>
</div>
{% endblock %}

home.html

thankyou.html 之類的就自己寫喔

完整程式碼

成發請不要直接抄它 </3

資料夾/
├── app.py
├── .env
├── static/
│   └── style.css     
└── templates/
    ├── home.html      
    └── thankyou.html
from flask import Flask, render_template, redirect, url_for, session
from flask_sqlalchemy import SQLAlchemy
from flask_wtf import FlaskForm
from wtforms import StringField, SelectField, SubmitField
from wtforms.validators import DataRequired 
from dotenv import load_dotenv
import os

load_dotenv()

SECRET_KEY = os.getenv('SECRET_KEY')

app = Flask(__name__)
app.config['SECRET_KEY'] = SECRET_KEY
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///items.db'
db = SQLAlchemy(app)

class User(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(length=30), nullable=False)
    hobby = db.Column(db.String(length=30), nullable=False)
    others = db.Column(db.String(length=100), nullable=False)

    def __repr__(self):
        return f"User('{self.name}', '{self.hobby}')"

class MyForm(FlaskForm):
    name = StringField('你的名字', validators=[DataRequired()])
    hobby = SelectField('你的興趣', choices=[('coding','打扣'),('gaming','遊戲'),('novels','小說')])
    others = StringField('想說的話', validators=[DataRequired()])
    submit = SubmitField('送出')

@app.route('/', methods=['GET', 'POST'])
def index():
    form = MyForm()
    if form.validate_on_submit():
        new_user = User(
            name=form.name.data,
            hobby=form.hobby.data,
            others=form.others.data
        )
        db.session.add(new_user)
        db.session.commit()
        
        session['name'] = form.name.data
        return redirect(url_for('thankyou'))
    
    return render_template('home.html', form=form)

@app.route('/thankyou')
def thankyou():
    name = session.get('name', ' ')
    return render_template('thankyou.html', name=name)

if __name__ == '__main__':
    with app.app_context():
        db.create_all()
    app.run(debug=True, port=5001)

app.py

body {
    font-family: "Courier New", Courier, monospace; 
    background-color: #ffffff;
    display: flex;
    justify-content: center;
    padding-top: 50px;
    color: #000000;
}

.form-container {
    background: #ffffff;
    padding: 2rem;
    border: 2px solid #000000; 
    width: 100%;
    max-width: 400px;
}

.form-group {
    margin-bottom: 20px;
}

label {
    display: block;
    margin-bottom: 8px;
    text-transform: uppercase; 
    font-size: 0.8rem;
    letter-spacing: 1px;
}

input, select {
    width: 100%;
    padding: 10px;
    border: 1px solid #000000;
    border-radius: 0;
    box-sizing: border-box;
    outline: none;
}

input:focus {
    background-color: #f0f0f0; 
}

.btn-submit {
    background-color: #000000;
    color: #ffffff;
    border: 1px solid #000000;
    padding: 12px;
    border-radius: 0;
    cursor: pointer;
    width: 100%;
    font-size: 14px;
    font-weight: bold;
    text-transform: uppercase;
    transition: all 0.2s ease;
}

.btn-submit:hover {
    background-color: #ffffff;
    color: #000000;
}

style.css

<!DOCTYPE html>
<html>
<head>
    <title>Home</title>
    <link rel="stylesheet" href="{{ url_for('static', filename='style.css') }}">
</head>
<body>
    <div class="form-container">
        <h2>請填寫資訊</h2>
        <form method="POST">
            {{ form.hidden_tag() }}
            
            <div class="form-group">
                {{ form.name.label }}
                {{ form.name() }}
            </div>

            <div class="form-group">
                {{ form.hobby.label }}
                {{ form.hobby() }}
            </div>

            <div class="form-group">
                {{ form.others.label }}
                {{ form.others() }}
            </div>

            {{ form.submit(class='btn-submit') }}
        </form>
    </div>
</body>
</html>

home.html

SECRET_KEY = 'asdfghjkl123456789' #某個亂碼

.env

5 成發

以小隊為單位

React 和 Flask 請(至少)擇一做成發

Flask 成發要求:要有前後端,但題材不限

亦可以結合其他的課程 ex: 機器學習

model = keras.models.load_model('mlp.keras')

(Google Colab)

用 Flask 部署模型 喜獲網站

謝謝大家

祝成發順利

Flask: 寒訓

By Suzy Huang

Flask: 寒訓

  • 45