Hiển thị các bài đăng có nhãn Thực hành làm Web -Python. Hiển thị tất cả bài đăng
Hiển thị các bài đăng có nhãn Thực hành làm Web -Python. Hiển thị tất cả bài đăng

Thứ Ba, 4 tháng 11, 2025

Bài 8: Full Flask App - Website Quản lý học sinh

Bài 8: Full Flask App - Website Quản lý học sinh

Bài học này sẽ hướng dẫn học sinh THPT tạo một Website quản lý học sinh hoàn chỉnh sử dụng Flask, SQLite, Bootstrap, Chart.jsFlask-Login. Chúng ta sẽ tích hợp tất cả các bài trước thành một ứng dụng duy nhất.

1. Mục tiêu bài học

  • Hiểu cách tích hợp đăng nhập và phân quyền với Flask-Login.
  • Thực hiện đầy đủ các thao tác CRUD trên dữ liệu học sinh.
  • Upload hình ảnh học sinh và hiển thị trên website.
  • Thống kê điểm trung bình bằng biểu đồ Chart.js.
  • Thiết kế giao diện responsive với Bootstrap.

2. Cấu trúc dự án

Chúng ta sẽ tạo cấu trúc dự án Flask như sau:


FlaskStudentApp/
│-- app.py
│-- students.db
│-- templates/
│   │-- base.html
│   │-- login.html
│   │-- dashboard.html
│   │-- student_list.html
│   │-- student_form.html
│   │-- chart.html
│-- static/
    │-- css/
    │-- images/

3. Cài đặt Flask và các thư viện cần thiết


pip install Flask Flask-Login Flask-WTF WTForms

4. Mã nguồn chính - app.py

Giải thích: Đây là file Flask chính, quản lý routing, database, và đăng nhập.


from flask import Flask, render_template, redirect, url_for, request, flash
from flask_sqlalchemy import SQLAlchemy
from flask_login import LoginManager, login_user, login_required, logout_user, UserMixin, current_user
from werkzeug.security import generate_password_hash, check_password_hash
import os

app = Flask(__name__)
app.config['SECRET_KEY'] = 'phanchutrinh'
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///students.db'
app.config['UPLOAD_FOLDER'] = 'static/images'
db = SQLAlchemy(app)
login_manager = LoginManager()
login_manager.init_app(app)
login_manager.login_view = 'login'

# User model
class User(UserMixin, db.Model):
    id = db.Column(db.Integer, primary_key=True)
    username = db.Column(db.String(150), unique=True)
    password = db.Column(db.String(150))
    role = db.Column(db.String(50))  # admin or teacher

# Student model
class Student(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(100))
    age = db.Column(db.Integer)
    grade = db.Column(db.Float)
    photo = db.Column(db.String(100))

@login_manager.user_loader
def load_user(user_id):
    return User.query.get(int(user_id))

@app.route('/login', methods=['GET','POST'])
def login():
    if request.method == 'POST':
        username = request.form['username']
        password = request.form['password']
        user = User.query.filter_by(username=username).first()
        if user and check_password_hash(user.password, password):
            login_user(user)
            return redirect(url_for('dashboard'))
        flash('Sai tên đăng nhập hoặc mật khẩu!')
    return render_template('login.html')

@app.route('/logout')
@login_required
def logout():
    logout_user()
    return redirect(url_for('login'))

@app.route('/')
@login_required
def dashboard():
    students = Student.query.all()
    return render_template('dashboard.html', students=students)

# CRUD student
@app.route('/student/add', methods=['GET','POST'])
@login_required
def add_student():
    if request.method == 'POST':
        name = request.form['name']
        age = request.form['age']
        grade = request.form['grade']
        photo_file = request.files['photo']
        photo_path = None
        if photo_file:
            photo_path = os.path.join(app.config['UPLOAD_FOLDER'], photo_file.filename)
            photo_file.save(photo_path)
        student = Student(name=name, age=age, grade=grade, photo=photo_file.filename if photo_file else None)
        db.session.add(student)
        db.session.commit()
        flash('Thêm học sinh thành công!')
        return redirect(url_for('dashboard'))
    return render_template('student_form.html', action="Add")

@app.route('/student/edit/', methods=['GET','POST'])
@login_required
def edit_student(id):
    student = Student.query.get_or_404(id)
    if request.method == 'POST':
        student.name = request.form['name']
        student.age = request.form['age']
        student.grade = request.form['grade']
        photo_file = request.files['photo']
        if photo_file:
            student.photo = photo_file.filename
            photo_file.save(os.path.join(app.config['UPLOAD_FOLDER'], photo_file.filename))
        db.session.commit()
        flash('Cập nhật học sinh thành công!')
        return redirect(url_for('dashboard'))
    return render_template('student_form.html', action="Edit", student=student)

@app.route('/student/delete/')
@login_required
def delete_student(id):
    student = Student.query.get_or_404(id)
    db.session.delete(student)
    db.session.commit()
    flash('Xóa học sinh thành công!')
    return redirect(url_for('dashboard'))

# Chart route
@app.route('/chart')
@login_required
def chart():
    students = Student.query.all()
    names = [s.name for s in students]
    grades = [s.grade for s in students]
    return render_template('chart.html', names=names, grades=grades)

if __name__ == '__main__':
    db.create_all()
    # Tạo user mặc định admin nếu chưa có
    if not User.query.filter_by(username='admin').first():
        admin = User(username='admin', password=generate_password_hash('123456', method='sha256'), role='admin')
        db.session.add(admin)
        db.session.commit()
    app.run(debug=True)

5. Templates chính

Ví dụ: login.html





    
    
    Login


Đăng nhập

Tương tự, bạn có thể tạo dashboard.html, student_form.html, chart.html tích hợp Bootstrap & Chart.js để hiển thị dữ liệu và thống kê điểm.

6. Hướng dẫn chạy dự án

  1. Tạo thư mục dự án và các subfolder templates, static/images.
  2. Lưu file app.py và các template vào đúng thư mục.
  3. Chạy lệnh python app.py và mở trình duyệt http://127.0.0.1:5000/.
  4. Đăng nhập với username admin và password 123456.

7. Kết luận

Qua bài học này, học sinh THPT sẽ hiểu cách tạo một **ứng dụng web quản lý học sinh hoàn chỉnh** với Flask, học cách tích hợp **CRUD, đăng nhập, upload hình ảnh, thống kê**, và thiết kế giao diện responsive bằng Bootstrap. Đây là nền tảng để tham gia **hội giảng chuyên đề STEM hoặc các cuộc thi Tin học ứng dụng**.

Bài 7: Flask + Upload hình ảnh học sinh + Thống kê nâng cao

🖼️ BÀI 7: UPLOAD ẢNH HỌC SINH + THỐNG KÊ NÂNG CAO

Thực hành Flask – Lưu hình ảnh và hiển thị thống kê nâng cao


🎯 Mục tiêu

  • Biết cách upload file ảnh trong Flask.
  • Lưu đường dẫn ảnh vào cơ sở dữ liệu.
  • Hiển thị hình ảnh học sinh và điểm số trên trang thống kê.
Flask upload demo

⚙️ 1. Cấu trúc dự án


flask_upload/
 ├─ app.py
 ├─ static/uploads/
 ├─ students.db
 └─ templates/
      ├─ upload.html
      ├─ gallery.html

💻 2. File app.py


from flask import Flask, render_template, request, redirect, url_for
import sqlite3, os
from werkzeug.utils import secure_filename

app = Flask(__name__)
app.config['UPLOAD_FOLDER'] = 'static/uploads'
app.secret_key = 'uploadkey'

# Tạo bảng
def init_db():
    conn = sqlite3.connect('students.db')
    c = conn.cursor()
    c.execute('''CREATE TABLE IF NOT EXISTS students (
                    id INTEGER PRIMARY KEY AUTOINCREMENT,
                    hoten TEXT,
                    diem REAL,
                    image TEXT
                )''')
    conn.commit()
    conn.close()

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

@app.route('/upload', methods=['POST'])
def upload_file():
    hoten = request.form['hoten']
    diem = request.form['diem']
    file = request.files['image']
    if file:
        filename = secure_filename(file.filename)
        path = os.path.join(app.config['UPLOAD_FOLDER'], filename)
        file.save(path)
        conn = sqlite3.connect('students.db')
        c = conn.cursor()
        c.execute("INSERT INTO students (hoten, diem, image) VALUES (?, ?, ?)", (hoten, diem, filename))
        conn.commit()
        conn.close()
    return redirect('/gallery')

@app.route('/gallery')
def gallery():
    conn = sqlite3.connect('students.db')
    c = conn.cursor()
    c.execute("SELECT hoten, diem, image FROM students")
    data = c.fetchall()
    conn.close()
    return render_template('gallery.html', students=data)

if __name__ == '__main__':
    os.makedirs('static/uploads', exist_ok=True)
    init_db()
    app.run(debug=True)

📋 3. File templates/upload.html


<!DOCTYPE html>
<html lang="vi">
<head>
<meta charset="UTF-8">
<title>Upload ảnh học sinh</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
</head>
<body class="bg-light d-flex justify-content-center align-items-center vh-100">
<form action="/upload" method="POST" enctype="multipart/form-data" class="p-4 bg-white shadow rounded" style="width:350px;">
  <h3 class="text-primary text-center mb-3">🖼️ Upload ảnh học sinh</h3>
  <input class="form-control mb-2" type="text" name="hoten" placeholder="Họ và tên" required>
  <input class="form-control mb-2" type="number" step="0.1" name="diem" placeholder="Điểm TB" required>
  <input class="form-control mb-3" type="file" name="image" accept="image/*" required>
  <button class="btn btn-success w-100">Tải lên</button>
  <a href="/gallery" class="d-block text-center mt-2">📸 Xem thư viện</a>
</form>
</body>
</html>

📷 4. File templates/gallery.html


<!DOCTYPE html>
<html lang="vi">
<head>
<meta charset="UTF-8">
<title>Thư viện học sinh</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
</head>
<body class="bg-light">
<div class="container mt-5">
  <h2 class="text-primary text-center mb-4">📸 Thư viện học sinh</h2>
  <div class="row">
    {% for hs in students %}
      <div class="col-md-3 mb-4">
        <div class="card shadow">
          <img src="{{ url_for('static', filename='uploads/' + hs[2]) }}" class="card-img-top" alt="{{ hs[0] }}">
          <div class="card-body text-center">
            <h5>{{ hs[0] }}</h5>
            <p>Điểm: {{ hs[1] }}</p>
          </div>
        </div>
      </div>
    {% endfor %}
  </div>
</div>
</body>
</html>

📈 5. Mở rộng nâng cao

  • Tính điểm TB toàn trường, hiển thị biểu đồ (Chart.js).
  • Tô màu khác nhau cho học sinh Giỏi / Khá / TB.
  • Cho phép upload nhiều ảnh cùng lúc.

© 2025 Trường THPT Phan Chu Trinh – Bài 7: Flask Upload + Thống kê nâng cao

Bài 6: Flask + Đăng nhập / Phân quyền người dùng

🔐 BÀI 6: FLASK + ĐĂNG NHẬP & PHÂN QUYỀN NGƯỜI DÙNG

Ứng dụng Flask-Login cơ bản cho trường THPT Phan Chu Trinh


🎯 Mục tiêu bài học

  • Hiểu khái niệm xác thực (login) và phân quyền người dùng.
  • Biết cách sử dụng Flask-Login để quản lý đăng nhập.
  • Tạo tài khoản quản trị (admin) và người dùng (giáo viên).
Flask login illustration

🧩 1. Cài đặt thư viện


pip install flask flask-login

⚙️ 2. Cấu trúc dự án


flask_login/
 ├─ app.py
 ├─ users.db
 └─ templates/
      ├─ login.html
      ├─ dashboard.html
      └─ unauthorized.html

💻 3. File app.py


from flask import Flask, render_template, redirect, url_for, request
from flask_login import LoginManager, UserMixin, login_user, logout_user, login_required, current_user
import sqlite3

app = Flask(__name__)
app.secret_key = 'secret123'

login_manager = LoginManager()
login_manager.init_app(app)
login_manager.login_view = 'login'

# --- Tạo DB người dùng ---
def init_db():
    conn = sqlite3.connect('users.db')
    c = conn.cursor()
    c.execute('''CREATE TABLE IF NOT EXISTS users (
        id INTEGER PRIMARY KEY AUTOINCREMENT,
        username TEXT,
        password TEXT,
        role TEXT
    )''')
    c.execute("INSERT OR IGNORE INTO users (id, username, password, role) VALUES (1,'admin','123','admin')")
    c.execute("INSERT OR IGNORE INTO users (id, username, password, role) VALUES (2,'gv1','123','teacher')")
    conn.commit()
    conn.close()

# --- Flask-Login ---
class User(UserMixin):
    def __init__(self, id, username, role):
        self.id = id
        self.username = username
        self.role = role

@login_manager.user_loader
def load_user(user_id):
    conn = sqlite3.connect('users.db')
    c = conn.cursor()
    c.execute("SELECT id, username, role FROM users WHERE id=?", (user_id,))
    data = c.fetchone()
    conn.close()
    if data:
        return User(data[0], data[1], data[2])
    return None

@app.route('/')
def home():
    return redirect('/login')

@app.route('/login', methods=['GET','POST'])
def login():
    if request.method == 'POST':
        username = request.form['username']
        password = request.form['password']
        conn = sqlite3.connect('users.db')
        c = conn.cursor()
        c.execute("SELECT id, username, password, role FROM users WHERE username=?", (username,))
        data = c.fetchone()
        conn.close()
        if data and data[2] == password:
            user = User(data[0], data[1], data[3])
            login_user(user)
            return redirect('/dashboard')
        else:
            return "Sai tài khoản hoặc mật khẩu!"
    return render_template('login.html')

@app.route('/dashboard')
@login_required
def dashboard():
    if current_user.role == 'admin':
        message = "Bạn đang đăng nhập với quyền QUẢN TRỊ"
    else:
        message = "Bạn là GIÁO VIÊN"
    return render_template('dashboard.html', user=current_user, message=message)

@app.route('/logout')
@login_required
def logout():
    logout_user()
    return redirect('/login')

if __name__ == '__main__':
    init_db()
    app.run(debug=True)

📋 4. File templates/login.html


<!DOCTYPE html>
<html lang="vi">
<head>
<meta charset="UTF-8">
<title>Đăng nhập</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
</head>
<body class="bg-light d-flex align-items-center justify-content-center vh-100">
<form method="POST" class="p-4 bg-white shadow rounded" style="width:320px;">
  <h3 class="text-center text-primary mb-3">🔐 Đăng nhập</h3>
  <input type="text" name="username" class="form-control mb-3" placeholder="Tên đăng nhập" required>
  <input type="password" name="password" class="form-control mb-3" placeholder="Mật khẩu" required>
  <button class="btn btn-primary w-100">Đăng nhập</button>
</form>
</body>
</html>

📊 5. File templates/dashboard.html


<!DOCTYPE html>
<html lang="vi">
<head>
<meta charset="UTF-8">
<title>Trang quản lý</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
</head>
<body class="bg-light">
<div class="container mt-5 text-center">
  <h2>Xin chào, {{ user.username }} 👋</h2>
  <p>{{ message }}</p>
  <a href="/logout" class="btn btn-danger mt-3">Đăng xuất</a>
</div>
</body>
</html>

🧠 Thử thách

  • Thêm chức năng “Đăng ký tài khoản mới”.
  • Chỉ Admin mới được xem danh sách giáo viên.
  • Giáo viên chỉ được truy cập trang riêng của mình.

© 2025 Trường THPT Phan Chu Trinh – Bài 6: Flask Login & Role System

Bài 5: Flask + Bootstrap + Chart.js - Thống kê học sinh

📊 BÀI 5: FLASK + BOOTSTRAP + BIỂU ĐỒ (CHART.JS)

Ứng dụng Flask hiển thị thống kê điểm trung bình học sinh


🎯 Mục tiêu bài học

  • Kết hợp Flask với Bootstrap để tạo giao diện hiện đại, thân thiện.
  • Dùng Chart.js để hiển thị biểu đồ thống kê điểm trung bình.
  • Tổng hợp dữ liệu từ SQLite để vẽ biểu đồ.
Flask Chart.js demo

🧩 1. Cấu trúc thư mục dự án


flask_chart/
 ├─ app.py
 ├─ students.db
 └─ templates/
      ├─ index.html
      └─ chart.html

⚙️ 2. File app.py


from flask import Flask, render_template
import sqlite3

app = Flask(__name__)

# --- Tạo bảng dữ liệu nếu chưa có ---
def init_db():
    conn = sqlite3.connect('students.db')
    c = conn.cursor()
    c.execute('''CREATE TABLE IF NOT EXISTS students (
                    id INTEGER PRIMARY KEY AUTOINCREMENT,
                    hoten TEXT,
                    lop TEXT,
                    diem REAL
                )''')
    conn.commit()
    conn.close()

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

@app.route('/chart')
def chart():
    conn = sqlite3.connect('students.db')
    c = conn.cursor()
    c.execute("SELECT lop, AVG(diem) FROM students GROUP BY lop")
    data = c.fetchall()
    conn.close()

    # Chuyển dữ liệu thành 2 danh sách: lớp và điểm trung bình
    labels = [row[0] for row in data]
    values = [round(row[1], 2) for row in data]

    return render_template('chart.html', labels=labels, values=values)

if __name__ == '__main__':
    init_db()
    app.run(debug=True)
💡 Giải thích: – Lấy dữ liệu điểm trung bình theo từng lớp bằng câu SQL GROUP BY. – Gửi dữ liệu sang chart.html để vẽ biểu đồ Chart.js.

📄 3. File templates/index.html – Trang chủ


<!DOCTYPE html>
<html lang="vi">
<head>
  <meta charset="UTF-8">
  <title>Trang chủ - Thống kê học sinh</title>
  <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
</head>
<body class="bg-light">

<div class="container mt-5 text-center">
  <h1 class="text-primary">📘 Thống kê học sinh với Flask</h1>
  <p>Dự án Flask cuối cùng - THPT Phan Chu Trinh</p>
  <a href="/chart" class="btn btn-success mt-3">Xem biểu đồ thống kê điểm TB</a>
</div>

</body>
</html>

📊 4. File templates/chart.html – Hiển thị biểu đồ


<!DOCTYPE html>
<html lang="vi">
<head>
  <meta charset="UTF-8">
  <title>Biểu đồ thống kê điểm trung bình</title>
  <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
  <script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
</head>

<body class="bg-white">
  <div class="container mt-5 mb-5">
    <h2 class="text-center text-primary">📊 Biểu đồ thống kê điểm trung bình theo lớp</h2>
    <canvas id="myChart" height="120"></canvas>

    <div class="text-center mt-4">
      <a href="/" class="btn btn-outline-secondary">⬅️ Quay lại trang chủ</a>
    </div>
  </div>

  <script>
  const ctx = document.getElementById('myChart');
  const chart = new Chart(ctx, {
    type: 'bar',
    data: {
      labels: {{ labels|tojson }},
      datasets: [{
        label: 'Điểm trung bình theo lớp',
        data: {{ values|tojson }},
        backgroundColor: [
          '#3498db','#1abc9c','#9b59b6','#f1c40f','#e67e22','#e74c3c'
        ]
      }]
    },
    options: {
      scales: {
        y: {
          beginAtZero: true,
          max: 10
        }
      }
    }
  });
  </script>
</body>
</html>

📈 5. Kết quả hiển thị

  • Biểu đồ cột hiển thị điểm trung bình của từng lớp.
  • Màu sắc tự động thay đổi theo lớp học.
  • Dễ dàng mở rộng thành biểu đồ tròn, đường, radar...
Chart.js Flask Demo

🧠 6. Thử thách nâng cao

  • Thêm biểu đồ tròn (Pie Chart) thống kê học lực: Giỏi – Khá – Trung bình.
  • Kết hợp Bootstrap Card hiển thị tổng số học sinh, điểm cao nhất.
  • Thêm filter chọn lớp để xem riêng một lớp.

📚 Kết luận

  • Học sinh đã kết hợp được 3 công nghệ: Flask + SQLite + Chart.js.
  • Biết cách trình bày giao diện web bằng Bootstrap.
  • Đây là nền tảng để phát triển ứng dụng web trực quan và sinh động hơn.

© 2025 Trường THPT Phan Chu Trinh – Gia Lai | Bài giảng Flask – Phần 5: Biểu đồ thống kê (Chart.js)

Bài 3: Flask + SQLite + Form lưu thông tin học sinh

📘 BÀI 3: FLASK + CƠ SỞ DỮ LIỆU SQLITE + FORM LƯU HỌC SINH


🎯 Mục tiêu bài học

  • Biết cách sử dụng SQLite trong Flask.
  • Tạo form nhập thông tin học sinh (họ tên, lớp, điểm trung bình).
  • Lưu dữ liệu vào cơ sở dữ liệu SQLite.
  • Hiển thị danh sách học sinh đã nhập.
Flask SQLite form illustration

🧩 1. Cấu trúc thư mục dự án


flask_sqlite/
 ├─ app.py
 ├─ students.db
 └─ templates/
      ├─ index.html
      └─ list.html

📂 File students.db là cơ sở dữ liệu SQLite. Flask sẽ tự tạo nếu chưa có.


⚙️ 2. File app.py


import sqlite3
from flask import Flask, render_template, request, redirect

app = Flask(__name__)

# --- Hàm khởi tạo database ---
def init_db():
    conn = sqlite3.connect('students.db')
    c = conn.cursor()
    c.execute('''CREATE TABLE IF NOT EXISTS students (
                    id INTEGER PRIMARY KEY AUTOINCREMENT,
                    hoten TEXT NOT NULL,
                    lop TEXT NOT NULL,
                    diem REAL
                )''')
    conn.commit()
    conn.close()

# --- Trang chủ: Form nhập thông tin ---
@app.route('/')
def index():
    return render_template('index.html')

# --- Xử lý lưu dữ liệu ---
@app.route('/add', methods=['POST'])
def add_student():
    hoten = request.form.get('hoten')
    lop = request.form.get('lop')
    diem = request.form.get('diem')

    conn = sqlite3.connect('students.db')
    c = conn.cursor()
    c.execute("INSERT INTO students (hoten, lop, diem) VALUES (?, ?, ?)",
              (hoten, lop, diem))
    conn.commit()
    conn.close()

    return redirect('/list')

# --- Trang hiển thị danh sách ---
@app.route('/list')
def list_students():
    conn = sqlite3.connect('students.db')
    c = conn.cursor()
    c.execute("SELECT * FROM students")
    data = c.fetchall()
    conn.close()
    return render_template('list.html', students=data)

if __name__ == '__main__':
    init_db()
    app.run(debug=True)
💡 Giải thích:
- Hàm init_db() tạo bảng “students” nếu chưa có.
- / là trang nhập thông tin học sinh.
- /add nhận dữ liệu POST và lưu vào database.
- /list hiển thị danh sách học sinh đã lưu.

🧾 3. File templates/index.html – Form nhập dữ liệu


<!DOCTYPE html>
<html lang="vi">
<head>
  <meta charset="UTF-8">
  <title>Nhập thông tin học sinh</title>
  <style>
    body {
      font-family: Arial;
      background: linear-gradient(120deg,#6dd5fa,#2980b9);
      display: flex;
      justify-content: center;
      align-items: center;
      height: 100vh;
    }
    .form-box {
      background: #fff;
      padding: 30px;
      border-radius: 8px;
      width: 400px;
      box-shadow: 0 0 10px rgba(0,0,0,0.2);
    }
    h2 {
      text-align: center;
      color: #2980b9;
    }
    label {
      display: block;
      margin-top: 10px;
      font-weight: bold;
    }
    input {
      width: 100%;
      padding: 8px;
      border: 1px solid #ccc;
      border-radius: 5px;
    }
    button {
      margin-top: 20px;
      background: #2980b9;
      color: #fff;
      border: none;
      padding: 10px;
      border-radius: 5px;
      width: 100%;
      cursor: pointer;
    }
    button:hover {
      background: #1a5f8a;
    }
  </style>
</head>
<body>
  <form class="form-box" action="/add" method="POST">
    <h2>🧾 Nhập thông tin học sinh</h2>
    <label>Họ và tên:</label>
    <input type="text" name="hoten" required>

    <label>Lớp:</label>
    <input type="text" name="lop" required>

    <label>Điểm trung bình:</label>
    <input type="number" name="diem" min="0" max="10" step="0.1" required>

    <button type="submit">Lưu học sinh</button>
  </form>
</body>
</html>

📋 4. File templates/list.html – Hiển thị danh sách học sinh


<!DOCTYPE html>
<html lang="vi">
<head>
  <meta charset="UTF-8">
  <title>Danh sách học sinh</title>
  <style>
    body {
      font-family: Arial;
      background: #f0f4f8;
      padding: 30px;
    }
    h2 {
      text-align: center;
      color: #2980b9;
    }
    table {
      width: 100%;
      border-collapse: collapse;
      margin-top: 20px;
    }
    th, td {
      border: 1px solid #ccc;
      padding: 10px;
      text-align: center;
    }
    th {
      background: #e9f2ff;
    }
    a {
      display: inline-block;
      margin-top: 20px;
      text-decoration: none;
      color: white;
      background: #2980b9;
      padding: 10px 20px;
      border-radius: 5px;
    }
  </style>
</head>
<body>
  <h2>📚 Danh sách học sinh</h2>

  <table>
    <tr>
      <th>Mã</th>
      <th>Họ và tên</th>
      <th>Lớp</th>
      <th>Điểm TB</th>
    </tr>
    {% for hs in students %}
      <tr>
        <td>{{ hs[0] }}</td>
        <td>{{ hs[1] }}</td>
        <td>{{ hs[2] }}</td>
        <td>{{ hs[3] }}</td>
      </tr>
    {% endfor %}
  </table>

  <div style="text-align:center;">
    <a href="/">➕ Thêm học sinh mới</a>
  </div>
</body>
</html>

🚀 5. Khi chạy Flask

Sau khi chạy python app.py và mở trình duyệt tại http://127.0.0.1:5000:

  • Trang đầu: nhập thông tin học sinh.
  • Khi bấm Lưu học sinh, dữ liệu được lưu vào SQLite.
  • Trang /list hiển thị bảng danh sách học sinh đã nhập.
Flask list demo

🏁 6. Thử thách mở rộng cho học sinh

  • Thêm cột “Email” vào form và cơ sở dữ liệu.
  • Thêm nút “Xóa” hoặc “Sửa” học sinh.
  • Trang “Thống kê” hiển thị học sinh có điểm > 8.

📚 Kết luận

  • Học sinh đã biết cách lưu và đọc dữ liệu thực bằng SQLite trong Flask.
  • Đây là nền tảng của lập trình web thực tế – dữ liệu, giao diện, xử lý backend.
  • Có thể mở rộng sang CRUD (Create, Read, Update, Delete).

© 2025 Trường THPT Phan Chu Trinh – Gia Lai | Bài giảng thực hành Flask – Phần 3

Bài 2: Flask + HTML Form nâng cao + CSS

🧠 BÀI 2: FLASK + HTML FORM NÂNG CAO + CSS


🎯 Mục tiêu bài học

  • Tạo form HTML có nhiều trường nhập liệu (tên, tuổi, sở thích...)
  • Dùng Flask để nhận và xử lý dữ liệu người dùng gửi lên
  • Trang trí giao diện bằng CSS cho đẹp mắt
  • Biết cách hiển thị kết quả trả về từ Python ra trang web
Form Flask illustration

📦 1. Chuẩn bị thư mục dự án


flask_form/
 ├─ app.py
 └─ templates/
      ├─ index.html
      └─ result.html
      └─ style.css

Chúng ta sẽ viết app.py điều khiển logic, còn HTML & CSS nằm trong thư mục templates.


⚙️ 2. File app.py


from flask import Flask, render_template, request

app = Flask(__name__)

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

@app.route('/ketqua', methods=['POST'])
def ketqua():
    ten = request.form.get('ten')
    tuoi = request.form.get('tuoi')
    sothich = request.form.getlist('sothich')

    return render_template('result.html', ten=ten, tuoi=tuoi, sothich=sothich)

if __name__ == '__main__':
    app.run(debug=True)
💡 Giải thích:
  • Trang / hiển thị form nhập liệu.
  • Trang /ketqua nhận dữ liệu POST, xử lý và gửi kết quả đến result.html.
  • request.form.getlist() dùng khi người dùng chọn nhiều mục (checkbox).

🧾 3. File templates/index.html

Tạo form nhập thông tin cá nhân, có giao diện đẹp nhờ CSS.


<!DOCTYPE html>
<html lang="vi">
<head>
  <meta charset="UTF-8">
  <title>Nhập thông tin của bạn</title>
  <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.0/css/all.min.css">
  <style>
    body {
      font-family: "Segoe UI", sans-serif;
      background: linear-gradient(120deg,#6dd5fa,#2980b9);
      display: flex;
      justify-content: center;
      align-items: center;
      height: 100vh;
      color: #333;
    }
    .form-container {
      background: #fff;
      padding: 30px 40px;
      border-radius: 10px;
      width: 400px;
      box-shadow: 0 0 10px rgba(0,0,0,0.2);
    }
    h2 {
      text-align: center;
      color: #2980b9;
    }
    label {
      display: block;
      margin-top: 10px;
      font-weight: bold;
    }
    input[type="text"], input[type="number"] {
      width: 100%;
      padding: 8px;
      border: 1px solid #ccc;
      border-radius: 5px;
    }
    .checkbox-group {
      margin-top: 5px;
    }
    button {
      margin-top: 20px;
      background: #2980b9;
      color: #fff;
      border: none;
      padding: 10px 20px;
      border-radius: 5px;
      cursor: pointer;
      width: 100%;
    }
    button:hover {
      background: #1a5f8a;
    }
  </style>
</head>
<body>
  <form class="form-container" action="/ketqua" method="POST">
    <h2>💬 Nhập thông tin cá nhân</h2>
    <label>Tên của bạn:</label>
    <input type="text" name="ten" required>

    <label>Tuổi:</label>
    <input type="number" name="tuoi" min="10" max="100" required>

    <label>Sở thích:</label>
    <div class="checkbox-group">
      <input type="checkbox" name="sothich" value="Âm nhạc"> Âm nhạc <br>
      <input type="checkbox" name="sothich" value="Thể thao"> Thể thao <br>
      <input type="checkbox" name="sothich" value="Lập trình"> Lập trình <br>
      <input type="checkbox" name="sothich" value="Đọc sách"> Đọc sách <br>
    </div>

    <button type="submit">Gửi thông tin</button>
  </form>
</body>
</html>

🎨 4. File templates/result.html


<!DOCTYPE html>
<html lang="vi">
<head>
  <meta charset="UTF-8">
  <title>Kết quả</title>
  <style>
    body {
      background: #f0f4f8;
      font-family: Arial;
      text-align: center;
      padding-top: 80px;
    }
    h2 {
      color: #2980b9;
    }
    ul {
      list-style: none;
      padding: 0;
    }
    a {
      display: inline-block;
      margin-top: 20px;
      text-decoration: none;
      color: white;
      background: #2980b9;
      padding: 10px 20px;
      border-radius: 5px;
    }
  </style>
</head>
<body>
  <h2>Xin chào, {{ ten }}! 👋</h2>
  <p>Bạn {{ tuoi }} tuổi.</p>

  <h3>Sở thích của bạn:</h3>
  <ul>
    {% for st in sothich %}
      <li>✅ {{ st }}</li>
    {% endfor %}
  </ul>

  <a href="/">Quay lại trang nhập</a>
</body>
</html>
🧠 Giải thích:
Flask tự động chèn dữ liệu từ Python vào vị trí {{ biến }} trong HTML.
Dòng {% for ... %} dùng để lặp danh sách sở thích.

🚀 5. Kết quả khi chạy

Khi bạn mở trình duyệt đến http://127.0.0.1:5000/, bạn sẽ thấy form như sau:

Flask form demo

Sau khi nhập tên, tuổi, chọn sở thích và nhấn Gửi, Flask hiển thị kết quả:

Flask result demo

🏁 6. Thử thách cho học sinh

  • Thêm trường “Mục tiêu nghề nghiệp” dạng <textarea> vào form.
  • Hiển thị kết quả bằng bảng có màu nền xen kẽ.
  • Làm nút “Làm lại” để xóa dữ liệu và quay lại trang form.

📘 Kết luận

  • Bạn đã biết cách xử lý form nâng cao bằng Flask.
  • Đã kết hợp HTML + CSS để web trông hấp dẫn hơn.
  • Đây là nền tảng để bước sang các dự án web lớn hơn (Flask + Database).

© 2025 Trường THPT Phan Chu Trinh – Gia Lai | Bài giảng thực hành: Flask cơ bản – Phần 2

Bài giảng: Thiết kế Web đơn giản bằng Python (Flask)

🧠 BÀI GIẢNG: LÀM WEB ĐƠN GIẢN VỚI PYTHON (FLASK)


🎯 Mục tiêu bài học

  • Hiểu Python có thể dùng để lập trình web như thế nào.
  • Biết cách cài đặt Flask – một framework web nhẹ.
  • Tạo trang web hiển thị nội dung và tương tác cơ bản.
  • Tự tạo trang web cá nhân nhỏ bằng Python.
Python Flask web illustration

🧩 1. Python và Web – Tại sao được?

Python có thể tạo web nhờ vào các framework như Flask. Flask giúp bạn:

  • Tạo máy chủ web mini chạy trên máy tính.
  • Trao đổi dữ liệu giữa người dùng và Python.
  • Hiển thị nội dung HTML ra trình duyệt.

⚙️ 2. Chuẩn bị môi trường

pip install flask

Bạn có thể cài trên máy, hoặc chạy trên Google Colab / Replit đều được.

🌐 3. Ví dụ 1: Trang web “Hello, World!” đầu tiên


from flask import Flask

app = Flask(__name__)

@app.route('/')
def home():
    return "<h1>Xin chào! Đây là trang web đầu tiên của tôi bằng Python Flask</h1>"

if __name__ == '__main__':
    app.run(debug=True)
💡 Giải thích:
- Flask(__name__): Tạo ứng dụng Flask.
- @app.route('/'): Định nghĩa đường dẫn trang chủ.
- app.run(debug=True): Chạy server web.
Sau khi chạy, mở http://127.0.0.1:5000/ để xem kết quả!

🖼️ 4. Ví dụ 2: Trang web có nhiều trang nhỏ


from flask import Flask

app = Flask(__name__)

@app.route('/')
def home():
    return "<h1>Trang chủ</h1><p>Chào mừng bạn đến với website học Python!</p>"

@app.route('/gioithieu')
def about():
    return "<h1>Giới thiệu</h1><p>Đây là trang giới thiệu về chúng tôi.</p>"

@app.route('/lienhe')
def contact():
    return "<h1>Liên hệ</h1><p>Email: pythonhoc@gmail.com</p>"

if __name__ == '__main__':
    app.run(debug=True)
🧭 Truy cập:
  • / → Trang chủ
  • /gioithieu → Trang giới thiệu
  • /lienhe → Trang liên hệ

📝 5. Ví dụ 3: Gửi dữ liệu từ trình duyệt đến Python


from flask import Flask, request

app = Flask(__name__)

@app.route('/')
def form():
    return '''
        <form action="/chao" method="post">
            Nhập tên của bạn: <input name="ten">
            <input type="submit" value="Gửi">
        </form>
    '''

@app.route('/chao', methods=['POST'])
def chao():
    ten = request.form.get('ten')
    return f"<h2>Xin chào, {ten}! Chúc bạn một ngày vui vẻ!</h2>"

if __name__ == '__main__':
    app.run(debug=True)

🧠 Đây là cách Flask nhận dữ liệu người dùng và trả lại kết quả động!

🎨 6. Ví dụ 4: Kết hợp HTML riêng biệt

Tạo cấu trúc thư mục:


my_web/
 ├─ app.py
 └─ templates/
     ├─ index.html
     └─ about.html
📄 app.py:

from flask import Flask, render_template

app = Flask(__name__)

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

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

if __name__ == '__main__':
    app.run(debug=True)
📄 templates/index.html:

<!DOCTYPE html>
<html>
<head>
  <title>Trang chủ Flask</title>
</head>
<body>
  <h1>Chào mừng đến với website Flask đầu tiên!</h1>
  <a href="/about">Tới trang Giới thiệu</a>
</body>
</html>
📄 templates/about.html:

<!DOCTYPE html>
<html>
<head>
  <title>Giới thiệu</title>
</head>
<body>
  <h2>Đây là trang giới thiệu được tách ra từ file HTML riêng.</h2>
  <a href="/">Quay lại Trang chủ</a>
</body>
</html>

➡️ Flask sẽ tự đọc các file HTML trong thư mục /templates và hiển thị ra trình duyệt.

🚀 7. Thử thách cho học sinh

  • Tạo thêm trang “Sở thích” hiển thị danh sách 3 sở thích của bạn.
  • Tạo form để nhập môn học yêu thích, trả về “Bạn thích môn Toán!”.
  • Trang trí bằng màu nền hoặc hình ảnh.

🏁 Kết luận

  • Flask giúp Python tạo web dễ dàng chỉ trong vài dòng code.
  • Học sinh THPT có thể làm web cá nhân nhỏ, rồi mở rộng thêm CSS, JS, SQL.

💬 Gợi ý mở rộng

  • Thử chạy code trên Replit hoặc PythonAnywhere.
  • Học thêm HTML, CSS, JavaScript để trang web chuyên nghiệp hơn.

© 2025 Trường THPT Phan Chu Trinh – Gia Lai | Biên soạn: DYI

Bài đăng phổ biến

💬 Bình luận

💬 Bình luận

📌 Danh sách bình luận