สรุป วิธีสร้างเว็บไซต์ด้วยภาษา Python โดยใช้ Flask framework และ การตั้งค่า VSCode เพื่อให้สามารถใช้ Emmet ร่วมกับ Flask framework ได้
สำหรับคนที่เข้าใจ Flask มาบ้างแล้วกดข้ามไปที่ เขียนโค้ด ได้เลยครับ
Flask framework
ต้องเข้าใจก่อนว่า ไม่ว่าจะเขียนเว็บไซต์ด้วยภาษาอะไรก็ตาม สุดท้ายแล้วเว็บไซต์เหล่านั้นจะถูกแปลงเป็น 3 องค์ประกอบหลัก คือ HTML + CSS + JavaScript เพื่อเป็นส่วนติดต่อกับผู้ใช้งานเว็บไซต์ หรือที่เรียกว่า Front-end
เครื่องมือที่ใช้แปลงภาษาเหล่านั้น ให้ออกมาเป็น HTML + CSS + JavaScript จะเรียกว่า Framework ซึ่งในบทความนี้กำลังพูดถึง Framework หนึ่ง ที่ใช้เขียนเว็บไซต์ด้วยภาษา Python คือ Flask framework
เพราะอะไรต้องแปลงให้ออกมาเป็น HTML + CSS + JavaScript
เว็บบราวเซอร์
เพราะเว็บบราวเซอร์ที่เราใช้ท่องเว็บไซต์ต่างๆ มันเข้าใจ HTML, CSS และ JavaScript นั่นเอง เราจึงต้องแปลงโค้ดต่างๆที่เราเขียน ให้อยู่ในรูปที่เว็บบราวเซอร์เข้าใจ
เขียนเว็บไซต์ด้วย Framework อะไรก็ตาม จะได้ผลลัพธ์เป็น HTML + CSS + JavaScript
แต่ถ้าเว็บไซต์ไหน เน้นการคำนวณ จะใช้เทคโนโลจี้ WebAssembly
Emmet
โดยปกติเราสามารถเขียนเว็บไซต์โดยไม่ต้องใช้ Framework ก็ได้ เรามีความรู้แค่ HTML, CSS และ JavaScript เราก็เขียนเว็บไซต์ได้แล้ว โดยหน้าเว็บเพจทุกๆหน้าจะต้องเขียน HTML แบบนี้ครับ
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title><ชื่อเพจ></title>
</head>
<body>
...
...
...
</body>
</html>
โค้ดที่แสดงอยู่ด้านบนนี้ จะเรียกว่า HTML Boilerplate ครั้นจะเขียนโค้ดแบบนี้ซ้ำๆกันก็เสียเวลา ในโลกของการเขียนโค้ดจึงมีเครื่องมือหนึ่ง ที่ใช้ Generate โค้ดที่เขียนซ้ำๆกันแบบนี้ เรียกว่า Emmet แต่ปัญหาคือ Emmet มันทำงานบน Pure HTML ถ้าเราต้องการให้ Emmet ทำงานบน Flask Framework จะตั้งค่า VSCode อย่างไร
Framework สำหรับ Python
ก่อนจะปรับแต่ง VSCode ให้สามารถใช้งาน Emmet ร่วมกับ Flask เราต้องรู้ก่อนว่า Flask ใช้อะไรเขียน HTML Template
อนึ่ง ถ้าเราต้องการเขียนเว็บไซต์โดยใช้ Framework ในภาษา Python เราก็จะต้องเรียนภาษา Python เพิ่มเติม ซึ่ง Framework ในภาษา Python หลักๆก็จะมี
- Flask
- Django
ทั้ง Flask และ Django เขียน HTML Template เหมือนกัน ต่างกันตรงเครื่องมือ
- Flask ใช้ Jinja2
- Django ใช้ Django Template
# Flask dependencies
❯ pip freeze
certifi==2020.6.20
click
Flask==1.1.2
importlib-metadata
itsdangerous
Jinja2
MarkupSafe
Werkzeug
zipp
เมื่อเรารู้ว่า Flask ใช้ Jinja2 ในการเขียน HTML Template ทีนี้เราจะให้ Emmet ทำงานร่วมกับ Jinja2 อย่างไร
Flask Python framework
ถ้าติดตั้ง Flask เรียบร้อยแล้ว กดข้าม ได้ครับ
Anaconda Flask
โครงงานที่ต้องเขียนโค้ดด้วยภาษา Python ทั้งหมด ผมจะทำงานบน Anaconda นะครับ สามารถดูวิธีติดตั้ง Anaconda และสร้าง Virtual Environment ได้ ที่นี่ ครับ หรือถ้ามี Anaconda อยู่บนเครื่องแล้ว สร้าง Python Virtual Environment ด้วยคำสั่ง
conda create -n env-flask python=3.8 flask
ติดตั้ง Flask
โดยทั่วไปจะติดตั้งโดยใช้ pip สามารถใช้คำสั่ง
pip install flask
หรือติดตั้งใน Anaconda env ด้วยคำสั่ง
conda install -c anaconda flask
❯ conda install -c anaconda flask
Collecting package metadata (current_repodata.json): done
Solving environment: done
## Package Plan ##
environment location: /Users/manotlj/Anaconda/anaconda3/envs/env-flask
added / updated specs:
- flask
The following packages will be downloaded:
package | build
---------------------------|-----------------
ca-certificates-2020.10.14 | 0 127 KB anaconda
certifi-2020.6.20 | py38_0 159 KB anaconda
flask-1.1.2 | py_0 74 KB anaconda
------------------------------------------------------------
Total: 360 KB
The following packages will be SUPERSEDED by a higher-priority channel:
ca-certificates pkgs/main::ca-certificates-2021.7.5-h~ --> anaconda::ca-certificates-2020.10.14-0
certifi pkgs/main::certifi-2021.5.30-py38hecd~ --> anaconda::certifi-2020.6.20-py38_0
flask pkgs/main::flask-1.1.2-pyhd3eb1b0_0 --> anaconda::flask-1.1.2-py_0
Proceed ([y]/n)? y
Downloading and Extracting Packages
flask-1.1.2 | 74 KB | #################################################################################### | 100%
ca-certificates-2020 | 127 KB | #################################################################################### | 100%
certifi-2020.6.20 | 159 KB | #################################################################################### | 100%
Preparing transaction: done
Verifying transaction: done
Executing transaction: done
╭─ ~/Projects/Sample_Projects/flask-framework/flask-principle main 23s env-flask
╰─ ─╯
เข้าทำงานใน Python Virtual Environment ด้วยคำสั่ง
conda activate <virtual-env>
ผมสร้าง Python Virtual Environment เป็นชื่อ env-flask ดังนั้นของผมจะได้
conda activate env-flask
Boilerplate
สร้างไฟล์ และโฟลเดอร์ดังนี้ครับ
โฟลเดอร์ app ใช้เก็บไฟล์ views.py โฟลเดอร์ static เก็บไฟล์ CSS , JavaScript และโฟลเดอร์ templates เก็บไฟล์ HTML Template
.
├── .vscode
│ └── settings.json
├── app
│ ├── static
│ │ ├── android-chrome-192x192.png
│ │ ├── android-chrome-512x512.png
│ │ ├── apple-touch-icon.png
│ │ ├── browserconfig.xml
│ │ ├── favicon-16x16.png
│ │ ├── favicon-32x32.png
│ │ ├── favicon.ico
│ │ ├── index.js
│ │ ├── mstile-150x150.png
│ │ ├── safari-pinned-tab.svg
│ │ ├── site.webmanifest
│ │ └── style.css
│ ├── templates
│ │ └── index.html
│ ├── __init__.py
│ └── views.py
├── .gitignore
├── config.py
├── README.md
└── run.py

VSCode Extension
แน่นอนครับ เมื่อ Flask ใช้ Jinja2 เป็นเครื่องมือ ในการเขียน HTML Template เราก็น่าจะติดตั้ง Jinja-HTML extension อะไรประมาณนี้ เพื่อให้มันทำงานร่วมกับ Emmet ได้ แต่คำถามคือ Jinja extension มีอยู่หลายตัว แล้วจะติดตั้งตัวไหนล่ะ
ดังนั้น เพื่อให้การเขียนโค้ดภาษา Python บน Flask framework เป็นไปอย่างสะดวกรวดเร็ว ขอแนะนำให้ติดตั้ง Visual Studio Code Extension ดังนี้ครับ
Better Jinja

Python Extension Pack

สำหรับ Python Extension Pack ติดตั้งทั้ง 4 ตัวเลยครับ
Flask + Emmet
การปรับแต่ง VSCode ให้ Flask ทำงานร่วมกับ Emmet ได้ มี 2 วิธีครับ
jinja-html
- สร้างโฟลเดอร์ “.vscode” และสร้างไฟล์ settings.json ในโฟลเดอร์นี้ครับ

หรือให้ VSCode สร้างให้
- โดยคลิ๊กตรงรูปเฟือง เลือก Settings
- เลือกแท็บ Workspace
- แล้วคลิ๊กตรงรูป กระดาษ –ไอคอนตัวที่ 2 จากซ้ายมือ

- ใส่โค้ดนี้เข้าไปใน settings.json ครับ แล้วบันทึกไฟล์
{
"files.associations": {
"**/*.html": "html",
"**/templates/**/*.html": "jinja-html"
},
"emmet.includeLanguages": {
"jinja-html": "html"
}
}
- เลือกชนิดไฟล์เป็น Jinja HTML
เลือกไฟล์ index.html ในโฟลเดอร์ templates แล้วคลิ๊กตรงชนิดของไฟล์ ค้นหาคำว่า Jinja HTML คลิ๊กเลือก ตอนนี้ VSCode ก็จะรับรู้ว่าไฟล์ที่มีนามสกุลเป็น html ที่วางอยู่ในโฟลเดอร์ templates จะเป็น jinja-html

ตอนนี้เราสามารถใช้ Emmet ได้แล้วครับ ลองพิมพ์ ! ในไฟล์ index.html

หรือพิมพ์ html ดูครับ VSCode จะขึ้น Emmet Abbreviation มาให้เราเลือก

django-html

สำหรับ django-html มีขั้นตอนเหมือน jinja-html ทุกอย่างครับ เปลี่ยนแค่โค้ดใน settings.json เป็น
{
"files.associations": {
"**/*.html": "html",
"**/templates/**/*.html": "django-html"
},
"emmet.includeLanguages": {
"django-html": "html"
}
}
และเลือกชนิดของไฟล์เป็น Django Template แค่นี้เองครับ
รูปด้านล่างนี้แสดงการทำงานของ VSCode ก่อนระบุค่า settings.json จะเห็นว่าเมื่อพิมพ์ ! จะไม่มี Emmet pop-up ขึ้นมา

แต่เมื่อตั้งค่า settings.json เรียบร้อยแล้ว ตอนนี้ Emmet ทำงานแล้วครับ

เมื่อพิมพ์ html ก็จะเห็น Emmet Abbreviation pop-up ขึ้นมาเช่นกัน

เขียนโค้ด
หลังจากติดตั้ง Flask เสร็จแล้วก็เขียนโค้ดกันเลยครับ เริ่มจากไฟล์ config.py เขียนโค้ดนี้เข้าไปครับ
DEBUG = True
ตอนนี้เรากำลังพัฒนา App ซึ่งอยู่ในขั้น Development เราจึงเปิดโหมด DEBUG จะเปลี่ยน True เป็น False เมื่อโค้ดของเราอยู่ในขั้น Production
เริ่มต้น App ด้วยไฟล์ __init__.py เขียนโค้ด
# Call Flask framework
from flask import Flask
# Initialize the application
app = Flask(__name__, instance_relative_config=True)
# Load the views
from app import views
# Load the configuration file
app.config.from_object('config')
แปลโค้ด:
- เรียกใช้งาน Flask
- สร้าง app จาก Flask
- เรียกใช้งาน views จาก app ที่เพิ่งสร้าง
- โหลดไฟล์ config.py
ต่อไปเขียนโค้ดที่ใช้สำหรับ render template ซึ่งก็คือไฟล์ app/views.py
from flask import render_template
from app import app
@app.route('/')
def hello_world():
return 'Hello World'
ไฟล์ run.py เป็น Entry-point ของ App เขียนโค้ดดังนี้ครับ
from app import app
if __name__ == '__main__':
app.run()
เสร็จแล้วสั่งให้ app ทำงานครับ แต่บอก Flask นิดหนึ่งว่า Entry-point คือไฟล์ run.py และ Environment เป็น Development
export FLASK_APP=run.py
export FLASK_ENV=development
flask run
ข้อสังเกต ถ้าเราไม่บอก Flask ว่า Environment เป็น Development มันจะ Default เป็น Production และ Debug mode เป็น off แต่อย่างไรก็ตาม app ของเรายังคงทำงานเป็นปกตินะครับ
❯ export FLASK_APP=run.py
❯ flask run
* Serving Flask app 'run.py' (lazy loading)
* Environment: production
WARNING: This is a development server. Do not use it in a production deployment.
Use a production WSGI server instead.
* Debug mode: off
* Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)
127.0.0.1 - - [18/Sep/2021 13:39:47] "GET / HTTP/1.1" 200 -
127.0.0.1 - - [18/Sep/2021 13:39:48] "GET /favicon.ico HTTP/1.1" 404 -
พอเราระบุ Environment เป็น Development ตอนนี้ Debug mode เป็น on ครับ หรือเปิด Debug mode นั่นเอง และมันจะ restart app เมื่อโค้ดของเราเปลี่ยนครับ
❯ export FLASK_ENV=development
❯ flask run
* Serving Flask app 'run.py' (lazy loading)
* Environment: development
* Debug mode: on
* Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)
* Restarting with stat
* Debugger is active!
* Debugger PIN: 714-022-754
เปิดเว็บบราวเซอร์ พิมพ์ localhost:5000 จะได้เว็บไซต์แบบนี้ครับ

favicon
generate favicon ที่ realfavicongenerator เมื่อดาวน์โหลด และแตกไฟล์ออกมาแล้ว ให้วางไฟล์ทั้งหมดไว้ที่โฟลเดอร์ static
├── app
│ ├── static
│ │ ├── android-chrome-192x192.png
│ │ ├── android-chrome-512x512.png
│ │ ├── apple-touch-icon.png
│ │ ├── browserconfig.xml
│ │ ├── favicon-16x16.png
│ │ ├── favicon-32x32.png
│ │ ├── favicon.ico
│ │ ├── index.js
│ │ ├── mstile-150x150.png
│ │ ├── safari-pinned-tab.svg
│ │ ├── site.webmanifest
│ │ └── style.css
เข้าไปที่ไฟล์ templates/index.html เขียนโค้ดดังนี้ครับ
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
{# get your own code from realfavicongenerator.net #}
<meta name="apple-mobile-web-app-title" content="MeansBiz" />
<meta name="application-name" content="MeansBiz" />
<meta name="msapplication-TileColor" content="#0984e3" />
<meta name="theme-color" content="#ffffff" />
<link
rel="apple-touch-icon"
sizes="180x180"
href="{{url_for('static', filename='apple-touch-icon.png')}}"
/>
<link
rel="icon"
type="image/png"
sizes="32x32"
href="{{url_for('static', filename='favicon-32x32.png')}}"
/>
<link
rel="icon"
type="image/png"
sizes="16x16"
href="{{url_for('static', filename='favicon-16x16.png')}}"
/>
<link
rel="manifest"
href="{{url_for('static', filename='site.webmanifest')}}"
/>
<link
rel="mask-icon"
href="{{url_for('static', filename='safari-pinned-tab.svg')}}"
color="#0984e3"
/>
{# end realfavicongenerator #}
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
<link
href="https://fonts.googleapis.com/css2?family=Rubik:[email protected];500;600;700&display=swap"
rel="stylesheet"
/>
<link
rel="stylesheet"
href="{{ url_for('static', filename='style.css')}}"
/>
<title>Gen-Template</title>
</head>
<body>
<container>
<div>
<h1>This page generated with render_template</h1>
<h2>Statements</h2>
{% if my_variable %}
<p>
This paragraph won't be displayed in the template because the variable
"my_variable" does not exist. The if statement therefore returns
False.
</p>
{% else %}
<p>This paragraph will be displayed.</p>
{% endif %}
<h2>Variables</h2>
<p>{{ my_variable }}</p>
<p>
The variable above will not be displayed because it doesn't exist!
</p>
<h2>Comments</h2>
{% if True %}
<p>This will be displayed because the if statement returns True.</p>
{% endif %} {# {% if True %}
<p>
This will not be displayed because it is commented out using Jinja2
syntax.
</p>
{% endif %} #}
</div>
<div id="new"></div>
</container>
<script
type="text/javascript"
src="{{ url_for('static', filename='index.js')}}"
></script>
</body>
</html>
วิธีอ้างอิง static files
จากไฟล์ index.html เราได้อ้างอิงไฟล์ CSS –style.css และ JavaScript –index.js โดยวิธีการอ้างอิง static file ใน Flask ให้ใช้แพทเทิร์นนี้ครับ
# CSS
<link
rel="stylesheet"
href="{{ url_for('<folder-name>', filename='<file-name>')}}"
/>
# JS
<script
type="text/javascript"
src="{{ url_for('<folder-name', filename='<file-name>')}}"
></script>
CSS
เขียนโค้ดนี้เข้าไปในไฟล์ “app/static/style.css”
body {
margin: 0;
padding: 0;
background: #0984e3;
color: #fff;
font-size: 22px;
-moz-osx-font-smoothing: grayscale;
-webkit-font-smoothing: antialiased;
}
h1,
h2,
h3,
h4,
h5,
h6,
p {
font-family: 'Rubik', sans-serif;
}
h1 {
font-weight: 500;
}
h2 {
font-weight: 600;
}
p {
font-weight: 300;
}
container {
height: 100vh;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
}
JavaScript
ใส่โค้ดนี้เข้าไปในไฟล์ “app/static/index.js” ครับ
console.log('JavaScript is running');
var tag = document.createElement('p');
var text = document.createTextNode('*** Flask Tutorial ***');
tag.appendChild(text);
var element = document.getElementById('new');
element.appendChild(tag);
แปลโค้ดทีละบรรทัดครับ:
- สั่งให้แสดง Log file บนเว็บบราวเซอร์ โดยเรา Hard code เข้าไปว่า “JavaScript is running”
- สร้างกล่องขึ้นมาชื่อว่า “tag” สร้าง Element ‘p’ หรือ paragraph หรือ สร้างย่อหน้าขึ้นมา 1 ย่อหน้า แล้วเก็บไว้ที่กล่อง “tag”
- สร้างกล่องอีกกล่องชื่อว่า “text” สร้างข้อความขึ้นมา 1 ข้อความคือ “*** Flask Tutorial ***” แล้วเก็บข้อความนี้ไว้ในกล่อง “text”
- กล่อง “tag” ถูกสร้างจาก “Object document” ดังนั้นมันจะได้คุณสมบัติของ document ติดมาด้วย หนึ่งในนั้นคือ “Object appendChild” ซึ่ง Object ตัวนี้เป็นฟังก์ชั่น
*** เราเรียกใช้ฟังก์ชั่น appendChild();
โดยเติมกล่อง “text” เข้าไปเป็น “วัตถุดิบ” ในการประมวลผล ออกมาเป็น “สินค้าสำเร็จรูป” และสินค้าสำเร็จรูปในที่นี้คือ
<p>*** Flask Tutorial ***</p>
# โดย <p></p> มาจาก var tag และ
# *** Flask Tutorial *** มาจาก tag.appendChild(text);
เมื่อมาถึงบรรทัดนี้ ฝากข้อสังเกตนิดหนึ่ง
Object เป็นอะไรก็ได้ เช่นเป็น text, number, Object หรือ Function
Function = โรงงานผลิตสินค้า ดังนั้นเราต้องใส่วัตถุดิบเข้าไป จึงจะได้สินค้า
Function(Raw Materials);
- สร้างกล่องที่ 3 ชื่อว่า “element” โดยให้มองหาวัตถุที่มี “id=new” นำมาใส่ไว้ในกล่อง
- นำกล่อง “tag” ซึ่งในกล่องมี “<p>*** Flask Tutorial ***</p>” นำมาใส่เข้าไปในกล่อง “element” เป็นกล่องซ้อนกล่อง หรือเขียนเป็นโค้ด
appendChild();
นั่นเอง
ดูโครงสร้างไฟล์อีกครั้งครับ กันพลาด

Render views จาก Template
เราลองมา Render ไฟล์ index.html กันครับ ไปที่ไฟล์ views.py แล้วแก้ไขโค้ดให้เป็นดังนี้ครับ
import os
from flask import render_template, send_from_directory
from app import app
@app.route('/favicon.ico')
def favicon():
return send_from_directory(os.path.join(app.root_path, 'static'), 'favicon.ico', mimetype='image/vnd.microsoft.icon')
@app.route('/')
def hello_world():
return 'Hello World'
@app.route('/gen-template')
def gen_template():
return render_template('index.html')
จากไฟล์ views.py ตอนนี้เรามี route ทั้งหมด 3 routes คือ Home หรือ “/” , “/favicon.ico” และ “/gen-template” สั่ง Run app ครับ แล้วเข้าไปที่ “localhost:5000/gen-template” เว็บไซต์ของเราก็จะมีหน้าตาแบบนี้ ให้เปิด Console ขึ้นมาดู จะเห็น Log เขียนว่า “JavaScript is running” และในส่วนของเว็บไซต์บรรทัดสุดท้ายจะมีคำว่า *** Flask Tutorial *** ปรากฏอยู่ ทั้ง 2 จุดเกิดจากการทำงานของไฟล์ JavaScript ครับ

ถ้ากลับไปดูไฟล์ index.html จะไม่พบคำว่า “*** Flask Tutorial ***” ประโยคนี้ถูกฉีดเข้าไปในไฟล์ index.html ด้วย JavaScript หรือไฟล์ index.js โดย JavaScript จะมองหา id=”new” เมื่อเจอแล้วจะฉีด element เข้าไประหว่าง <div></div>

ดังนั้นโค้ดจะเปลี่ยนจาก
<div id="new"></div>
เป็น
<div id="new">
<p>*** Flask Tutorial ***</p>
</div>
Variables
ต่อไปเราลองใส่ค่า ตัวแปร เข้าไปใน jinja-html กลับไปที่ไฟล์ views.py เติมตัวแปร name="Flask"
เข้าไปในบรรทัดสุดท้าย
import os
from flask import render_template, send_from_directory
from app import app
@app.route('/favicon.ico')
def favicon():
return send_from_directory(os.path.join(app.root_path, 'static'), 'favicon.ico', mimetype='image/vnd.microsoft.icon')
@app.route('/')
def hello_world():
return 'Hello World'
@app.route('/gen-template')
def gen_template():
return render_template('index.html', name="Flask")
ตอนนี้ตัวแปรของเราจะถูกส่งไปที่ไฟล์ index.html เราสามารถนำตัวแปรมาเขียนโค้ดใน jinja-html ได้ โดยเปิดไฟล์ index.html แล้วแก้ไขไฟล์ดังนี้ครับ
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
{# get your own code from realfavicongenerator.net #}
<meta name="apple-mobile-web-app-title" content="MeansBiz" />
<meta name="application-name" content="MeansBiz" />
<meta name="msapplication-TileColor" content="#0984e3" />
<meta name="theme-color" content="#ffffff" />
<link
rel="apple-touch-icon"
sizes="180x180"
href="{{url_for('static', filename='apple-touch-icon.png')}}"
/>
<link
rel="icon"
type="image/png"
sizes="32x32"
href="{{url_for('static', filename='favicon-32x32.png')}}"
/>
<link
rel="icon"
type="image/png"
sizes="16x16"
href="{{url_for('static', filename='favicon-16x16.png')}}"
/>
<link
rel="manifest"
href="{{url_for('static', filename='site.webmanifest')}}"
/>
<link
rel="mask-icon"
href="{{url_for('static', filename='safari-pinned-tab.svg')}}"
color="#0984e3"
/>
{# end realfavicongenerator #}
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
<link
href="https://fonts.googleapis.com/css2?family=Rubik:[email protected];500;600;700&display=swap"
rel="stylesheet"
/>
<link
rel="stylesheet"
href="{{ url_for('static', filename='style.css')}}"
/>
<title>Gen-Template</title>
</head>
<body>
<container>
<div>
<h1>This page generated with render_template</h1>
<h2>Statements</h2>
{% if name %}
<p>
This paragraph will be displayed in the template because the variable
"name" exist. The if statement therefore returns True.
</p>
{% else %}
<p>This paragraph will not be displayed.</p>
{% endif %}
<h2>Variables</h2>
<p>Variable name contain: {{ name }}</p>
<p>The variable above will be displayed because it exist!</p>
<h2>Comments</h2>
{% if True %}
<p>This will be displayed because the if statement returns True.</p>
{% endif %} {# {% if True %}
<p>
This will not be displayed because it is commented out using Jinja2
syntax.
</p>
{% endif %} #}
</div>
<div id="new"></div>
</container>
<script
type="text/javascript"
src="{{ url_for('static', filename='index.js')}}"
></script>
</body>
</html>
เมื่อสั่ง flask run
ตอนนี้ค่าตัวแปรจะมาปรากฏที่หน้าเว็บไซต์แล้วครับ

Jinja –Block
ตอนนี้เรารู้วิธีใส่ค่าตัวแปรใน jinja-html แล้ว ขออีกซักกรณีตัวอย่างนะครับ
กรณีที่เราต้องการสร้างเพจใหม่ และไม่ต้องการเขียน HTML, CSS และ JavaScript ใหม่ เราจะเขียนโค้ดอย่างไร เช่นเราต้องการสร้างเพจ “/profile” ให้ทำดังนี้ครับ
- เราจะใช้ไฟล์ index.html เป็นไฟล์ Master block โดยจะมีข้อมูล เช่น meta, link, CSS, JavaScript, div และ id เป็นค่ามาตรฐาน ใช้เป็น template สำหรับไฟล์ HTML ทุกไฟล์
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
{# get your own code from realfavicongenerator.net #}
<meta name="apple-mobile-web-app-title" content="MeansBiz" />
<meta name="application-name" content="MeansBiz" />
<meta name="msapplication-TileColor" content="#0984e3" />
<meta name="theme-color" content="#ffffff" />
<link
rel="apple-touch-icon"
sizes="180x180"
href="{{url_for('static', filename='apple-touch-icon.png')}}"
/>
<link
rel="icon"
type="image/png"
sizes="32x32"
href="{{url_for('static', filename='favicon-32x32.png')}}"
/>
<link
rel="icon"
type="image/png"
sizes="16x16"
href="{{url_for('static', filename='favicon-16x16.png')}}"
/>
<link
rel="manifest"
href="{{url_for('static', filename='site.webmanifest')}}"
/>
<link
rel="mask-icon"
href="{{url_for('static', filename='safari-pinned-tab.svg')}}"
color="#0984e3"
/>
{# end realfavicongenerator #}
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
<link
href="https://fonts.googleapis.com/css2?family=Rubik:[email protected];500;600;700&display=swap"
rel="stylesheet"
/>
<link
rel="stylesheet"
href="{{ url_for('static', filename='style.css')}}"
/>
<title>Gen-Template</title>
</head>
<body>
<container>
<div>
...
...
...
</div>
<div id="new"></div>
</container>
<script
type="text/javascript"
src="{{ url_for('static', filename='index.js')}}"
></script>
</body>
</html>
ดังนั้นไปที่ไฟล์ index.html แก้ไขโค้ดดังนี้ครับ โดยจริงๆแล้วเพิ่มโค้ดเข้าไปแค่ 2 บรรทัด คือ
{% block content %} ... ... ... {% endblock %}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
{# get your own code from realfavicongenerator.net #}
<meta name="apple-mobile-web-app-title" content="MeansBiz" />
<meta name="application-name" content="MeansBiz" />
<meta name="msapplication-TileColor" content="#0984e3" />
<meta name="theme-color" content="#ffffff" />
<link
rel="apple-touch-icon"
sizes="180x180"
href="{{url_for('static', filename='apple-touch-icon.png')}}"
/>
<link
rel="icon"
type="image/png"
sizes="32x32"
href="{{url_for('static', filename='favicon-32x32.png')}}"
/>
<link
rel="icon"
type="image/png"
sizes="16x16"
href="{{url_for('static', filename='favicon-16x16.png')}}"
/>
<link
rel="manifest"
href="{{url_for('static', filename='site.webmanifest')}}"
/>
<link
rel="mask-icon"
href="{{url_for('static', filename='safari-pinned-tab.svg')}}"
color="#0984e3"
/>
{# end realfavicongenerator #}
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
<link
href="https://fonts.googleapis.com/css2?family=Rubik:[email protected];500;600;700&display=swap"
rel="stylesheet"
/>
<link
rel="stylesheet"
href="{{ url_for('static', filename='style.css')}}"
/>
<title>Gen-Template</title>
</head>
<body>
<container>
<div>
{% block content %}
<h1>This page generated with render_template</h1>
<h2>Statements</h2>
{% if name %}
<p>
This paragraph will be displayed in the template because the variable
"name" exist. The if statement therefore returns True.
</p>
{% else %}
<p>This paragraph will not be displayed.</p>
{% endif %}
<h2>Variables</h2>
<p>Variable name contain: {{ name }}</p>
<p>The variable above will be displayed because it exist!</p>
<h2>Comments</h2>
{% if True %}
<p>This will be displayed because the if statement returns True.</p>
{% endif %} {# {% if True %}
<p>
This will not be displayed because it is commented out using Jinja2
syntax.
</p>
{% endif %} #}
<h3>This is block content in Jinja2</h3>
{% endblock %}
</div>
<div id="new"></div>
</container>
<script
type="text/javascript"
src="{{ url_for('static', filename='index.js')}}"
></script>
</body>
</html>
โค้ดที่อยู่ระหว่าง {% block content %}
และ {% endblock %}
จะมีผลเฉพาะเพจ “/gen-template” เท่านั้น
- สร้างไฟล์ templates/profile.html แล้วใส่โค้ดนี้เข้าไปครับ
{% extends "index.html" %} {% block content %}
<h3>This is the profile page</h3>
{% endblock %}
จะเห็นว่าเราไม่ต้องเขียนโค้ด !DOCTYPE html, meta, link, CSS … ซ้ำอีก ช่วยประหยัดเวลาเราไปได้เยอะ เราจะเขียนโค้ดเฉพาะที่เพจ “/profile” ต้องการเท่านั้น ซึ่งในที่นี้จะเขียนแค่ This is the profile page
- ไปที่ app/views.py เพื่อสร้าง route ให้กับเพจ “/profile” โดยแก้ไขโค้ดให้เป็น
import os
from flask import render_template, send_from_directory
from app import app
@app.route('/favicon.ico')
def favicon():
return send_from_directory(os.path.join(app.root_path, 'static'), 'favicon.ico', mimetype='image/vnd.microsoft.icon')
@app.route('/')
def hello_world():
return 'Hello World'
@app.route('/gen-template')
def gen_template():
return render_template('index.html', name="Flask")
@app.route('/profile')
def profile():
return render_template('profile.html')
refresh เว็บบราวเซอร์ แล้วเข้าไปที่เพจ “/gen-template” จะได้เว็บไซต์หน้าตาแบบนี้

ถ้าไปที่เพจ “/profile” ก็จะได้เพจตามรูปประกอบด้านล่างนี้ครับ จะเห็นว่า เราได้ตัวอักษร และพื้นหลังสีเดียวกันกับเพจ “/gen-template”

สรุป
- เมื่อเราสร้างเว็บไซต์ด้วย Flask Python framework เรา Render front-end ด้วย views.py
- ใน views.py ประกอบด้วย URL ของแต่ละเพจ หรือ Route
- ชนิดของข้อมูลที่ views.py นำมา Render มีหลายชนิด หนึ่งในนั้นคือไฟล์ HTML
- เราเก็บไฟล์ HTML ที่ใช้ Render ไว้ที่โฟลเดอร์ templates และในไฟล์ HTML สามารถดึงไฟล์ CSS และ JavaScript มาใช้ได้เหมือนการเขียน Pure HTML
- ไฟล์รูปภาพ, favicon, CSS และ JavaScript จะเก็บไว้ที่โฟลเดอร์ static
- Flask ใช้พอร์ต 5000 เป็นค่าปกติ
ดูโค้ดทั้งหมดได้ที่ Github
โบนัส
ทุกๆครั้งที่เปิดเครื่องคอมพิวเตอร์ และต้องการให้ Flask ทำงานใน Development mode เราต้องพิมพ์คำสั่ง
export FLASK_ENV=development
ทุกครั้ง เพื่อประหยัดเวลาให้แก้โค้ดดังนี้ครับ
- ติดตั้ง python-dotenv
pip install python-dotenv
❯ pip install python-dotenv
Collecting python-dotenv
Using cached python_dotenv-0.19.0-py2.py3-none-any.whl (17 kB)
Installing collected packages: python-dotenv
Successfully installed python-dotenv-0.19.0
- สร้างไฟล์ .env ที่รากของโฟลเดอร์งาน และใส่โค้ดนี้เข้าไปครับ
FLASK_APP=run.py
FLASK_ENV=development
- แก้ไขไฟล์ config.py โดยเขียนโค้ดใหม่ ดังนี้
from os import getenv
from dotenv import load_dotenv
load_dotenv()
Environment = getenv('FLASK_ENV')
print('Flask running on : %s' % Environment, 'mode')
เมื่อใช้คำสั่ง flask run ก็จะได้ Environment: development และ Debug mode: on ตามภาพด้านล่างครับ
