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**.

Không có nhận xét nào:

Đăng nhận xét

Bài đăng phổ biến

💬 Bình luận

💬 Bình luận

📌 Danh sách bình luận