Thursday, November 21, 2024

[Flask] – Phần 11: Phân tách các modules với Blueprint

-

1. Tổng quan.

Trong Flask, các khái niệm “module” và “Blueprint” được sử dụng để tạo ứng dụng web Flask một cách có cấu trúc và dễ quản lý khi bạn có một số lượng lớn các route và view functions. Dưới đây là giải thích về cả hai khái niệm:

  • Module (module): Trong Flask, một module đề cập đến một file Python (file Python) chứa các route và view functions để xử lý các yêu cầu HTTP từ người dùng. Mỗi module thường chứa một số route và view functions liên quan đến một phần của ứng dụng web của bạn. Sử dụng module giúp bạn chia nhỏ ứng dụng của mình thành các phần nhỏ hơn để dễ quản lý.Ví dụ, bạn có thể có một module cho việc xử lý đăng nhập và đăng ký người dùng, và một module khác cho việc quản lý dữ liệu người dùng.
  • Blueprint: Blueprint là một cách trong Flask để bạn tạo ra các phần ứng dụng con (sub-applications) có thể được gắn vào ứng dụng chính một cách dễ dàng. Blueprint cho phép bạn định nghĩa các route và view functions trong một module riêng biệt, sau đó gắn nó vào ứng dụng chính bằng cách sử dụng app.register_blueprint().Blueprints giúp bạn tổ chức mã nguồn của ứng dụng một cách cấu trúc hơn, đặc biệt khi bạn làm việc với các ứng dụng lớn hoặc khi bạn muốn tạo các ứng dụng con riêng biệt có thể tái sử dụng trong nhiều dự án khác nhau.Ví dụ, bạn có thể tạo một Blueprint cho phần quản lý người dùng của ứng dụng và sau đó gắn nó vào ứng dụng chính.

Dưới đây là ví dụ cơ bản về cách sử dụng Blueprint trong Flask:

# Tạo một Blueprint
from flask import Blueprint

user_bp = Blueprint('user_bp', __name__)

# Định nghĩa route và view function cho Blueprint
@user_bp.route('/profile')
def profile():
    return 'User Profile Page'

# Gắn Blueprint vào ứng dụng chính
app.register_blueprint(user_bp, url_prefix='/user')

Trong ví dụ này, chúng ta đã tạo một Blueprint có tên là “user_bp” và định nghĩa một route “/profile” trong đó. Sau đó, chúng ta đã gắn Blueprint này vào ứng dụng chính với tiền tố URL “/user”.

2. Thực hành.

Bài này chúng ta tiếp tục lấy ví dụ trước để thực hành luôn nhé. Bạn hãy chuẩn bị 1 Project với cấu trúc file và thư mục như sau.

../myapp/
├── flask_blueprint.py
├── requirements.txt
├── templates
│   ├── base.html
│   ├── home.html
│   ├── login.html
│   └── user.html
└── user.py

File requirements.txt bạn hãy bổ sung các module cần sử dụng và sau đó dùng lệnh pip install -r requirements.txt để cài đặt các module cần thiết.

sqlalchemy
PyMySQL
mysqlclient

File base.html giữ nguyên nội dung cũ.

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>{% block title %} {% endblock %}</title>
    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@4.3.1/dist/css/bootstrap.min.css" integrity="sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T" crossorigin="anonymous">
</head>
<body>
    <h1>My website</h1>
    <nav class="navbar navbar-expand-lg navbar-light bg-light">
        <a class="navbar-brand" href="#">Navbar</a>
        <button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation">
          <span class="navbar-toggler-icon"></span>
        </button>
      
        <div class="collapse navbar-collapse" id="navbarSupportedContent">
          <ul class="navbar-nav mr-auto">
            <li class="nav-item active">
              <a class="nav-link" href="/home">Home</a>
            </li>
            <li class="nav-item active">
              <a class="nav-link" href="/login">Login </a>
            </li>
            <li class="nav-item active">
              <a class="nav-link" href="/logout">Logout</a>
            </li>
          </ul>
        </div>
      </nav>
    {% block content %} {% endblock %}
</body>
    <script src="https://code.jquery.com/jquery-3.3.1.slim.min.js" integrity="sha384-q8i/X+965DzO0rT7abK41JStQIAqVgRVzpbzo5smXKp4YfRvH+8abtTE1Pi6jizo" crossorigin="anonymous"></script>
    <script src="https://cdn.jsdelivr.net/npm/popper.js@1.14.7/dist/umd/popper.min.js" integrity="sha384-UO2eT0CpHqdSJQ6hJty5KVphtPhzWj9WO1clHTMGa3JDZwrnQq4sF86dIHNDz0W1" crossorigin="anonymous"></script>
    <script src="https://cdn.jsdelivr.net/npm/bootstrap@4.3.1/dist/js/bootstrap.min.js" integrity="sha384-JjSmVgyd0p3pXB1rRibZUAYoIIy6OrQ6VrjIEaFf/nJGzIxFDsf4x0xIM+B07jRM" crossorigin="anonymous"></script>
</html>

File home.html cũng giữ nguyên như cũ.

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

{% block content %}
<h2>This is Home Pages</h2>
{% endblock %}

File login.html cũng giữ nguyên như cũ.

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

{% block content %}
<h2>This is Login Pages</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="/login" method="post">
        <label for="name">Name</label>
        <input type="text" name="name" id="name"></input>
        <button style="background-color: rgb(155, 19, 132); color: white;" type="submit">Login</button>
    </form>
</div>
{% endblock %}

File user.html cũng giữ nguyên như cũ.

{% 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 %}

Trong đoạn code Python tiếp theo có một sự kết hợp giữa Flask và Blueprint để xây dựng ứng dụng web. Với file flask_blueprint.py mình sẽ tạo và paste vào nội dung sau.

Ở đây mình thêm from user import myapp, user_myapp để import biến myapp và Blueprint user_myapp từ file user.py vào file hiện tại flask_blueprint.p, đây là file chính của ứng dụng. Điều này cho phép bạn sử dụng myappuser_myapp trong file hiện tại mà không cần phải định nghĩa chúng lại.

myapp.register_blueprint(user_myapp) này sẽ đăng ký Blueprint user_myapp vào ứng dụng Flask myapp. Điều này có nghĩa rằng tất cả các route và view functions được định nghĩa trong user_myapp sẽ trở nên khả dụng trong ứng dụng chính myapp.

from flask import render_template
from datetime import timedelta
from user import myapp,user_myapp

myapp.register_blueprint(user_myapp)

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

if __name__ == "__main__":
    myapp.run(debug=True, host='0.0.0.0', port=5000)

Và file user.py và paste vào đoạn code này, đoạn code này tương tự như các bài trước mình không giải thích thêm. Chỉ khác là mình thêm vào module Blueprint, tạo biến user_myapp = Blueprint("user_bp", __name__), thay đổi route (ví dụ @user_myapp.route) và chỉnh sửa lại phần redirect thêm user_bp.<tên hàm> (ví dụ redirect(url_for("user_bp.login"))

Chú ý tại các route, ví dụ@user_myapp.route('/login', methods=["POST", "GET"]): Khi bạn định nghĩa route trong một Blueprint, bạn cần sử dụng decorator @user_myapp.route thay vì @myapp.route để chỉ định route cho Blueprint cụ thể. Điều này giúp Flask biết rằng route này thuộc về Blueprint.

Trong Flask, khi bạn đăng ký một Blueprint trong ứng dụng chính, bạn có thể sử dụng url_for để tạo URL đến các route và view functions trong Blueprint đó. Khi bạn sử dụng url_for, bạn cần chỉ định tên của Blueprint (trong trường hợp này là “user_bp”) để xác định route hoặc view function bạn muốn tạo URL.

Trong trường hợp của bạn, khi bạn sử dụng redirect(url_for("user_bp.user")), bạn đang tạo một redirection đến route hoặc view function có tên “user” trong Blueprint “user_bp”. Việc chỉ định tên Blueprint (“user_bp”) giúp Flask hiểu rằng route hoặc view function bạn đang muốn truy cập thuộc về Blueprint “user_bp”, không phải là một route hoặc view function tại cấp độ ứng dụng chính.

Nếu bạn chỉ sử dụng url_for("user.user") mà không chỉ định tên của Blueprint, Flask có thể gây hiểu nhầm và tìm kiếm route hoặc view function “user” trực tiếp tại cấp độ ứng dụng chính, thay vì trong Blueprint “user_bp”. Bằng cách chỉ định tên Blueprint, bạn đảm bảo Flask biết chính xác địa chỉ của route hoặc view function bạn muốn truy cập.

from flask import Flask, redirect, url_for, render_template, request, session, Blueprint
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)

user_myapp = Blueprint("user_bp", __name__)

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

@user_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_bp.user"))
    if "user" in session:
        return redirect(url_for("user_bp.user"))
    return render_template("login.html")

@user_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("user_bp.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("user_bp.login"))

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

Kết quả cuối cùng thì không có gì thay đổi, khác trước là mình đã phân tách project của mình thành từng phần nhỏ, trong đó file flask_blueprint.py là file chính và user.py là file chứa chức năng về xử lý user.

LEAVE A REPLY

Please enter your comment!
Please enter your name here

4,956FansLike
256FollowersFollow
223SubscribersSubscribe
spot_img

Related Stories