Notes & software courses · Free to learn
Aph's Blog
On this page

วันที่ 26 — เว็บด้วย Python (Python Web)

👋 อ่านฟรีทั้งหมดบน Aph's Blog — เนื้อหาภาษาไทย ทำตามทีละหน้าใน sidebar ได้เลย หากมีข้อเสนอแนะหรืออยากให้เพิ่มหัวข้อไหน บอกได้เสมอ

เรียนใช้ Flask สร้างเว็บแอปพลิเคชันด้วย Python — ตั้งแต่ route แรก จนถึง deploy บน Heroku

30 Days of Python Banner

Python สำหรับเว็บ

Python เป็นภาษาโปรแกรมที่ใช้งานทั่วไปและสามารถนำไปใช้ได้หลายที่ ในส่วนนี้ เราจะดูว่าเราใช้ Python สำหรับเว็บได้อย่างไร มี Python web framework มากมาย Django และ Flask เป็น framework ที่ได้รับความนิยมมากที่สุด วันนี้ เราจะดูวิธีใช้ Flask สำหรับ web development

Flask

Flask เป็น web development framework ที่เขียนด้วย Python Flask ใช้ Jinja2 template engine Flask ยังสามารถใช้ร่วมกับ front-end library สมัยใหม่อื่น ๆ เช่น React ได้

ถ้าคุณยังไม่ได้ติดตั้ง package virtualenv ให้ติดตั้งก่อน Virtual environment จะช่วยแยก project dependencies ออกจาก dependencies บนเครื่อง local

โครงสร้างโฟลเดอร์

หลังจากทำครบทุกขั้นตอนแล้ว โครงสร้างไฟล์ของ project ควรมีหน้าตาแบบนี้:

shell

├── Procfile
├── app.py
├── env
   ├── bin
├── requirements.txt
├── static
   └── css
       └── main.css
└── templates
    ├── about.html
    ├── home.html
    ├── layout.html
    ├── post.html
    └── result.html

การตั้งค่า Project Directory

ทำตามขั้นตอนต่อไปนี้เพื่อเริ่มต้นใช้ Flask

ขั้นตอนที่ 1: ติดตั้ง virtualenv โดยใช้คำสั่งต่อไปนี้

shell
pip install virtualenv

ขั้นตอนที่ 2:

shell
asabeneh@Asabeneh:~/Desktop$ mkdir python_for_web
asabeneh@Asabeneh:~/Desktop$ cd python_for_web/
asabeneh@Asabeneh:~/Desktop/python_for_web$ virtualenv venv
asabeneh@Asabeneh:~/Desktop/python_for_web$ source venv/bin/activate
(env) asabeneh@Asabeneh:~/Desktop/python_for_web$ pip freeze
(env) asabeneh@Asabeneh:~/Desktop/python_for_web$ pip install Flask
(env) asabeneh@Asabeneh:~/Desktop/python_for_web$ pip freeze
Click==7.0
Flask==1.1.1
itsdangerous==1.1.0
Jinja2==2.10.3
MarkupSafe==1.1.1
Werkzeug==0.16.0
(env) asabeneh@Asabeneh:~/Desktop/python_for_web$

เราสร้าง project directory ชื่อ python_for_web ภายใน project เราสร้าง virtual environment ชื่อ venv (จะตั้งชื่ออะไรก็ได้แต่ผู้เขียนชอบใช้ชื่อ venv) จากนั้น activate virtual environment เราใช้ pip freeze เพื่อตรวจสอบ package ที่ติดตั้งใน project directory ผล pip freeze ว่างเปล่าเพราะยังไม่ได้ติดตั้ง package

ตอนนี้ มาสร้างไฟล์ app.py ใน project directory และเขียนโค้ดต่อไปนี้ ไฟล์ app.py จะเป็นไฟล์หลักของ project โค้ดต่อไปนี้มี flask module และ os module

การสร้าง Routes

Home route

python
# let's import the flask
from flask import Flask
import os # importing operating system module

app = Flask(__name__)

@app.route('/') # this decorator create the home route
def home ():
    return '<h1>Welcome</h1>'

if __name__ == '__main__':
    # for deployment we use the environ
    # to make it work for both production and development
    port = int(os.environ.get("PORT", 5000))
    app.run(debug=True, host='0.0.0.0', port=port)

ในการรัน flask application ให้พิมพ์ python app.py ใน directory หลักของ flask application

หลังจากรัน python app.py แล้วให้เช็ค local host 5000

มาเพิ่ม route เพิ่มเติม โดยสร้าง about route

python
# let's import the flask
from flask import Flask
import os # importing operating system module

app = Flask(__name__)

@app.route('/') # this decorator create the home route
def home ():
    return '<h1>Welcome</h1>'

@app.route('/about')
def about():
    return '<h1>About us</h1>'

if __name__ == '__main__':
    # for deployment we use the environ
    # to make it work for both production and development
    port = int(os.environ.get("PORT", 5000))
    app.run(debug=True, host='0.0.0.0', port=port)

ตอนนี้ เราได้เพิ่ม about route แล้ว แล้วถ้าเราต้องการ render ไฟล์ HTML แทนที่จะเป็น string ล่ะ? สามารถ render ไฟล์ HTML ได้โดยใช้ function render_template มาสร้างโฟลเดอร์ชื่อ templates และสร้าง home.html และ about.html ใน project directory มา import function render_template จาก flask ด้วย

การสร้าง Templates

สร้างไฟล์ HTML ภายในโฟลเดอร์ templates

home.html

python
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Home</title>
  </head>

  <body>
    <h1>Welcome Home</h1>
  </body>
</html>

about.html

python
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>About</title>
  </head>

  <body>
    <h1>About Us</h1>
  </body>
</html>

Python Script

app.py

python
# let's import the flask
from flask import Flask, render_template
import os # importing operating system module

app = Flask(__name__)

@app.route('/') # this decorator create the home route
def home ():
    return render_template('home.html')

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

if __name__ == '__main__':
    # for deployment we use the environ
    # to make it work for both production and development
    port = int(os.environ.get("PORT", 5000))
    app.run(debug=True, host='0.0.0.0', port=port)

ดังที่เห็น การไปยังหน้าต่าง ๆ หรือการ navigate จำเป็นต้องมี navigation มาเพิ่ม link ไปยังแต่ละหน้า หรือสร้าง layout ที่ใช้ในทุกหน้า

Navigation

python
<ul>
  <li><a href="/">Home</a></li>
  <li><a href="/about">About</a></li>
</ul>

ตอนนี้ เราสามารถ navigate ระหว่างหน้าต่าง ๆ โดยใช้ link ด้านบน มาสร้างหน้าเพิ่มเติมที่จัดการข้อมูลฟอร์ม ตั้งชื่อได้ตามต้องการ ผู้เขียนชอบเรียกว่า post.html

เราสามารถ inject ข้อมูลลงในไฟล์ HTML โดยใช้ Jinja2 template engine

python
# let's import the flask
from flask import Flask, render_template, request, redirect, url_for
import os # importing operating system module

app = Flask(__name__)

@app.route('/') # this decorator create the home route
def home ():
    techs = ['HTML', 'CSS', 'Flask', 'Python']
    name = '30 Days Of Python Programming'
    return render_template('home.html', techs=techs, name = name, title = 'Home')

@app.route('/about')
def about():
    name = '30 Days Of Python Programming'
    return render_template('about.html', name = name, title = 'About Us')

@app.route('/post')
def post():
    name = 'Text Analyzer'
    return render_template('post.html', name = name, title = name)


if __name__ == '__main__':
    # for deployment
    # to make it work for both production and development
    port = int(os.environ.get("PORT", 5000))
    app.run(debug=True, host='0.0.0.0', port=port)

มาดู templates ด้วย:

home.html

python
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Home</title>
  </head>

  <body>
    <ul>
      <li><a href="/">Home</a></li>
      <li><a href="/about">About</a></li>
    </ul>
    <h1>Welcome to {{name}}</h1>
     <ul>
    {% for tech in techs %}
      <li>{{tech}}</li>
    {% endfor %}
    </ul>
  </body>
</html>

about.html

python
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>About Us</title>
  </head>

  <body>
    <ul>
      <li><a href="/">Home</a></li>
      <li><a href="/about">About</a></li>
    </ul>
    <h1>About Us</h1>
    <h2>{{name}}</h2>
  </body>
</html>

การสร้าง Layout

ในไฟล์ template มีโค้ดที่ซ้ำกันมาก เราสามารถเขียน layout และลบความซ้ำซ้อนออกได้ มาสร้าง layout.html ภายในโฟลเดอร์ templates หลังจากสร้าง layout แล้ว เราจะ import ไปใช้ในทุกไฟล์

การ Serve ไฟล์ Static

สร้างโฟลเดอร์ static ใน project directory ภายในโฟลเดอร์ static สร้างโฟลเดอร์ CSS หรือ styles แล้วสร้าง CSS stylesheet เราใช้ module url_for เพื่อ serve ไฟล์ static

layout.html

python
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <link
      href="https://fonts.googleapis.com/css?family=Lato:300,400|Nunito:300,400|Raleway:300,400,500&display=swap"
      rel="stylesheet"
    />
    <link
      rel="stylesheet"
      href="{{ url_for('static', filename='css/main.css') }}"
    />
    {% if title %}
    <title>30 Days of Python - {{ title}}</title>
    {% else %}
    <title>30 Days of Python</title>
    {% endif %}
  </head>

  <body>
    <header>
      <div class="menu-container">
        <div>
          <a class="brand-name nav-link" href="/">30DaysOfPython</a>
        </div>
        <ul class="nav-lists">
          <li class="nav-list">
            <a class="nav-link active" href="{{ url_for('home') }}">Home</a>
          </li>
          <li class="nav-list">
            <a class="nav-link active" href="{{ url_for('about') }}">About</a>
          </li>
          <li class="nav-list">
            <a class="nav-link active" href="{{ url_for('post') }}"
              >Text Analyzer</a
            >
          </li>
        </ul>
      </div>
    </header>
    <main>
      {% block content %} {% endblock %}
    </main>
  </body>
</html>

ตอนนี้ มาลบโค้ดที่ซ้ำซ้อนในไฟล์ template อื่น ๆ แล้ว import layout.html href ใช้ function url_for พร้อมชื่อ route function เพื่อเชื่อมต่อแต่ละ navigation route

home.html

python
{% extends 'layout.html' %} {% block content %}
<div class="container">
  <h1>Welcome to {{name}}</h1>
  <p>
    This application clean texts and analyse the number of word, characters and
    most frequent words in the text. Check it out by click text analyzer at the
    menu. You need the following technologies to build this web application:
  </p>
  <ul class="tech-lists">
    {% for tech in techs %}
    <li class="tech">{{tech}}</li>

    {% endfor %}
  </ul>
</div>

{% endblock %}

about.html

python
{% extends 'layout.html' %} {% block content %}
<div class="container">
  <h1>About {{name}}</h1>
  <p>
    This is a 30 days of python programming challenge. If you have been coding
    this far, you are awesome. Congratulations for the job well done!
  </p>
</div>
{% endblock %}

post.html

python
{% extends 'layout.html' %} {% block content %}
<div class="container">
  <h1>Text Analyzer</h1>
  <form action="https://thirtydaysofpython-v1.herokuapp.com/post" method="POST">
    <div>
      <textarea rows="25" name="content" autofocus></textarea>
    </div>
    <div>
      <input type="submit" class="btn" value="Process Text" />
    </div>
  </form>
</div>

{% endblock %}

Request methods มี request method หลายแบบ (GET, POST, PUT, DELETE) ซึ่งเป็น request method ทั่วไปที่ช่วยให้เราทำ CRUD (Create, Read, Update, Delete) operation ได้

ใน post route เราจะใช้ method GET และ POST สลับกันขึ้นอยู่กับประเภทของ request ดูได้ในโค้ดด้านล่าง request method เป็น function สำหรับจัดการ request methods และยังใช้เข้าถึงข้อมูล form ได้ด้วย

app.py

python
# let's import the flask
from flask import Flask, render_template, request, redirect, url_for
import os # importing operating system module

app = Flask(__name__)
# to stop caching static file
app.config['SEND_FILE_MAX_AGE_DEFAULT'] = 0



@app.route('/') # this decorator create the home route
def home ():
    techs = ['HTML', 'CSS', 'Flask', 'Python']
    name = '30 Days Of Python Programming'
    return render_template('home.html', techs=techs, name = name, title = 'Home')

@app.route('/about')
def about():
    name = '30 Days Of Python Programming'
    return render_template('about.html', name = name, title = 'About Us')

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

@app.route('/post', methods= ['GET','POST'])
def post():
    name = 'Text Analyzer'
    if request.method == 'GET':
         return render_template('post.html', name = name, title = name)
    if request.method =='POST':
        content = request.form['content']
        print(content)
        return redirect(url_for('result'))

if __name__ == '__main__':
    # for deployment
    # to make it work for both production and development
    port = int(os.environ.get("PORT", 5000))
    app.run(debug=True, host='0.0.0.0', port=port)

จนถึงตอนนี้ เราได้เรียนวิธีใช้ template วิธี inject ข้อมูลลงใน template วิธีสร้าง common layout แล้ว ตอนนี้ มาจัดการไฟล์ static กัน สร้างโฟลเดอร์ชื่อ static ใน project directory แล้วสร้างโฟลเดอร์ชื่อ css ภายใน css สร้าง main.css ไฟล์ main.css ของคุณจะถูก link ใน layout.html

ไม่จำเป็นต้องเขียนไฟล์ css เอง ให้ copy แล้วนำไปใช้ได้เลย มาต่อกันที่การ deploy

การ Deploy

การสร้างบัญชี Heroku

Heroku ให้บริการ deployment ฟรีสำหรับทั้ง front end และ fullstack applications สร้างบัญชีที่ heroku แล้วติดตั้ง heroku CLI สำหรับเครื่องของคุณ หลังจากติดตั้ง heroku แล้ว พิมพ์คำสั่งต่อไปนี้

การ Login เข้า Heroku

shell
asabeneh@Asabeneh:~$ heroku login
heroku: Press any key to open up the browser to login or q to exit:

มาดูผลลัพธ์โดยกดปุ่มใดก็ได้บนแป้นพิมพ์ เมื่อกดปุ่มใด ๆ มันจะเปิดหน้า heroku login แล้วคลิกที่หน้า login จากนั้นเครื่อง local ของคุณจะเชื่อมต่อกับ Heroku server ระยะไกล ถ้าเชื่อมต่อสำเร็จ คุณจะเห็นสิ่งนี้:

shell
asabeneh@Asabeneh:~$ heroku login
heroku: Press any key to open up the browser to login or q to exit:
Opening browser to https://cli-auth.heroku.com/auth/browser/be12987c-583a-4458-a2c2-ba2ce7f41610
Logging in... done
Logged in as asabeneh@gmail.com
asabeneh@Asabeneh:~$

การสร้าง requirements และ Procfile

ก่อน push โค้ดไปยัง remote server เราต้องมีสิ่งเหล่านี้:

  • requirements.txt
  • Procfile
shell
(env) asabeneh@Asabeneh:~/Desktop/python_for_web$ pip freeze
Click==7.0
Flask==1.1.1
itsdangerous==1.1.0
Jinja2==2.10.3
MarkupSafe==1.1.1
Werkzeug==0.16.0
(env) asabeneh@Asabeneh:~/Desktop/python_for_web$ touch requirements.txt
(env) asabeneh@Asabeneh:~/Desktop/python_for_web$ pip freeze > requirements.txt
(env) asabeneh@Asabeneh:~/Desktop/python_for_web$ cat requirements.txt
Click==7.0
Flask==1.1.1
itsdangerous==1.1.0
Jinja2==2.10.3
MarkupSafe==1.1.1
Werkzeug==0.16.0
(env) asabeneh@Asabeneh:~/Desktop/python_for_web$ touch Procfile
(env) asabeneh@Asabeneh:~/Desktop/python_for_web$ ls
Procfile          env/              static/
app.py            requirements.txt  templates/
(env) asabeneh@Asabeneh:~/Desktop/python_for_web$

Procfile จะมีคำสั่งที่รัน application บน web server ในกรณีของเราคือบน Heroku

shell
web: python app.py

การ Push Project ไปยัง Heroku

ตอนนี้พร้อม deploy แล้ว ขั้นตอนการ deploy application บน Heroku:

  1. git init
  2. git add .
  3. git commit -m "commit message"
  4. heroku create 'name of the app as one word'
  5. git push heroku master
  6. heroku open (เพื่อเปิด application ที่ deploy แล้ว)

หลังจากขั้นตอนนี้ คุณจะได้ application เหมือน http://thirdaysofpython-practice.herokuapp.com/

แบบฝึกหัด: วันที่ 26

  1. คุณจะสร้าง application นี้ https://thirtydaysofpython-v1-final.herokuapp.com/ ส่วนที่เหลือคือ text analyser เท่านั้น