Thursday, November 21, 2024

[Flask] – Phần 10: Tạo Database với SQLAlchemy sử dụng MySQL

-

Bài trước chúng ta đã tìm hiểu SQLAlchemy là gì và thực hành nó với SQLite, bài này chúng ta hãy thử thực hành tạo Database trên MySQL bằng SQLAlchemy nhé.

Để cài đặt được module mysqlclient cho Python, bạn cần cài đặt các gói tin cần thiết sau:

sudo apt-get update
sudo apt-get install gcc libmysqlclient-dev python3-dev -y 
sudo apt install pkg-config -y 

Vào trong môi trường ảo bạn hãy cài đặt SQLAlchemy bằng lệnh pip install sqlalchemy.

$ pip install Flask-SQLAlchemy
Collecting sqlalchemy
  Downloading SQLAlchemy-2.0.20-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (3.0 MB)
     ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 3.0/3.0 MB 11.9 MB/s eta 0:00:00
Collecting greenlet!=0.4.17
  Downloading greenlet-2.0.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (613 kB)
     ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 613.7/613.7 KB 23.2 MB/s eta 0:00:00
Collecting typing-extensions>=4.2.0
  Downloading typing_extensions-4.7.1-py3-none-any.whl (33 kB)
Installing collected packages: typing-extensions, greenlet, sqlalchemy
Successfully installed greenlet-2.0.2 sqlalchemy-2.0.20 typing-extensions-4.7.1

Tiếp tục cài đặt module mysqlclient.

$ pip install mysqlclient
Collecting mysqlclient
  Using cached mysqlclient-2.2.0.tar.gz (89 kB)
  Installing build dependencies ... done
  Getting requirements to build wheel ... done
  Installing backend dependencies ... done
  Preparing metadata (pyproject.toml) ... done
Requirement already satisfied: flask>=2.2.5 in /home/myproject/.venv/lib/python3.10/site-packages (from Flask-SQLAlchemy->-r requirements.txt (line 1)) (2.3.3)
Requirement already satisfied: sqlalchemy>=1.4.18 in /home/myproject/.venv/lib/python3.10/site-packages (from Flask-SQLAlchemy->-r requirements.txt (line 1)) (2.0.20)
Requirement already satisfied: bcrypt>=3.1.1 in /home/myproject/.venv/lib/python3.10/site-packages (from Flask-Bcrypt->-r requirements.txt (line 2)) (4.0.1)
Requirement already satisfied: blinker>=1.6.2 in /home/myproject/.venv/lib/python3.10/site-packages (from flask>=2.2.5->Flask-SQLAlchemy->-r requirements.txt (line 1)) (1.6.2)
Requirement already satisfied: itsdangerous>=2.1.2 in /home/myproject/.venv/lib/python3.10/site-packages (from flask>=2.2.5->Flask-SQLAlchemy->-r requirements.txt (line 1)) (2.1.2)
Requirement already satisfied: Werkzeug>=2.3.7 in /home/myproject/.venv/lib/python3.10/site-packages (from flask>=2.2.5->Flask-SQLAlchemy->-r requirements.txt (line 1)) (2.3.7)
Requirement already satisfied: click>=8.1.3 in /home/myproject/.venv/lib/python3.10/site-packages (from flask>=2.2.5->Flask-SQLAlchemy->-r requirements.txt (line 1)) (8.1.7)
Requirement already satisfied: Jinja2>=3.1.2 in /home/myproject/.venv/lib/python3.10/site-packages (from flask>=2.2.5->Flask-SQLAlchemy->-r requirements.txt (line 1)) (3.1.2)
Requirement already satisfied: greenlet!=0.4.17 in /home/myproject/.venv/lib/python3.10/site-packages (from sqlalchemy>=1.4.18->Flask-SQLAlchemy->-r requirements.txt (line 1)) (2.0.2)
Requirement already satisfied: typing-extensions>=4.2.0 in /home/myproject/.venv/lib/python3.10/site-packages (from sqlalchemy>=1.4.18->Flask-SQLAlchemy->-r requirements.txt (line 1)) (4.7.1)
Requirement already satisfied: MarkupSafe>=2.0 in /home/myproject/.venv/lib/python3.10/site-packages (from Jinja2>=3.1.2->flask>=2.2.5->Flask-SQLAlchemy->-r requirements.txt (line 1)) (2.1.3)
Building wheels for collected packages: mysqlclient
  Building wheel for mysqlclient (pyproject.toml) ... done
  Created wheel for mysqlclient: filename=mysqlclient-2.2.0-cp310-cp310-linux_x86_64.whl size=123666 sha256=984152e71d3771685c41feb0a802f0145cbb7fd8702dced6090750b271b4722f
  Stored in directory: /root/.cache/pip/wheels/a4/f8/fd/0399687c0abd03c10c975ed56c692fcd3d0fb80440b5a661f1
Successfully built mysqlclient
Installing collected packages: mysqlclient
Successfully installed mysqlclient-2.2.0

Hãy xây dựng project của bạn theo cấu trúc như dưới đây.

../myapp/
├── SQLAlchemy.py
└── templates
    ├── base.html
    ├── home.html
    ├── login.html
    └── user.html

Các file login.html, base.htmlhome.html mình sẽ giữ nguyên ở bài trước.

Nội dung file user.html.

{% extends "base.html" %}
{% block title %}Login Page{% endblock %}

{% block content %}
<h2>Hello {{user}}</h2>
{% with messages = get_flashed_messages() %}
    {% if messages %}
        {% for m in messages %}
            <p>{{m}}</p>
        {% endfor %}
    {% endif %}
{% endwith %}
<div style="margin-left: 50px;">
    <form action="#" method="post">
        <label for="email">Email</label>
        <input type="email" name="email" placeholder="Enter email..." value="{{email if email}}"></input>
        <button style="background-color: slateblue; color: white;" type="submit">Submit</button>
        <input type="name" name="name" placeholder="Enter name..." value="{{user if user}}"></input>
        <button style="background-color: slateblue; color: white;" type="submit">Delete</button>
    </form>
</div>
{% endblock %}

Đoạn code HTML trên mình chỉnh sửa phần (form) trong trang web và dưới đây là giải thích từng phần của nó:

  • <form action="#" method="post">: Đây là phần bắt đầu của biểu mẫu. Nó xác định phương thức HTTP (POST) và URL mục tiêu (action). Trong trường hợp này, action được đặt thành "#" nghĩa là biểu mẫu sẽ gửi yêu cầu POST đến chính trang web hiện tại.
  • <label for="email">Email</label>: Đây là một nhãn (label) cho trường nhập email. Thuộc tính for của nhãn được liên kết với thuộc tính id của trường nhập dữ liệu email, giúp cải thiện khả năng tương tác của người dùng.
  • <input type="email" name="email" placeholder="Enter email..." value="{{email if email}}">: Đây là trường nhập dữ liệu email. Các thuộc tính quan trọng là:
    • type="email": Xác định loại dữ liệu mà trường này sẽ chấp nhận, trong trường hợp này là địa chỉ email.
    • name="email": Đặt tên cho trường nhập, được sử dụng để xác định dữ liệu trong yêu cầu khi biểu mẫu được gửi đi.
    • placeholder="Enter email...": Hiển thị một dòng văn bản mà người dùng có thể thấy bên trong trường nhập, đóng vai trò hướng dẫn cho họ.
    • value="{{email if email}}": Giá trị mặc định của trường nhập. Trong Flask, {{...}} được sử dụng để thay thế giá trị từ biến Python trong template. Trong trường hợp này, nếu biến email tồn tại (không rỗng), thì giá trị mặc định của trường nhập sẽ là giá trị của biến email.
  • <button style="background-color: slateblue; color: white;" type="submit">Submit</button>: Đây là nút gửi biểu mẫu. Nút này có văn bản là “Submit” và sẽ gửi yêu cầu POST khi người dùng nhấn vào nó. Thuộc tính style được sử dụng để định dạng giao diện người dùng của nút.
  • <input type="name" name="name" placeholder="Enter name..." value="{{user if user}}">: Đây là trường nhập dữ liệu tên. Các thuộc tính tương tự như trường email, bao gồm type, name, placeholder, và value. Trong trường hợp này, giá trị mặc định của trường nhập được thiết lập bằng giá trị của biến user nếu biến này tồn tại.
  • <button style="background-color: slateblue; color: white;" type="submit">Delete</button>: Đây là nút “Delete” cho việc xóa tài khoản người dùng. Nút này cũng gửi yêu cầu POST khi người dùng nhấn vào nó và có một giao diện người dùng tương tự như nút “Submit”.

Nội dung file SQLAlchemy.py.

Phần này sẽ tạo một ứng dụng web Flask đơn giản để quản lý đăng nhập người dùng và lưu trữ thông tin người dùng trong cơ sở dữ liệu MySQL.

from flask import Flask, redirect, url_for, render_template, request, session
from datetime import timedelta
from flask.helpers import flash
from flask_sqlalchemy import SQLAlchemy

myapp = Flask(__name__)
myapp.config["SECRET_KEY"] = "hoanghd-secret-key"
myapp.config["SQLALCHEMY_DATABASE_URI"] = "mysql://db_username:db_password@db_host/db_name"
myapp.permanent_session_lifetime = timedelta(minutes=1)

db = SQLAlchemy(myapp)

class User(db.Model):
    user_id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(100))
    email = db.Column(db.String(100))

    def __init__(self, name, email):
        self.name = name
        self.email = email

@myapp.route('/home')
def hello_world():
    return render_template("home.html")

@myapp.route('/login', methods=["POST", "GET"])
def login():
    if request.method == "POST":
        user_name = request.form["name"]
        session.permanent = True
        if user_name:
            session["user"] = user_name
            found_user = User.query.filter_by(name=user_name).first()
            if found_user:
                session["email"] = found_user.email
            else:
                user = User(user_name, "temp@gmail.com")
                db.session.add(user)
                db.session.commit()
                flash("Created in DB!")
            return redirect(url_for("user"))
    if "user" in session:
        return redirect(url_for("user"))
    return render_template("login.html")

@myapp.route("/user", methods=["POST", "GET"])
def user():
    email = None
    if "user" in session:
        name = session["user"]
        if request.method == "POST":
            if not request.form["email"] and request.form["name"]:
                User.query.filter_by(name=name).delete()
                db.session.commit()
                flash("Deleted user!")
                return redirect(url_for("log_out"))
            else:
                email = request.form["email"]
                session["email"] = email
                found_user = User.query.filter_by(name=name).first()
                found_user.email = email
                db.session.commit()
                flash("Email updated!")
                print(email)
        return render_template("user.html", user=name, email=email)
    else:
        return redirect(url_for("login"))

@myapp.route("/logout")
def log_out():
    session.pop("user", None)
    return redirect(url_for("login"))

if __name__ == "__main__":
    myapp.run(debug=True, host='0.0.0.0', port=5000)
  • Import các thư viện và modules cần thiết:
    • Flask: Được sử dụng để tạo ứng dụng web Flask.
    • redirect, url_for, render_template, request, session: Các chức năng và modules từ Flask cho việc định tuyến, hiển thị template HTML, xử lý yêu cầu từ người dùng và quản lý phiên làm việc.
    • timedelta: Được sử dụng để đặt thời gian sống của phiên làm việc (session lifetime).
    • flash: Dùng để hiển thị thông báo cho người dùng.
    • SQLAlchemy: Thư viện ORM (Object-Relational Mapping) cho việc tương tác với cơ sở dữ liệu MySQL.
  • Khởi tạo ứng dụng Flask:
    • Tạo một đối tượng Flask với tên myapp.
    • Đặt cấu hình cho ứng dụng, bao gồm cấu hình cho việc bảo mật (SECRET_KEY), đường dẫn cơ sở dữ liệu MySQL (SQLALCHEMY_DATABASE_URI), và đặt thời gian sống của phiên làm việc (permanent_session_lifetime).
  • Tạo đối tượng cơ sở dữ liệu SQLAlchemy:
    • Sử dụng SQLAlchemy để kết nối và tương tác với cơ sở dữ liệu MySQL.
    • Định nghĩa một lớp User để biểu diễn các bản ghi trong bảng User trong cơ sở dữ liệu. Các trường trong bảng bao gồm user_id, name, và email.
  • Định nghĩa các route và xử lý chức năng:
    • @myapp.route('/home'): Định nghĩa một route cho trang chủ. Nó trả về một template HTML là “home.html” khi người dùng truy cập.
    • @myapp.route('/login', methods=["POST", "GET"]): Định nghĩa route cho trang đăng nhập. Nó xử lý cả yêu cầu GET và POST. Nếu người dùng đã đăng nhập, họ sẽ được chuyển hướng đến trang /user. Nếu người dùng gửi một biểu mẫu POST (đăng nhập), nó sẽ kiểm tra thông tin đăng nhập, tạo hoặc cập nhật thông tin người dùng trong cơ sở dữ liệu và đặt phiên làm việc (session) cho người dùng. Đồng thời, nếu người dùng đã tồn tại, nó sẽ lấy thông tin email của họ.
    • @myapp.route("/user", methods=["POST", "GET"]): Định nghĩa route cho trang người dùng. Nếu người dùng đã đăng nhập, họ có thể thấy thông tin cá nhân của họ và cập nhật email. Nếu người dùng gửi biểu mẫu POST để cập nhật email hoặc xóa tài khoản, thì thông tin sẽ được cập nhật trong cơ sở dữ liệu.
    • @myapp.route("/logout"): Định nghĩa route cho việc đăng xuất. Nó xóa phiên làm việc của người dùng và chuyển hướng họ đến trang đăng nhập.
  • Cuối cùng, ứng dụng kiểm tra xem nó có đang chạy trong chế độ main (__name__ == "__main__") và nếu vậy, nó sẽ bắt đầu chạy ứng dụng Flask trên máy chủ cục bộ, lắng nghe các yêu cầu từ người dùng trên cổng 5000.

Nhớ vào DB tạo thủ công Database, Table và các trường cho nó nhé.

Hãy chạy ứng dụng SQLAlchemy của bạn.

$ python SQLAlchemy.py 
 * Serving Flask app 'SQLAlchemy.py'
 * Debug mode: on
 * Running on http://127.0.0.1:5000
 * Running on http://192.168.13.200:5000
Press CTRL+C to quit
 * Restarting with stat
 * Debugger is active!
 * Debugger PIN: 176-096-173

Kết quả.

Nếu bạn chưa xóa dữ liệu người dùng thì trong DB của bạn sẽ có thông tin của người dùng nhé.

Tới đây bạn đã biết cách sử dụng SQLAlchemy để truy vấn tới MySQL tạo một form đơn giản rồi, chúc các bạn thành công

LEAVE A REPLY

Please enter your comment!
Please enter your name here

4,956FansLike
256FollowersFollow
223SubscribersSubscribe
spot_img

Related Stories