01204223/flask-backend-db
- หน้านี้เป็นส่วนหนึ่งของ 01204223
เราจะแก้ส่วน backend ให้ทำงานกับ database แทนที่เราจะเขียน SQL ตรง ๆ เราจะให้ไลบรารีที่เรียกว่า ORM (Object Relational Mapper) ที่ทำให้เราเขียน-อ่านกับข้อมูลในฐานข้อมูลผ่านทาง object
เนื้อหา
โมเดล
- ส่วนนี้ใช้ gemini เขียนให้
ในบริบทของการพัฒนาซอฟต์แวร์ (Software Development) โดยเฉพาะในส่วนของ Backend หรือ Web Application คำว่า "โมเดล" (Model) คือส่วนประกอบที่ทำหน้าที่เป็น ตัวแทนของโครงสร้างข้อมูลและกฎเกณฑ์ทางธุรกิจ (Business Logic) ที่เกี่ยวข้องกับข้อมูลนั้นๆ
เพื่อให้เข้าใจง่ายที่สุด: โมเดลคือ "พิมพ์เขียว" (Blueprint) ที่บอกโปรแกรมว่าข้อมูลของเราหน้าตาเป็นอย่างไร และจะคุยกับฐานข้อมูล (Database) อย่างไร
หน้าที่หลักของ Model
Model ทำหน้าที่เป็น ตัวกลาง ระหว่างโค้ดของโปรแกรม (Application Code) กับ ฐานข้อมูล (Database) โดยมีหน้าที่หลัก 3 ประการ:
- กำหนดโครงสร้าง (Structure Definition): บอกว่าข้อมูลชุดนี้มีช่องข้อมูล (Fields/Columns) อะไรบ้าง และเป็นชนิดใด (เช่น ข้อความ, ตัวเลข, วันที่)
- จัดการข้อมูล (Data Manipulation): ใช้สำหรับ ดึง (Read), เพิ่ม (Create), แก้ไข (Update), และ ลบ (Delete) ข้อมูล หรือที่เรียกว่า CRUD
- ตรวจสอบความถูกต้อง (Validation): ตรวจสอบว่าข้อมูลที่จะบันทึกลงฐานข้อมูลนั้นถูกต้องตามกฎหรือไม่ (เช่น อีเมลต้องมีเครื่องหมาย @)
แนวคิด ORM (Object-Relational Mapping)
ในการเขียนโปรแกรมสมัยใหม่ เรามักใช้เทคนิคที่เรียกว่า ORM ผ่าน Model ซึ่งช่วยให้โปรแกรมเมอร์ไม่ต้องเขียนคำสั่ง SQL (ภาษาฐานข้อมูล) ที่ซับซ้อนโดยตรง แต่จะเขียนเป็น "Code" แทน
- Class (ในโค้ด): เปรียบเสมือน ตาราง (Table) ในฐานข้อมูล
- Object/Instance (ตัวแปร): เปรียบเสมือน แถว (Row) ของข้อมูล 1 รายการ
- Attribute (ตัวแปรในคลาส): เปรียบเสมือน คอลัมน์ (Column)
ตัวอย่างโค้ด (SQLAlchemy)
สมมติว่าเราต้องการเก็บข้อมูล "ผู้ใช้งาน" (User) โดยใช้ SQLAlchemy แบบดั้งเดิม (Declarative Base)
การนิยามโมเดล (Model Definition)
เราจะสร้างคลาสเพื่อเป็นตัวแทนตารางในฐานข้อมูล
class User(Base):
# กำหนดโครงสร้างข้อมูล (Columns)
id = Column(Integer, primary_key=True)
name = Column(String(100))
email = Column(String(100), unique=True)
age = Column(Integer)
# กฎทางธุรกิจ (Business Logic)
def is_adult(self):
return self.age >= 18
ในการใช้งานเราสามารถสั่ง
user.is_adult()
ได้โดยตรง
Code เริ่มต้นของ project
เพื่อความสะดวกเราจะเริ่มต้นจากโค้ดตัวอย่างที่รวบรวมมาแล้ว โดย clone จาก https://github.com/jittat/01204223-flask-react-todo-68
หมายเหตุ โค้ดของเก่ามีผิดพลาดในส่วนของ python backend ตอนที่สั่ง getattr(data,'done',False) จริง ๆ แล้วต้องสั่ง data.get('done', False) เพราะว่า data เป็น dict ไม่ใช่ object ใน repo นี้แก้ไขแล้ว
เราจะแยกไดเร็กทอรีภายในเป็น backend และ frontend ให้ไปติดตั้งไลบรารีของทั้งสองส่วนดังนี้
Backend
เข้าไปใน backend (ถ้าเป็น windows อย่าลืมใช้ cmd อย่าใช้ powershell)
cd backend
เราจะสร้าง virtual environment และติดตั้งไลบรารี ขั้นแรกสร้าง virtual environment ก่อน
python -m venv venv
แล้ว activate ถ้าเป็น linux/mac เรียก
./venv/bin/activate
ถ้าเป็น windows เรียก
venv\Scripts\activate.bat
เมื่อ activate venv แล้ว ให้เรียก pip เพื่อติดตั้งไลบรารี เราได้รวบรวมรายกายไลบรารีไว้ในไฟล์ requirements.txt แล้ว ดังนั้นเราจะสั่ง
pip install -r requirements.txt
เพื่อติดตั้งได้เลย ถ้าติดตั้งเรียบร้อยทดลองเรียก flask backend ได้
ขั้นแรก ถ้าเป็น linux/mac ให้เรียก
export FLASK_APP=main.py
ถ้าเป็น windows เรียก
set FLASK_APP=main.py
แล้วเรียก
flask run --debug
Frontend
เปิดอีก terminal หนึ่ง แล้วเรียก
cd frontend
เรียก npm เพื่อติดตั้งไลบรารี
npm install
แล้วเรียก
npm run dev
เพื่อเปิดเซิร์ฟเวอร์ของ frontend react
ระบบฐานข้อมูล
เพื่อความง่าย ในตอนแรกเราจะใช้ระบบฐานข้อมูล sqlite ซึ่งเป็น database system ที่เก็บข้อมูลในไฟล์ และมากับ Python แล้ว เมื่อเราทำเสร็จ เราจะย้ายไปทดลองกับ MySQL หรือ dbms ตัวอื่นที่นิสิตได้เคยติดตั้งตอนเรียนวิชา database
การติดตั้งไลบรารี SQLAlchemy และ SQLAlchemy-Flask
เราจะใช้ SQLAlchemy และ Flask-SQLAlchemy โดยจะต้องติดตั้งไลบรารีเพิ่มเติม ให้เข้าไปในส่วน backend และ activate virtual environment ก่อน จากนั้นสั่ง
pip install Flask-SQLAlchemy
เมื่อติดตั้งเรียบร้อยแล้ว เราจะอัพเดทไฟล์ requirements.txt เพื่อให้รวมไลบรารีเหล่านี้ด้วย ถ้าเราลองสั่ง
pip freeze
เราจะได้รายการของไลบรารีทั้งหมดที่เราติดตั้งผ่าน pip เราจะเก็บข้อมูลดังกล่าวลงใน requirements.txt โดยสั่ง (เป็นการทำ redirection)
pip freeze > requirements.txt
การเก็บรายการไลบรารีแบบนี้จะมีประโยชน์เมื่อเราต้องการรันระบบของเราที่เครื่องอื่นนอกเหนือจากเครื่องที่เราใช้พัฒนาเอง (และจำเป็นตอนสร้าง container image ตอนที่เราเรียน DevOp ต่อไปด้วย)
โมเดลแรก
เราจะแก้ไข main.py โดยเริ่มจากการ import ไลบรารีที่จำเป็นที่หัวโปรแกรม
from flask_sqlalchemy import SQLAlchemy
from sqlalchemy.orm import DeclarativeBase
from sqlalchemy import Integer, String
from sqlalchemy.orm import Mapped, mapped_column
ในการใช้งาน SQLAlchemy ใน Flask นั้นเราจะต้อง init ไลบรารีก่อน รวมทั้งตั้งค่าเกี่ยวกับฐานข้อมูลที่เราจะใช้ ให้เพิ่มโค้ดต่อไปนี้หลังบรรทัด app = Flask(__name__) และ CORS(app)
ส่วนแรกเป็นการตั้งค่าการเชื่อมต่อฐานข้อมูล ถ้าเราจะเปลี่ยนไปใช้ระบบฐานข้อมูลอื่น เราจะแก้ไขตรงนี้
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///todos.db'
จากนั้นจะสร้างตัวแปร db ที่จะเป็นจุดที่เราเรียกไลบรารีทั้งหมด โดยต้องมีการผูกเข้ากับ Flask app รวมทั้งมีการระบุด้วยว่าในการสร้างโมเดลทั้งหมดจะใช้อะไรเป็น base class (ในที่นี้เราใช้คลาส Base ที่เป็น DeclarativeBase อีกที
class Base(DeclarativeBase):
pass
db = SQLAlchemy(app, model_class=Base)