มีนส์บล็อก

สีที่ใช้

#84e309

#0984e3

#e30984

Flask Python framework + Emmet

Flask Python framework โดย สรุป

สรุป วิธีสร้างเว็บไซต์ด้วยภาษา 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 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 อย่างไร


<!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" />
    
    <link
      rel="stylesheet"
      href="<file-name.css>"
    />
    
    <title><ชื่อเพจ></title>
  </head>
  <body>
      ...
      ...
      ...
    <script
      type="text/javascript"
      src="<file-name.js>"
    ></script>
  </body>
</html>

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
Files tree
Files & Folders structure

VSCode Extension

แน่นอนครับ เมื่อ Flask ใช้ Jinja2 เป็นเครื่องมือ ในการเขียน HTML Template เราก็น่าจะติดตั้ง Jinja-HTML extension อะไรประมาณนี้ เพื่อให้มันทำงานร่วมกับ Emmet ได้ แต่คำถามคือ Jinja extension มีอยู่หลายตัว แล้วจะติดตั้งตัวไหนล่ะ

ดังนั้น เพื่อให้การเขียนโค้ดภาษา Python บน Flask framework เป็นไปอย่างสะดวกรวดเร็ว ขอแนะนำให้ติดตั้ง Visual Studio Code Extension ดังนี้ครับ

Better Jinja

Better Jinja
Better Jinja

Python Extension Pack

Python Extension Pack
Python Extension Pack

สำหรับ Python Extension Pack ติดตั้งทั้ง 4 ตัวเลยครับ


Flask + Emmet

การปรับแต่ง VSCode ให้ Flask ทำงานร่วมกับ Emmet ได้ มี 2 วิธีครับ

jinja-html

  1. สร้างโฟลเดอร์ “.vscode” และสร้างไฟล์ settings.json ในโฟลเดอร์นี้ครับ
settings.json สำหรับ jinja-html
settings.json สำหรับ jinja-html

หรือให้ VSCode สร้างให้

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

  1. ใส่โค้ดนี้เข้าไปใน settings.json ครับ แล้วบันทึกไฟล์
{
  "files.associations": {
    "**/*.html": "html",
    "**/templates/**/*.html": "jinja-html"
  },
  "emmet.includeLanguages": {
    "jinja-html": "html"
  }
}
  1. เลือกชนิดไฟล์เป็น Jinja HTML

เลือกไฟล์ index.html ในโฟลเดอร์ templates แล้วคลิ๊กตรงชนิดของไฟล์ ค้นหาคำว่า Jinja HTML คลิ๊กเลือก ตอนนี้ VSCode ก็จะรับรู้ว่าไฟล์ที่มีนามสกุลเป็น html ที่วางอยู่ในโฟลเดอร์ templates จะเป็น jinja-html

templates/index.html
templates/index.html

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

Emmet: !
Emmet: !

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

Emmet: html
Emmet: html

django-html

Django extension
Django extension

สำหรับ 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 ขึ้นมา

Emmet and Django Template
Before enable django-html

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

After enable django-html
After enable django-html

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

Emmet html:5 Working
Emmet html:5 working

เขียนโค้ด

หลังจากติดตั้ง 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 จะได้เว็บไซต์แบบนี้ครับ

Hello World
Hello World

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);

แปลโค้ดทีละบรรทัดครับ:

  1. สั่งให้แสดง Log file บนเว็บบราวเซอร์ โดยเรา Hard code เข้าไปว่า “JavaScript is running”
  2. สร้างกล่องขึ้นมาชื่อว่า “tag” สร้าง Element ‘p’ หรือ paragraph หรือ สร้างย่อหน้าขึ้นมา 1 ย่อหน้า แล้วเก็บไว้ที่กล่อง “tag”
  3. สร้างกล่องอีกกล่องชื่อว่า “text” สร้างข้อความขึ้นมา 1 ข้อความคือ “*** Flask Tutorial ***” แล้วเก็บข้อความนี้ไว้ในกล่อง “text”
  4. กล่อง “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);

  1. สร้างกล่องที่ 3 ชื่อว่า “element” โดยให้มองหาวัตถุที่มี “id=new” นำมาใส่ไว้ในกล่อง
  2. นำกล่อง “tag” ซึ่งในกล่องมี “<p>*** Flask Tutorial ***</p>” นำมาใส่เข้าไปในกล่อง “element” เป็นกล่องซ้อนกล่อง หรือเขียนเป็นโค้ด appendChild(); นั่นเอง

ดูโครงสร้างไฟล์อีกครั้งครับ กันพลาด

Files structure
Files structure

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 ครับ

JavaScript is running
JavaScript is running

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

JavaScript injection
JavaScript injection

ดังนั้นโค้ดจะเปลี่ยนจาก

<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 ตอนนี้ค่าตัวแปรจะมาปรากฏที่หน้าเว็บไซต์แล้วครับ

Variables in Flask
Variables in Flask

Jinja –Block

ตอนนี้เรารู้วิธีใส่ค่าตัวแปรใน jinja-html แล้ว ขออีกซักกรณีตัวอย่างนะครับ

กรณีที่เราต้องการสร้างเพจใหม่ และไม่ต้องการเขียน HTML, CSS และ JavaScript ใหม่ เราจะเขียนโค้ดอย่างไร เช่นเราต้องการสร้างเพจ “/profile” ให้ทำดังนี้ครับ

  1. เราจะใช้ไฟล์ 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” เท่านั้น

  1. สร้างไฟล์ 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

  1. ไปที่ 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” จะได้เว็บไซต์หน้าตาแบบนี้

Jinja Block Master
Jinja Block – Master

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

Jinja Block extends
Jinja Block – extends

สรุป

  • เมื่อเราสร้างเว็บไซต์ด้วย 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 ทุกครั้ง เพื่อประหยัดเวลาให้แก้โค้ดดังนี้ครับ

  1. ติดตั้ง 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
  1. สร้างไฟล์ .env ที่รากของโฟลเดอร์งาน และใส่โค้ดนี้เข้าไปครับ
FLASK_APP=run.py
FLASK_ENV=development
  1. แก้ไขไฟล์ 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 ตามภาพด้านล่างครับ

Python .env
python-dotenv

เอกสารอ้างอิง

สมัครรับบทความ

ท่านจะได้รับบทความเกี่ยวกับเทคนิคในการเขียนโค้ด การสร้างเว็บไซต์ ความรู้ด้านบัญชี ภาษีอากร และอื่นๆ

0 0 votes
ให้คะแนนบทความ
Notify of
guest
0 ความเห็นทั้งหมด
Inline Feedbacks
ดูความเห็นทั้งหมด

บทความแนะนำ

Redis บน Debian AWS EC2

Redis บน Debian AWS EC2

ติดตั้ง Redis แบบ Manual บน ระบบปฏิบัติการ Debian ที่อยู่บน EC2 ของ AWS Cloud จะช่วยให้การปรับแต่ง Redis เช่น การเพิ่มพอร์ต ทำได้ง่ายขึ้น

Colorize VIM

Developer Playground: Colorize VIM

ตกแต่ง VIM Editor ให้ดู Colorize ด้วย VIM Plug และมันยังมีประโยชน์ต่อการเขียนโค้ดด้วย เพราะมันจะแสดงข้อมูลที่สำคัญบริเวณขอบล่างของหน้าจอ

Debian บน AWS EC2

Debian บน AWS EC2

วิธีติดตั้งระบบปฏิบัติการ Debian บน AWS EC2 วิธีการเชื่อมต่อผ่าน SSH บน VSCode การเพิ่ม Super User เข้าไปในกลุ่ม sudo และตกแต่งด้วย Oh-My-Zsh

0
แสดงความเห็นได้นะx
()
x
Scroll to Top
Share on facebook
Share on twitter
Share on linkedin