01204223/flask
- เป็นส่วนหนึ่งของวิชา 01204223
ในขั้นแรกของการฝึกพัฒนาโปรแกรมประยุกต์บนเว็บ เราจะเริ่มโดยการใช้ Flask ที่เป็นเฟรมเวิร์คในการพัฒนาเว็บบน Python ที่มีความเรียบง่าย เพื่อให้เราให้ภาพรวมคร่าว ๆ ก่อน จากนั้นเราจะขยับขยายไปใช้ระบบอื่น ๆ ต่อไป
เอกสารนี้จะเป็นเอกสารประกอบการเรียนด้วยคลิปตามรายการด้านล่าง
- Flask ตอน 1 - เริ่มต้น เรียก server การใช้ route และการ render template เบื้องต้น
- Flask ตอน 2 - การส่งข้อมูลไปยัง template, คำสั่งใน template jinja, การสร้างลิงก์ด้วย tag a, การส่งพารามิเตอร์ทาง url, และการใช้ url_for
- Flask ตอน 3 - การใช้ form ในการส่งข้อมูล การใช้ redirect
ในคลิปดังกล่าวจะไม่ได้มีการใช้ git ประกอบการทำ แต่เราจะพยายามแสดงจุดที่ควรจะ commit งานเป็นระยะ ดังนั้นระหว่างการหัดใช้ Flask ให้ทดลองใช้ git ไปด้วยเลย
เนื้อหา
เริ่มต้น และการสร้าง repository
เราจะสร้าง directory สำหรับทำโครงงาน todo list แบบง่าย (ไม่มี database) ให้สร้างไดเร็กทอรีและย้ายไปในไดเร็กทอรีนั้น แล้วสั่ง
git init
เพื่อเริ่มสร้าง git repository สำหรับการทำงาน
การติดตั้งและเริ่มเปิดหน้าเว็บ
Flask เป็น library บน python ซึ่งเราจะติดตั้งผ่านทางคำสั่ง pip อย่างไรก็ตาม เราจะพยายามแบ่งส่วนการติดตั้ง library ของ python ให้แยกกันไปตามแต่ละงาน โดยใช้ virtual environment (venv) ดังนั้นก่อนจะติดตั้ง Flask เราจะสร้าง virtual environment ก่อน โดยสั่ง
python -m venv venv
(พารามิเตอร์ venv ตัวแรกเป็นชื่อ module ใน python พารามิเตอร์ venv ตัวที่สองเป็นชื่อไดเร็กทอรีที่จะเก็บ environment ซึ่งเราเปลี่ยนเป็นชื่ออื่นได้)
ถ้าจะเริ่มใช้จะเรียก activate ใน venv/bin/activate (เรียกแตกต่างกันตาม os)
ถ้าเรียก activate แล้ว prompt ใน terminal จะเปลี่ยนไป (ลองสังเกต) จากนั้นเราจะสามารถสั่งให้ติดตั้ง Flask ได้ โดยสั่ง
pip install Flask
Flask จะมาพร้อม development web server เพื่อการพัฒนาและทดสอบเบื้องต้น เราจะเรียกโดยสั่ง
flask run
ซึ่งในตอนแรกถ้าเรายังไม่ได้เขียนอะไร มันจะแสดง error ออกมา
main.py
เราจะเริ่มโดยการสร้าง main.py ดังด้านล่าง
from flask import Flask
app = Flask(__name__)
@app.route("/")
def hello_world():
return "<p>Hello, World!</p>"
เราต้องตั้งค่าใน environment ก่อนว่าโปรแกรมหลักเราชื่ออะไร
ถ้าใช้ windows สั่ง
set FLASK_APP=main.py
ถ้าใช้ unix (linux,mac) สั่ง
export FLASK_APP=main.py
และสั่งให้ Flask ทำงานใน terminal โดยสั่ง
flask run
จากนั้นเราจะสามารถเปิดเว็บได้ที่ลิงก์ http://localhost:5000 (โดยปกติ Flask จะเปิดเว็บเซิร์ฟเวอร์ไว้ที่พอร์ต 5000)
git commit
ถ้าเริ่มแสดงหน้าเว็บได้ ก็เป็นจุดที่ดีที่เราควรจะ commit เนื่องจากเราใช้ terminal ใน vscode ในการรัน flask run (ซึ่งเราจะรันทิ้งไว้เลย) ในการสั่ง git ควรจะต้องเปิดอีก terminal หนึ่ง
เราจะเพิ่ม main.py ลงไปใน repo โดยสั่ง
git add main.py
ignore venv. ปกติเราจะไม่เก็บ virtual environment ลงใน version control ดังนั้นให้สร้างไฟล์ .gitignore และเพิ่มบรรทัด
venv/
ลงไปในนั้นด้วย เพื่อบอก git ให้ไม่ต้องสนใจไดเร็กทอรีดังกล่าว จากนั้นให้เพิ่ม .gitignore ลงใน repo โดยสั่ง
git add .gitignore
อาจจะสั่ง git status ดูอีกทีว่าเราเพิ่มไฟล์ครบแล้ว เมื่อเรียบร้อยแล้วให้สั่ง git commit
git commit -m "first hello page"
เราจะใส่ commit message สั้น ๆ ให้พอทราบว่าในแต่ละขั้นเราทำอะไร
ในการทำงานต่อไป จะมีกล่องสีฟ้าดังด้านล่างเพื่อบอกเตือนจุดที่ควรจะ commit
- 🄶 ได้เวลา commit แล้ว
ให้ add ไฟล์ที่เกี่ยวข้องและ commit งานที่ทำขณะนั้นลงใน git เลือก commit message ให้เหมาะสมด้วย
route
การเรียกใช้งานเว็บแอพลิเคชันทำผ่านทางการส่ง request มาที่ web server โดยสิ่งที่ระบุเป้าหมายของการทำงานคือ url (ดูตรงหัวของ browser) ตัวอย่างของ url เช่น
- http://localhost:5000/
- http://localhost:5000/hello/
- https://theory.cpe.ku.ac.th/wiki/index.php/01204223/flask
ในการเขียน Flask เราจะระบุให้ฟังก์ชันใด เป็นฟังก์ชันที่รับผิดชอบเป้าหมาย (target หรือ end point) ใด ๆ ผ่านทาง annotation @app.route ดังตัวอย่างด้านบนที่เราระบุ
@app.route("/")
ไว้ก่อนฟังก์ชัน ซึ่งเป็นการแจ้งกับ web server ของ Flask ว่าถ้ามี request มาที่ / ให้เรียกใช้ฟังก์ชันดังกล่าว
เราจะเพิ่มอีกฟังก์ชันเพื่อรับ request ที่ /hello/ ดังด้านล่าง
@app.route('/hello/')
def hello_world1():
return 'Hi my name is someone'
ให้ทดลองเรียกใช้ และเรียกไปยัง url http://localhost:5000/hello/
- 🄶 ถ้าเปิดได้ ให้ commit ไว้ก่อนเลย
เรามักจะตั้งชื่อ function ให้สื่อความหมาย เดี๋ยวให้แก้ main.py โดยลบ hello_world1 ที่เราทดสอบ route ออกและแก้ชื่อฟังก์ชัน hello_world เป็น index เพื่อเตรียมหัดใช้ template ในส่วนต่อไป
- 🄶 เมื่อแก้เสร็จแล้ว ให้ทดลองกดหน้าเว็บสักนิด ถ้าทุกอย่างโอเค อย่าลืม commit
การแสดง template
ในการเขียนโปรแกรมที่ดีนั้น เราจะแยกส่วนของโค้ดออกตามความรับผิดชอบและภาระหน้าที่ สิ่งหนึ่งที่เราพยายามแยกจากกันคือส่วนประมวลผลกับส่วนแสดงผล (จัดการหน้าจอต่างๆ) โค้ดที่เขียนมาในตอนต้นมีโค้ด html (สำหรับนำเสนอ) ปนอยู่กับโค้ด python (สำหรับประมวลผล) ซึ่งเป็นเครื่องหมายที่ดีว่าเราน่าจะต้องแยกของสองอย่างนี้ออกจากกัน
เราจะแยกส่วนแสดงผลออกมาไว้ใน template สาเหตุที่เรียกว่า template เพราะว่าในส่วนนี้เรายังสามารถเขียนโค้ดบางอย่างเพื่อใช้ในการแสดงผลได้ เช่นการทำซ้ำ แต่ลักษณะโครงสร้างของไฟล์ template นั้นจะเหมือนเป็นไฟล์ html ที่มีการเจาะช่องเป็นโครงเอาไว้เพื่อให้เราใส่ข้อมูลไปแสดงผลได้
การใช้งาน template
main.py
from flask import Flask
from flask import render_template
from datetime import datetime
app = Flask(__name__)
news_items = {
1: {'id': 1,
'title': 'COVID-19 update',
'body': 'This is a news on COVID-19'},
2: {'id': 2,
'title': 'Facemasks found',
'body': 'Recent news on facemask production'},
3: {'id': 3,
'title':'Python 4',
'body':'Python 4 will be out soon.... this is FAKE'},
}
@app.route('/')
def index():
name = 'Somchai'
time = datetime.now()
return render_template('index.html',
name=name,
time=time,
news_items=news_items.values())
@app.route('/news/<id>/')
def show_news_item(id):
news_item = news_items[int(id)]
return render_template('news_item.html',
id=news_item['id'],
title=news_item['title'],
body=news_item['body'])
templates/index.html
<html>
<body>
<h1>Hello!! {{ name }}</h1>
Hi. This is {{ time }}. This is recent news.
<ul>
{% for item in news_items %}
<li>
<a href="{{ url_for('show_news_item', id=item.id) }}">{{ item.title }}</a>
</li>
{% endfor %}
</ul>
</body>
</html>
templates/show.html
<html>
<body>
<h1>{{ title }}</h1>
<p>{{ body }}</p>
<a href="{{ url_for('index') }}">Home</a>
</body>
</html>
การสร้าง form
main.py
from flask import Flask
from flask import render_template, redirect, url_for
from flask import request
from datetime import datetime
app = Flask(__name__)
news_items = {
1: {'id': 1,
'title': 'COVID-19 update',
'body': 'This is a news on COVID-19'},
2: {'id': 2,
'title': 'Facemasks found',
'body': 'Recent news on facemask production'},
3: {'id': 3,
'title':'Python 4',
'body':'Python 4 will be out soon.... this is FAKE'},
}
@app.route('/')
def index():
name = 'Somchai'
time = datetime.now()
return render_template('index.html',
name=name,
time=time,
news_items=news_items.values())
@app.route('/news/<id>/')
def show_news_item(id):
news_item = news_items[int(id)]
return render_template('news_item.html',
id=news_item['id'],
title=news_item['title'],
body=news_item['body'])
def new_news_item(title, body):
new_id = max(news_items.keys()) + 1
return {
'id': new_id,
'title': title,
'body': body
}
@app.route('/news/create/', methods=['POST'])
def create_news_item():
item = new_news_item(request.form['title'],
request.form['body'])
news_items[item['id']] = item
return redirect(url_for('index'))
templates/index.html
<html>
<body>
<h1>Hello!! {{ name }}</h1>
Hi. This is {{ time }}. This is recent news.
<ul>
{% for item in news_items %}
<li>
<a href="{{ url_for('show_news_item', id=item.id) }}">{{ item.title }}</a>
</li>
{% endfor %}
</ul>
<form action="{{ url_for('create_news_item') }}" method="POST">
Title: <input name="title"/><br />
Body: <textarea name="body"></textarea><br />
<input type="submit" value="Save"/>
</form>
</body>
</html>