Wednesday, July 3, 2024

[Flask] – Phần 14 (phần cuối): Hướng dẫn xây dựng trang đăng ký tài khoản

-

Trong hơn một tá bài học trước đây, chúng ta đã cùng nhau khám phá những kiến thức cơ bản để xây dựng một trang web bằng Flask sử dụng ngôn ngữ lập trình Python. Bây giờ, chúng ta đã đến phần cuối cùng của chuỗi hướng dẫn này, và trong phần này, chúng tôi sẽ hướng dẫn bạn cách tạo một biểu mẫu đăng ký tài khoản hoàn chỉnh và cơ bản nhất. Dưới đây là nội dung cụ thể của phần này:

1. Chuẩn bị mô trường.

4.1. Chuẩn bị môi trường cần thiết.

  • Chuẩn bị OS, mình sử dụng Ubuntu 22.04.
$ lsb_release -a
No LSB modules are available.
Distributor ID: Ubuntu
Description:    Ubuntu 22.04.3 LTS
Release:        22.04
Codename:       jammy
  • Cài đặt môi trường.
apt-get update
apt install python3-pip -y
apt install python3.10-venv python3-virtualenv -y
apt-get install gcc libmysqlclient-dev python3-dev -y 
apt install pkg-config -y 
  • Chuẩn bị môi trường ảo.
cd /home
mkdir myproject
cd myproject
python3 -m venv .venv

2. Chuẩn bị code HTML, CSS.

Download code HTML và CSS tại https://wiki.hoanghd.com/wp-content/uploads/codes/form-main.zip.

Giải nén nó ra và bạn sẽ có 1 cây thư mục với cấu trúc file như sau:

../Form--main/
├── background6.jpg
├── index.css
└── index.html

Đây là giao diện cơ bản của nó khi bạn mở file index.html bằng trình duyệt.

3. Cấu trúc file và thư mục.

Và từ đoạn source code trên mình sẽ chỉnh sửa lại nó theo cấu trúc như sau:

usermanager/
├── requirements.txt
├── static
│   ├── images
│   │   └── f_login_background.jpg
│   └── styles
│       └── f_login.css
├── templates
│   ├── base.html
│   ├── forgot_password.html
│   ├── home.html
│   ├── login.html
│   └── register.html
└── usermanager.py

Cấu trúc thư mục này là một phần quan trọng của dự án web Flask hoặc bất kỳ dự án web nào khác và giúp bạn tổ chức và quản lý các file và mẫu một cách dễ dàng.

Dưới đây là giải thích cho cấu trúc thư mục này:

  • ../usermanager/: Đây là thư mục gốc của dự án, có thể là tên của ứng dụng web hoặc dự án. Tất cả các file và thư mục khác trong dự án sẽ được tổ chức dưới đây.
  • requirements.txt: dùng để chứa các module cần thiết cho dự án.
  • static/: Đây là thư mục chứa tất cả các file tĩnh, chẳng hạn như hình ảnh và file CSS, mà trang web của bạn sử dụng để hiển thị giao diện và nội dung. Các file tĩnh này không thay đổi dựa trên dữ liệu của người dùng và được phục vụ trực tiếp cho trình duyệt của họ.
    • images/: Thư mục này chứa tất cả các hình ảnh được sử dụng trong trang web của bạn. Trong ví dụ này, có một file hình ảnh có tên là “f_login_background.jpg”.
    • styles/: Thư mục này chứa tất cả các file CSS để tạo kiểu cho trang web. Trong ví dụ này, có một file CSS có tên là “f_login.css”.
  • templates/: Đây là thư mục chứa các mẫu HTML được sử dụng để hiển thị nội dung trên trang web của bạn. Mẫu HTML giúp bạn tạo ra giao diện người dùng và hiển thị dữ liệu từ máy chủ web của bạn.
    • base.html: Đây có thể là một mẫu cơ sở chung được sử dụng cho tất cả các trang trong trang web của bạn. Nó có thể chứa phần đầu (header) và phần chân (footer) của trang web.
    • forgot_password.html: Mẫu này có thể được sử dụng để hiển thị giao diện cho việc quên mật khẩu.
    • home.html: Mẫu này có thể là trang chủ của trang web của bạn, nơi bạn hiển thị thông tin cơ bản.
    • login.html: Mẫu này có thể được sử dụng để hiển thị giao diện đăng nhập.
    • register.html: Mẫu này có thể được sử dụng để hiển thị giao diện đăng ký tài khoản.

4. Chuẩn bị module phục vụ cho dự án.

Tại thư mục usermanager tạo file requirements.txt như cây thư mục đã nói ở trên và truyền vào các module sau.

Flask
Flask-SQLAlchemy
Flask-Bcrypt
mysqlclient
  • Vào môi trường ảo và cài đặt các module đã khai báo trong file requirements.txt vào môi trường này.
. .venv/bin/activate
pip install -r requirements.txt

4. Nội dung của từng file sau khi đã chỉnh sửa.

4.1. File f_login.css.

Với file này mình chỉ chỉnh sửa phần .form-box với height600px với mục đích là tăng kích cỡ form đăng ký.

@import url('https://fonts.googleapis.com/css2?family=Poppins:wght@500&display=swap');
*{
    margin: 0;
    padding: 0;
    font-family: 'poppins',sans-serif;
}
section{
    display: flex;
    justify-content: center;
    align-items: center;
    min-height: 100vh;
    width: 100%;
    
    background: url('../images/f_login_background.jpg')no-repeat;
    background-position: center;
    background-size: cover;
}
.form-box{
    position: relative;
    width: 400px;
    height: 600px;
    background: transparent;
    border: 2px solid rgba(255,255,255,0.5);
    border-radius: 20px;
    backdrop-filter: blur(15px);
    display: flex;
    justify-content: center;
    align-items: center;

}
h2{
    font-size: 2em;
    color: #fff;
    text-align: center;
}
.inputbox{
    position: relative;
    margin: 30px 0;
    width: 310px;
    border-bottom: 2px solid #fff;
}
.inputbox label{
    position: absolute;
    top: 50%;
    left: 5px;
    transform: translateY(-50%);
    color: #fff;
    font-size: 1em;
    pointer-events: none;
    transition: .5s;
}
input:focus ~ label,
input:valid ~ label{
top: -5px;
}
.inputbox input {
    width: 100%;
    height: 50px;
    background: transparent;
    border: none;
    outline: none;
    font-size: 1em;
    padding:0 35px 0 5px;
    color: #fff;
}
.inputbox ion-icon{
    position: absolute;
    right: 8px;
    color: #fff;
    font-size: 1.2em;
    top: 20px;
}
.forget{
    margin: -15px 0 15px ;
    font-size: .9em;
    color: #fff;
    display: flex;
    justify-content: space-between;  
}

.forget label input{
    margin-right: 3px;
    
}
.forget label a{
    color: #fff;
    text-decoration: none;
}
.forget label a:hover{
    text-decoration: underline;
}
button{
    width: 100%;
    height: 40px;
    border-radius: 40px;
    background: #fff;
    border: none;
    outline: none;
    cursor: pointer;
    font-size: 1em;
    font-weight: 600;
}
.register{
    font-size: .9em;
    color: #fff;
    text-align: center;
    margin: 25px 0 10px;
}
.register p a{
    text-decoration: none;
    color: #fff;
    font-weight: 600;
}
.register p a:hover{
    text-decoration: underline;
}

4.2. File base.html.

File này mình copy từ file index.html và chỉnh sửa nội dung sau:

  • Dùng Jinja đục lỗ ở phần tiêu đề <title>{% block title %} {% endblock %}</title>.
  • Xoá bỏ nội dung ở trong khối section và dùng Jinja đục lỗ để truyền phần nội dung động của content vào đây {% block content %} {% endblock %}.
<!DOCTYPE html>
<html lang="en">
<head>
  <link rel="stylesheet" href="{{url_for('static', filename= 'styles/f_login.css')}}">
  <title>{% block title %} {% endblock %}</title>
</head>
<body>
    <section>
        {% block content %} {% endblock %}
    </section>
    <script type="module" src="https://unpkg.com/ionicons@5.5.2/dist/ionicons/ionicons.esm.js"></script>
    <script nomodule src="https://unpkg.com/ionicons@5.5.2/dist/ionicons/ionicons.js"></script>
</body>
</html>

4.3. File forgot_password.html.

File này mình truyền nội dung động bằng Jinja.

  • {% extends “base.html” %}: Dòng này cho biết rằng trang mẫu hiện tại được mở rộng từ một mẫu cơ sở gọi là “base.html”. Điều này có nghĩa là trang mẫu hiện tại sẽ sử dụng giao diện cơ bản được định nghĩa trong “base.html” và thêm nội dung cụ thể của nó.
  • {% block title %} Forgot your password {% endblock %}: Dòng này xác định một khối có tên là “title” trong trang mẫu hiện tại và đặt nội dung của khối này là “Forgot your password”. Khối “title” này sẽ được đặt vào thẻ <title> trong phần đầu của trang web khi trang này được hiển thị.
  • {% block content %}: Dòng này mở khối có tên “content”, cho phép bạn đặt nội dung chính của trang mẫu hiện tại.
  • <content>: Nội dung này sẽ thay thế khối “content” khi trang mẫu này được hiển thị. Tuy nhiên, bạn cần cung cấp nội dung thực sự mà bạn muốn hiển thị ở đây.
  • {% endblock %}: Dòng này đóng khối “content” hiện tại.
{% extends "base.html" %}
{% block title %} Forgot your password {% endblock %}
{% block content %}

<nội dung của content>

{% block content %}
{% endblock %}

Nhớ rằng để hoàn thành trang mẫu này, bạn cần thêm nội dung thực sự vào trong khối “content”. Dòng <content> là chỉ là ví dụ và cần được thay thế bằng HTML và nội dung cụ thể bạn muốn hiển thị trên trang “Forgot your password”. Nội dung của content chính là phần mà mình đã xoá bỏ trong block section ở file index.html gốc.

Tiếp theo mình thêm đoạn messages này để đưa ra cảnh báo khi người dùng thao tác sai.

{% with messages = get_flashed_messages() %}
{% if messages %}
    {% for m in messages %}
        <p style="color: red;">{{m}}</p>
    {% endfor %}
{% endif %}
{% endwith %}

Và dưới đây là nội dung đầy đủ của file này.

{% extends "base.html" %}
{% block title %} Forgot your password {% endblock %}

{% block content %}
    <div class="form-box">
        <div class="form-value">
            <form action="" method="post">
                <h2>Forgot your password</h2>
                <div class="inputbox">
                    <ion-icon name="person-outline"></ion-icon>
                    <input type="text" id="username" name="username" required>
                    <label for="">Username</label>
                </div>
                <div class="inputbox">
                    <ion-icon name="mail-outline"></ion-icon>
                    <input name="email" type="email" id="email" required>
                    <label for="">Your email</label>
                </div>
                <div class="inputbox">
                    <ion-icon name="lock-closed-outline"></ion-icon>
                    <input name="new_password" id="new_password" type="password"  required>
                    <label for="">New password</label>
                    {% with messages = get_flashed_messages() %}
                        {% if messages %}
                            {% for m in messages %}
                                <p style="color: red;">{{m}}</p>
                            {% endfor %}
                        {% endif %}
                    {% endwith %}
                </div>
                <div class="inputbox">
                    <ion-icon name="lock-closed-outline"></ion-icon>
                    <input name="confirm_new_password" id="confirm_new_password" type="password" required>
                    <label for="">Confirm new password</label>
                    {% with messages = get_flashed_messages() %}
                        {% if messages %}
                            {% for m in messages %}
                                <p style="color: red;">{{m}}</p>
                            {% endfor %}
                        {% endif %}
                    {% endwith %}
                </div>
                <div class="forget">
                    <label for=""><input type="checkbox">Remember Me  <a href="#">Forget Password</a></label>
                </div>
                <button type="submit">Change password</button>
                <div class="register">
                    <p><a href="/register">Don't have a account Register</a></p>
                </div>
            </form>
        </div>
    </div>
{% endblock %}

4.4. File home.html.

File này đơn giản mình chỉ dựa vào file base.html để tạo một nội dung là Hello World khi người dùng đăng nhập thành công và kèm theo 1 nút Logout khi muốn thoát người dùng ra khỏi hệ thống.

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

{% block content %}
<h1>Hello World</h1>
<form action="" method="post">
    <button type="submit">Logout</button>
</form>
{% endblock %}

4.5. File login.html.

Tương tự như giải thích ở file forgot_password.html, file này dùng để làm chức năng login.

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

{% block content %}
    <div class="form-box">
        <div class="form-value">
            <form action="" method="post">
                <h2>Login</h2>
                <div class="inputbox">
                    <ion-icon name="person-outline"></ion-icon>
                    <input name="username" type="username" id="username" required>
                    <label for="">Username</label>
                </div>
                {% with messages = get_flashed_messages() %}
                    {% if messages %}
                        {% for m in messages %}
                            <p style="color: red;">{{m}}</p>
                        {% endfor %}
                    {% endif %}
                {% endwith %}
                <div class="inputbox">
                    <ion-icon name="lock-closed-outline"></ion-icon>
                    <input name="password" type="password" id="password" required>
                    <label for="">Password</label>
                </div>
                <div class="forget">
                    <label for=""><input type="checkbox">Remember me forget password<a href="#"></a></label>
                </div>
                <button>Log in</button>
                <div class="register">
                    <p><a href="/register">Don't have a account register</a></p>
                </div>
                <div class="register">
                    <p><a href="/forgot_password">Or</a></p>
                </div>
                <div class="register">
                    <p><a href="/forgot_password">Forgot your password</a></p>
                </div>
            </form>
        </div>
    </div>
{% endblock %}

4.6. File register.html.

Tương tự như giải thích ở file forgot_password.html, file này dùng để làm chức năng đăng ký người dùng.

{% extends "base.html" %}
{% block title %} Register Username {% endblock %}

{% block content %}
    <div class="form-box">
        <div class="form-value">
            <form action="" method="post">
                <h2>Create new account</h2>
                <div class="inputbox">
                    <ion-icon name="person-outline"></ion-icon>
                    <input name="username" type="username" id="username" required>
                    <label for="">Username</label>
                    {% with messages = get_flashed_messages() %}
                        {% if messages %}
                            {% for m in messages %}
                                <p style="color: red;">{{m}}</p>
                            {% endfor %}
                        {% endif %}
                    {% endwith %}
                </div>
                <div class="inputbox">
                    <ion-icon name="lock-closed-outline"></ion-icon>
                    <input name="password" type="password" id="password" required>
                    <label for="">Password</label>
                </div>
                <div class="inputbox">
                    <ion-icon name="lock-closed-outline"></ion-icon>
                    <input name="confirm_password" id="confirm_password" type="password" required>
                    <label for="">Confirm password</label>
                </div>
                <div class="inputbox">
                    <ion-icon name="mail-outline"></ion-icon>
                    <input name="email" type="email" id="email" required>
                    <label for="">Email</label>
                    {% with messages = get_flashed_messages() %}
                        {% if messages %}
                            {% for m in messages %}
                                <p style="color: red;">{{m}}</p>
                            {% endfor %}
                        {% endif %}
                    {% endwith %}
                </div>
                <div class="forget">
                    <label for=""><input type="checkbox">I agree to the terms you have provided<a href="#"></a></label>
                </div>
                <button type="submit">Register</button>
            </form>
        </div>
    </div>
{% endblock %}

5. Cài đặt Server Database.

Lưu ý rằng mình đang sử dụng cơ sở dữ liệu MySQL để lưu trữ thông tin người dùng, vì vậy bạn cần đảm bảo đã cài đặt MySQL và đã tạo cơ sở dữ liệu “usermanager” trước khi chạy ứng dụng.

Mình sử dụng MariaDB 10.06 cho lần demo này, nếu bạn dùng Ubuntu có thể sử dụng Script sau để cài đặt nhanh nó.

#!/bin/bash
dbuser='hoanghd'
dbname='usermanager'
dbpasswd='Hoanghd164'

sudo apt update

sudo apt install software-properties-common -y
sudo curl -LsS -O https://downloads.mariadb.com/MariaDB/mariadb_repo_setup
sudo bash mariadb_repo_setup --mariadb-server-version=10.6
sudo apt update
sudo apt -y install mariadb-common mariadb-client-10.6 mariadb-server-10.6 

sudo systemctl start mariadb
sudo systemctl enable mariadb

sudo mysql -u root -e  "ALTER USER 'root'@'localhost' IDENTIFIED BY '$dbpasswd';"
sudo mysql -u root -p$dbpasswd -e "create database $dbname character set utf8mb4 collate utf8mb4_bin;"
sudo mysql -u root -p$dbpasswd -e "grant all privileges on $dbname.* to $dbuser@'%' identified by '$dbpasswd';"
sudo mysql -u root -p$dbpasswd -e "FLUSH PRIVILEGES;"

sudo sed -i 's|bind-address.*|bind-address = 0.0.0.0|' /etc/mysql/mariadb.conf.d/50-server.cnf
sudo systemctl restart mariadb

Bạn có thể chạy thêm script này để kích hoạt tài khoản root được phép login ở các máy remote.

#!/bin/bash
dbuser='hoanghd'
dbname='usermanager'
dbpasswd='Hoanghd164'

sudo mysql -u root -p$dbpasswd -e "CREATE USER 'root'@'%' IDENTIFIED BY '$dbpasswd';"
sudo mysql -u root -p$dbpasswd -e "GRANT ALL PRIVILEGES ON *.* TO 'root'@'%';"
sudo mysql -u root -p$dbpasswd -e "FLUSH PRIVILEGES;"

Và đây là kết quả khi mình sử dụng Navicat để login vào kiểm tra tình trạng DB.

4. Xây dụng chức năng cho form đăng ký tài khoản.

Mình khởi tạo 1 file mới ở thư mục gốc với tên usermanager.py và sau khi tạo xong file này, cấu trúc file và thư mục của mình sẽ như sau:

../usermanager/
├── static
│   ├── images
│   │   └── f_login_background.jpg
│   └── styles
│       └── f_login.css
├── templates
│   ├── base.html
│   ├── forgot_password.html
│   ├── home.html
│   ├── login.html
│   └── register.html
└── usermanager.py

Dưới đây là nội dung đầy đủ của file này.

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
from flask_bcrypt import Bcrypt

Usermanager = Flask(__name__)
Usermanager.config["SECRET_KEY"] = "hoanghd-secret-key"
Usermanager.config["SQLALCHEMY_DATABASE_URI"] = "mysql://hoanghd:Hoanghd164@192.168.13.200/usermanager"
Usermanager.permanent_session_lifetime = timedelta(minutes=1)

db_conn = SQLAlchemy(Usermanager)
bcrypt = Bcrypt(Usermanager)

class usermanager(db_conn.Model):
    id = db_conn.Column(db_conn.Integer, primary_key=True)
    username = db_conn.Column(db_conn.String(255))
    password = db_conn.Column(db_conn.String(255))
    email = db_conn.Column(db_conn.String(255))

    def __init__(self, username, password, email):
        self.username = username
        self.password = bcrypt.generate_password_hash(password).decode('utf-8')
        self.email = email

@Usermanager.route('/', methods=["POST", "GET"])
@Usermanager.route('/home', methods=["POST", "GET"])
def home_page():
    if "session_username" not in session:
        return redirect(url_for("username_login")) 
    
    if request.method == "POST":
        return redirect(url_for("username_logout"))
    
    return render_template("home.html")

@Usermanager.route('/login', methods=["POST", "GET"])
def username_login():
    with Usermanager.app_context():
        db_conn.create_all()

    if request.method == "POST":
        session.permanent = True
        form_username = request.form["username"]
        form_password = request.form["password"]
        if form_username:
            found_user = usermanager.query.filter_by(username=form_username).first()
            session["session_username"] = form_username
            if found_user:
                is_password = bcrypt.check_password_hash(found_user.password, form_password)
            if found_user and is_password:
                username_in_db = found_user.username
                session["session_username"] = username_in_db
                if is_password:
                    flash("Username login successfully")
                    return redirect(url_for("home_page"))
                else:
                    flash("Password is incorrect")
                    return redirect(url_for("username_login"))
            else:
                flash("Username %s does not exist!" %(session["session_username"]))
                return redirect(url_for("username_login"))
        else:
            if "session_username" in session:
                return redirect(url_for("home_page"))
            else:
                return redirect(url_for("username_login"))
        
    return render_template("login.html")

@Usermanager.route('/register', methods=["POST", "GET"])
def username_register():
    with Usermanager.app_context():
        db_conn.create_all()

    if request.method == "POST":
        session.permanent = True
        form_username = request.form["username"]
        form_password = request.form["password"]
        form_confirm_password = request.form["confirm_password"]
        form_email = request.form["email"]
        session.clear()

        if form_username:
            found_user = usermanager.query.filter_by(username=form_username).first()
            found_email = usermanager.query.filter_by(email=form_email).first()

            if found_user:
                username_in_db = found_user.username
                if form_username == username_in_db:
                    flash("User already exists!!!")
                    return redirect(url_for("username_register"))
            else:
                if found_email:
                    flash("Email already exists!!!")
                    return redirect(url_for("username_register"))
                else:
                    if form_password == form_confirm_password:
                        user_info = usermanager(form_username, form_password, form_email)
                        db_conn.session.add(user_info)
                        db_conn.session.commit()
                        flash("Username created successfully!!!")
                        return redirect(url_for("username_login"))
                    else:
                        flash("Password must be the same!")
                        return redirect(url_for("username_register"))

        if "session_username" in session:
            return redirect(url_for("home_page"))
        
    return render_template("register.html")

@Usermanager.route('/forgot_password', methods=["POST", "GET"])
def forgot_password():
    with Usermanager.app_context():
        db_conn.create_all()

    if request.method == "POST":
        session.permanent = True
        form_username = request.form["username"]
        form_new_password = request.form["new_password"]
        form_confirm_new_password = request.form["confirm_new_password"]
        form_email = request.form["email"]
        session.clear()
        
        if form_username:
            found_user = usermanager.query.filter_by(username=form_username).first()
            found_email = usermanager.query.filter_by(email=form_email).first()
            if found_user and found_email:
                if form_new_password == form_confirm_new_password:
                    found_user = usermanager.query.filter_by(username=form_username).first()
                    found_user.password = bcrypt.generate_password_hash(form_new_password).decode('utf-8')
                    db_conn.session.commit()
                    flash("Change password successfully!!!")
                    return redirect(url_for("username_login"))
                else:
                    flash("Password must be the same!!!")
                    return redirect(url_for("forgot_password"))
            else:
                if found_user:
                    email_in_db = found_user.email
                    if form_email != email_in_db:
                        flash("Username and email do not match on the system!!")
                        return redirect(url_for("forgot_password"))
                else:
                    flash("Username %s do not exist on the system!!" %(form_username))
                    return redirect(url_for("forgot_password"))
                
    return render_template("forgot_password.html")

@Usermanager.route('/logout')
def username_logout():
    session.clear()
    return redirect(url_for("username_login"))

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

Đoạn code trên sử dụng Flask để quản lý đăng nhập, đăng ký tài khoản và quên mật khẩu. Dưới đây là chi tiết của code:

  • Import các thư viện và module: Đầu tiên, bạn import các thư viện và module cần thiết cho ứng dụng Flask của bạn, bao gồm Flask, redirect, url_for, render_template, request, session, timedelta, flash, SQLAlchemy (để làm việc với cơ sở dữ liệu), và Bcrypt (để mã hóa và kiểm tra mật khẩu).
  • Khởi tạo ứng dụng Flask: Bạn tạo một ứng dụng Flask và cấu hình nó bằng cách đặt các thuộc tính như “SECRET_KEY” (để bảo mật các phiên làm việc của người dùng), “SQLALCHEMY_DATABASE_URI” (để chỉ định đường dẫn đến cơ sở dữ liệu MySQL), và “permanent_session_lifetime” (để đặt thời gian tồn tại của phiên làm việc của người dùng).
  • Khởi tạo đối tượng SQLAlchemy và Bcrypt: Bạn khởi tạo đối tượng SQLAlchemy và Bcrypt bằng cách truyền ứng dụng Flask của bạn vào chúng.
  • Định nghĩa một lớp “usermanager” để làm việc với cơ sở dữ liệu: Bạn định nghĩa một lớp Python có tên “usermanager” kế thừa từ lớp Model của SQLAlchemy. Lớp này biểu diễn một bảng trong cơ sở dữ liệu với các cột như “id,” “username,” “password,” và “email.” Bạn cũng định nghĩa một phương thức __init__ để khởi tạo các thuộc tính của đối tượng.
  • Định nghĩa các route và xử lý yêu cầu HTTP:
    • //home: Trang chủ của ứng dụng. Nếu không có phiên làm việc nào tồn tại, người dùng sẽ được chuyển hướng đến trang đăng nhập. Nếu có yêu cầu POST, người dùng sẽ được chuyển hướng đến trang đăng xuất.
    • /login: Trang đăng nhập. Bạn kiểm tra yêu cầu POST để xác thực thông tin đăng nhập và đăng nhập người dùng nếu thông tin hợp lệ.
    • /register: Trang đăng ký tài khoản. Bạn kiểm tra yêu cầu POST để đảm bảo thông tin đăng ký hợp lệ và tạo tài khoản mới.
    • /forgot_password: Trang quên mật khẩu. Bạn kiểm tra yêu cầu POST để đảm bảo thông tin đúng và cập nhật mật khẩu nếu mọi thứ hợp lệ.
    • /logout: Đăng xuất người dùng và xóa phiên làm việc của họ.
  • Chạy ứng dụng Flask: Cuối cùng, bạn chạy ứng dụng Flask trên máy chủ cục bộ với tùy chọn debug=True để phát triển và kiểm tra ứng dụng.Ứng dụng sẽ lắng nghe trên cổng 5000 và có thể được truy cập tại địa chỉ http://0.0.0.0:5000.

Tham khảo full code tại đây.

5. Chạy ứng dụng.

Tại môi trường ảo hãy chạy ứng dụng bằng lệnh python <file ứng dụng> bạn sẽ có kết quả như dưới.

(.venv) root@flask-13-200:/home/usermanager# python usermanager.py 
 * Serving Flask app 'usermanager'
 * Debug mode: on
 * Running on all addresses (0.0.0.0)
 * 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: 110-259-104

6. Kiểm tra kết quả.

Dùng trình duyệt login vào http://192.168.13.200:5000 theo thông tin ở trên thì ngay lập tức website của bạn sẽ trỏ về /login vì trong phần khai báo route mình đã khai báo nếu không có tài khoản login thì trả về trang login.

Và lúc này nếu bạn vào DB kiểm tra thì bạn sẽ thấy đã có 1 bảng được tạo để lưu thông tin user như đã khai báo ở phần Model.

Và tất nhiên rồi, bảng này hiện đang trống.

  • Chức năng đăng ký.

Lúc này bạn sẽ có thông tin đầu tiên.

  • Chức năng login.

Bạn phải login để có thể xem được nội dung trang /homeHello World.

  • Chức năng logout.

Sau khi bạn logout thì nếu bạn cố gắng vào /home thì bạn sẽ bị đẩy về trang login để ép bạn phải login.

  • Chức năng khôi phục mật khẩu.

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