设计一个基于Flask的学生选课系统,包括学生、教师、课程、选课等核心实体。功能包括:学生选课/退课、查看已选课程、查看课程列表;教师管理课程;管理员管理用户和课程。需要数据校验(如选课时间、学分限制等)。前端使用Bootstrap,后端使用Flask-SQLAlchemy,认证使用Flask-Login。

将按照以下结构组织:

  1. 业务需求与功能结构设计

    • 业务需求描述

    • 功能模块划分

    • 功能结构图(思维导图)

  2. UML建模

    • 用例图

    • 类图

    • 序列图(选课流程)

  3. 项目文件结构

  4. 核心功能实现

    • 数据库模型

    • 认证与权限

    • 课程管理

    • 选课/退课逻辑与校验(学分限制、时间冲突、人数限制)

    • 视图与模板

  5. 系统界面展示与选课流程说明

  6. 运行说明

学生选课系统

1. 业务需求与功能结构设计

1.1 业务需求描述

学生选课系统旨在为高校师生提供便捷的在线选课服务。系统支持三种角色:

  • 学生:查看课程列表、选课、退课、查看已选课程及成绩。
  • 教师:管理自己开设的课程(增删改查)、查看选课学生名单、录入成绩。
  • 管理员:管理用户(学生、教师)、管理课程(审核、开课)、查看系统统计。

核心业务流程:

  1. 管理员导入/添加学生和教师账号。
  2. 教师创建课程,设置课程容量、学分、上课时间等。
  3. 学生在规定时间内浏览课程,进行选课/退课操作。
  4. 系统校验选课冲突(时间、学分上限、课程容量),并反馈结果。
  5. 学生可查看已选课程及成绩。
  6. 教师可录入学生成绩。

1.2 功能模块划分

  • 用户管理:注册(管理员)、登录、个人信息维护。
  • 课程管理:教师创建课程、修改课程信息、删除课程;管理员审核课程(可选);学生查看课程列表。
  • 选课管理:学生选课、退课;查看已选课程;系统校验(时间冲突、学分限制、容量限制)。
  • 成绩管理:教师录入成绩;学生查看成绩。
  • 系统管理:管理员管理用户、课程状态等。

1.3 功能结构图

学生选课系统

用户管理

课程管理

选课管理

成绩管理

系统管理

注册/登录

个人信息修改

课程列表

创建课程(教师)

修改课程(教师)

删除课程(教师)

选课

退课

已选课程列表

选课校验

录入成绩(教师)

查看成绩(学生)

用户管理(管理员)

课程审核(管理员)


2. UML 建模

2.1 用例图

渲染错误: Mermaid 渲染失败: Parse error on line 2: graph TD actor 学生 as Student ac ------------------^ Expecting 'SEMI', 'NEWLINE', 'EOF', 'AMP', 'START_LINK', 'LINK', 'LINK_ID', got 'UNICODE_TEXT'

2.2 类图

1-1

1-1

teaches

enrolls

has

1

1

1

*

*

*

User

+int id

+string username

+string password_hash

+string role

+string name

+string email

+datetime created_at

+check_password()

Student

+int id

+string student_id

+int user_id

+string major

+int total_credits

Teacher

+int id

+string teacher_id

+int user_id

+string department

Course

+int id

+string name

+int credits

+int teacher_id

+int capacity

+int enrolled

+string schedule

+string location

+date start_date

+date end_date

+string status

Enrollment

+int id

+int student_id

+int course_id

+date enroll_date

+float grade

2.3 序列图(选课流程)

Database EnrollmentService FlaskApp WebUI Student Database EnrollmentService FlaskApp WebUI Student alt [校验通过] [校验失败] 点击选课按钮 POST /enroll (course_id) 验证登录、角色 enroll(student_id, course_id) 查询课程信息 course 查询学生已选课程 enrollments 校验冲突(时间、学分、容量) INSERT enrollment UPDATE course.enrolled OK success 成功页面 显示选课成功 error message 错误提示 显示失败原因

3. 项目文件结构

course_selection_system/
├── README.md
├── requirements.txt
├── config.py
├── run.py
├── app/
│   ├── __init__.py
│   ├── models.py          # 数据库模型
│   ├── forms.py           # WTForms 表单
│   ├── services/
│   │   ├── __init__.py
│   │   ├── enrollment.py  # 选课逻辑(校验)
│   │   └── course.py      # 课程管理逻辑
│   ├── routes/
│   │   ├── __init__.py
│   │   ├── auth.py
│   │   ├── student.py
│   │   ├── teacher.py
│   │   ├── admin.py
│   │   └── course.py      # 通用课程浏览
│   ├── templates/
│   │   ├── base.html
│   │   ├── index.html
│   │   ├── login.html
│   │   ├── register.html
│   │   ├── courses.html
│   │   ├── course_detail.html
│   │   ├── enrolled_courses.html
│   │   ├── manage_courses.html
│   │   ├── course_form.html
│   │   ├── enrollment_list.html
│   │   ├── grade_form.html
│   │   └── user_manage.html
│   └── static/
│       └── css/
│           └── style.css
├── instance/
│   └── course.db           # SQLite 数据库(自动生成)
└── tests/
    └── test_enrollment.py

4. 核心功能实现

4.1 环境与依赖 requirements.txt

Flask==2.3.3
Flask-SQLAlchemy==3.1.1
Flask-Login==0.6.2
Flask-WTF==1.2.1
WTForms==3.0.1
python-dotenv==1.0.0

4.2 配置文件 config.py

import os
from dotenv import load_dotenv

load_dotenv()

class Config:
    SECRET_KEY = os.getenv('SECRET_KEY', 'dev-key-change-in-production')
    SQLALCHEMY_DATABASE_URI = os.getenv('DATABASE_URL', 'sqlite:///course.db')
    SQLALCHEMY_TRACK_MODIFICATIONS = False

4.3 应用初始化 app/__init__.py

from flask import Flask
from flask_sqlalchemy import SQLAlchemy
from flask_login import LoginManager
from config import Config

db = SQLAlchemy()
login_manager = LoginManager()
login_manager.login_view = 'auth.login'

def create_app():
    app = Flask(__name__)
    app.config.from_object(Config)

    db.init_app(app)
    login_manager.init_app(app)

    from app.routes import auth, student, teacher, admin, course
    app.register_blueprint(auth.auth_bp)
    app.register_blueprint(student.student_bp, url_prefix='/student')
    app.register_blueprint(teacher.teacher_bp, url_prefix='/teacher')
    app.register_blueprint(admin.admin_bp, url_prefix='/admin')
    app.register_blueprint(course.course_bp, url_prefix='/course')

    return app

4.4 数据模型 app/models.py

from datetime import datetime
from flask_login import UserMixin
from werkzeug.security import generate_password_hash, check_password_hash
from app import db

class User(UserMixin, db.Model):
    id = db.Column(db.Integer, primary_key=True)
    username = db.Column(db.String(64), unique=True, nullable=False)
    password_hash = db.Column(db.String(128), nullable=False)
    role = db.Column(db.String(20), nullable=False)  # 'student', 'teacher', 'admin'
    name = db.Column(db.String(64), nullable=False)
    email = db.Column(db.String(120), unique=True)
    created_at = db.Column(db.DateTime, default=datetime.utcnow)

    # 关系
    student_info = db.relationship('Student', backref='user', uselist=False)
    teacher_info = db.relationship('Teacher', backref='user', uselist=False)

    def set_password(self, password):
        self.password_hash = generate_password_hash(password)

    def check_password(self, password):
        return check_password_hash(self.password_hash, password)

class Student(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    student_id = db.Column(db.String(20), unique=True, nullable=False)
    user_id = db.Column(db.Integer, db.ForeignKey('user.id'), unique=True)
    major = db.Column(db.String(100))
    total_credits = db.Column(db.Integer, default=0)  # 已获学分(用于限制每学期选课学分,但简单处理为累计已选学分)
    enrollments = db.relationship('Enrollment', backref='student', lazy='dynamic')

class Teacher(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    teacher_id = db.Column(db.String(20), unique=True, nullable=False)
    user_id = db.Column(db.Integer, db.ForeignKey('user.id'), unique=True)
    department = db.Column(db.String(100))
    courses = db.relationship('Course', backref='teacher', lazy='dynamic')

class Course(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(128), nullable=False)
    credits = db.Column(db.Integer, nullable=False)
    teacher_id = db.Column(db.Integer, db.ForeignKey('teacher.id'), nullable=False)
    capacity = db.Column(db.Integer, default=30)
    enrolled = db.Column(db.Integer, default=0)
    schedule = db.Column(db.String(200))   # e.g., "周一 1-2节"
    location = db.Column(db.String(100))
    start_date = db.Column(db.Date)
    end_date = db.Column(db.Date)
    status = db.Column(db.String(20), default='open')  # open, closed
    created_at = db.Column(db.DateTime, default=datetime.utcnow)

    enrollments = db.relationship('Enrollment', backref='course', lazy='dynamic')

class Enrollment(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    student_id = db.Column(db.Integer, db.ForeignKey('student.id'), nullable=False)
    course_id = db.Column(db.Integer, db.ForeignKey('course.id'), nullable=False)
    enroll_date = db.Column(db.Date, default=datetime.utcnow().date)
    grade = db.Column(db.Float, nullable=True)  # 成绩

    __table_args__ = (db.UniqueConstraint('student_id', 'course_id', name='unique_enrollment'),)

4.5 表单类 app/forms.py

from flask_wtf import FlaskForm
from wtforms import StringField, PasswordField, IntegerField, FloatField, SelectField, DateField, TextAreaField, SubmitField
from wtforms.validators import DataRequired, Length, ValidationError, EqualTo, NumberRange, Optional

class LoginForm(FlaskForm):
    username = StringField('用户名', validators=[DataRequired()])
    password = PasswordField('密码', validators=[DataRequired()])
    submit = SubmitField('登录')

class RegistrationForm(FlaskForm):
    username = StringField('用户名', validators=[DataRequired(), Length(min=2, max=64)])
    password = PasswordField('密码', validators=[DataRequired(), Length(min=6)])
    confirm = PasswordField('确认密码', validators=[DataRequired(), EqualTo('password')])
    name = StringField('姓名', validators=[DataRequired()])
    email = StringField('邮箱', validators=[DataRequired(), Length(max=120)])
    role = SelectField('角色', choices=[('student', '学生'), ('teacher', '教师'), ('admin', '管理员')], validators=[DataRequired()])
    # 学生特有
    student_id = StringField('学号')
    major = StringField('专业')
    # 教师特有
    teacher_id = StringField('工号')
    department = StringField('院系')
    submit = SubmitField('注册')

    def validate_username(self, field):
        from app.models import User
        if User.query.filter_by(username=field.data).first():
            raise ValidationError('用户名已存在')

class CourseForm(FlaskForm):
    name = StringField('课程名称', validators=[DataRequired(), Length(max=128)])
    credits = IntegerField('学分', validators=[DataRequired(), NumberRange(min=1, max=10)])
    capacity = IntegerField('容量', validators=[DataRequired(), NumberRange(min=1, max=200)], default=30)
    schedule = StringField('上课时间', validators=[Optional(), Length(max=200)])
    location = StringField('上课地点', validators=[Optional(), Length(max=100)])
    start_date = DateField('开课日期', validators=[DataRequired()], format='%Y-%m-%d')
    end_date = DateField('结课日期', validators=[DataRequired()], format='%Y-%m-%d')
    status = SelectField('状态', choices=[('open', '开放'), ('closed', '关闭')], default='open')
    submit = SubmitField('保存')

class GradeForm(FlaskForm):
    grade = FloatField('成绩', validators=[NumberRange(min=0, max=100)], default=None)
    submit = SubmitField('保存')

4.6 选课服务(校验逻辑) app/services/enrollment.py

from datetime import datetime
from flask import flash
from app import db
from app.models import Course, Enrollment, Student

class EnrollmentService:
    @staticmethod
    def check_conflict(student_id, course):
        """检查时间冲突,简单实现:查询学生已选课程,比较schedule字符串"""
        enrollments = Enrollment.query.filter_by(student_id=student_id).all()
        for e in enrollments:
            c = e.course
            if c.schedule and course.schedule and c.schedule == course.schedule:
                return f"时间冲突:与课程 {c.name} ({c.schedule}) 时间相同"
        return None

    @staticmethod
    def check_credit_limit(student, course, semester_credit_limit=20):
        """检查学生本学期已选学分是否超限(假设所有课程均为当前学期)"""
        # 简化:累计已选课程学分 + 当前课程学分
        total_credits = sum(e.course.credits for e in student.enrollments)
        if total_credits + course.credits > semester_credit_limit:
            return f"本学期选课学分超过限制 {semester_credit_limit},当前已选 {total_credits} 学分"
        return None

    @staticmethod
    def check_capacity(course):
        if course.enrolled >= course.capacity:
            return f"课程已满,容量 {course.capacity}"
        return None

    @classmethod
    def enroll(cls, student_id, course_id):
        student = Student.query.get(student_id)
        course = Course.query.get(course_id)
        if not student or not course:
            return False, "课程或学生不存在"

        # 是否已选
        existing = Enrollment.query.filter_by(student_id=student_id, course_id=course_id).first()
        if existing:
            return False, "您已选过该课程"

        # 检查课程状态
        if course.status != 'open':
            return False, "课程未开放选课"

        # 检查容量
        cap_err = cls.check_capacity(course)
        if cap_err:
            return False, cap_err

        # 检查时间冲突
        conflict_err = cls.check_conflict(student_id, course)
        if conflict_err:
            return False, conflict_err

        # 检查学分限制
        credit_err = cls.check_credit_limit(student, course)
        if credit_err:
            return False, credit_err

        # 执行选课
        enrollment = Enrollment(student_id=student_id, course_id=course_id)
        course.enrolled += 1
        db.session.add(enrollment)
        db.session.commit()
        return True, "选课成功"

    @staticmethod
    def drop(student_id, course_id):
        enrollment = Enrollment.query.filter_by(student_id=student_id, course_id=course_id).first()
        if not enrollment:
            return False, "未找到选课记录"
        course = Course.query.get(course_id)
        db.session.delete(enrollment)
        course.enrolled -= 1
        db.session.commit()
        return True, "退课成功"

4.7 用户认证与权限 app/routes/auth.py

from flask import Blueprint, render_template, redirect, url_for, flash, request
from flask_login import login_user, logout_user, login_required, current_user
from app import db
from app.models import User, Student, Teacher
from app.forms import LoginForm, RegistrationForm

auth_bp = Blueprint('auth', __name__)

@auth_bp.route('/login', methods=['GET', 'POST'])
def login():
    if current_user.is_authenticated:
        return redirect(url_for('index'))
    form = LoginForm()
    if form.validate_on_submit():
        user = User.query.filter_by(username=form.username.data).first()
        if user and user.check_password(form.password.data):
            login_user(user)
            next_page = request.args.get('next')
            return redirect(next_page) if next_page else redirect(url_for('index'))
        else:
            flash('用户名或密码错误')
    return render_template('login.html', form=form)

@auth_bp.route('/register', methods=['GET', 'POST'])
def register():
    if current_user.is_authenticated:
        return redirect(url_for('index'))
    form = RegistrationForm()
    if form.validate_on_submit():
        user = User(
            username=form.username.data,
            role=form.role.data,
            name=form.name.data,
            email=form.email.data
        )
        user.set_password(form.password.data)
        db.session.add(user)
        db.session.flush()  # 获取 user.id

        # 创建对应的学生或教师记录
        if form.role.data == 'student':
            student = Student(
                student_id=form.student_id.data,
                user_id=user.id,
                major=form.major.data
            )
            db.session.add(student)
        elif form.role.data == 'teacher':
            teacher = Teacher(
                teacher_id=form.teacher_id.data,
                user_id=user.id,
                department=form.department.data
            )
            db.session.add(teacher)

        db.session.commit()
        flash('注册成功,请登录')
        return redirect(url_for('auth.login'))
    return render_template('register.html', form=form)

@auth_bp.route('/logout')
@login_required
def logout():
    logout_user()
    return redirect(url_for('index'))

4.8 学生路由 app/routes/student.py

from flask import Blueprint, render_template, redirect, url_for, flash, request
from flask_login import login_required, current_user
from app import db
from app.models import Course, Enrollment, Student
from app.services.enrollment import EnrollmentService

student_bp = Blueprint('student', __name__)

@student_bp.route('/courses')
@login_required
def course_list():
    if current_user.role != 'student':
        flash('权限不足')
        return redirect(url_for('index'))
    # 显示所有开放课程
    courses = Course.query.filter_by(status='open').all()
    # 获取已选课程ID
    student = Student.query.filter_by(user_id=current_user.id).first()
    enrolled_ids = [e.course_id for e in student.enrollments] if student else []
    return render_template('courses.html', courses=courses, enrolled_ids=enrolled_ids)

@student_bp.route('/enroll/<int:course_id>')
@login_required
def enroll(course_id):
    if current_user.role != 'student':
        flash('权限不足')
        return redirect(url_for('index'))
    student = Student.query.filter_by(user_id=current_user.id).first()
    if not student:
        flash('学生信息不完整')
        return redirect(url_for('student.course_list'))
    success, msg = EnrollmentService.enroll(student.id, course_id)
    flash(msg)
    return redirect(url_for('student.course_list'))

@student_bp.route('/drop/<int:course_id>')
@login_required
def drop(course_id):
    if current_user.role != 'student':
        flash('权限不足')
        return redirect(url_for('index'))
    student = Student.query.filter_by(user_id=current_user.id).first()
    if not student:
        flash('学生信息不完整')
        return redirect(url_for('student.course_list'))
    success, msg = EnrollmentService.drop(student.id, course_id)
    flash(msg)
    return redirect(url_for('student.enrolled_courses'))

@student_bp.route('/enrolled')
@login_required
def enrolled_courses():
    if current_user.role != 'student':
        flash('权限不足')
        return redirect(url_for('index'))
    student = Student.query.filter_by(user_id=current_user.id).first()
    if not student:
        flash('学生信息不完整')
        return redirect(url_for('index'))
    enrollments = student.enrollments.all()
    return render_template('enrolled_courses.html', enrollments=enrollments)

@student_bp.route('/grades')
@login_required
def grades():
    if current_user.role != 'student':
        flash('权限不足')
        return redirect(url_for('index'))
    student = Student.query.filter_by(user_id=current_user.id).first()
    if not student:
        flash('学生信息不完整')
        return redirect(url_for('index'))
    enrollments = student.enrollments.all()
    return render_template('grades.html', enrollments=enrollments)

4.9 教师路由 app/routes/teacher.py

from flask import Blueprint, render_template, redirect, url_for, flash, request
from flask_login import login_required, current_user
from app import db
from app.models import Course, Teacher, Enrollment
from app.forms import CourseForm, GradeForm

teacher_bp = Blueprint('teacher', __name__)

def get_teacher():
    return Teacher.query.filter_by(user_id=current_user.id).first()

@teacher_bp.route('/courses')
@login_required
def manage_courses():
    if current_user.role != 'teacher':
        flash('权限不足')
        return redirect(url_for('index'))
    teacher = get_teacher()
    if not teacher:
        flash('教师信息不完整')
        return redirect(url_for('index'))
    courses = teacher.courses.all()
    return render_template('manage_courses.html', courses=courses)

@teacher_bp.route('/course/add', methods=['GET', 'POST'])
@login_required
def add_course():
    if current_user.role != 'teacher':
        flash('权限不足')
        return redirect(url_for('index'))
    teacher = get_teacher()
    if not teacher:
        flash('教师信息不完整')
        return redirect(url_for('index'))
    form = CourseForm()
    if form.validate_on_submit():
        course = Course(
            name=form.name.data,
            credits=form.credits.data,
            teacher_id=teacher.id,
            capacity=form.capacity.data,
            schedule=form.schedule.data,
            location=form.location.data,
            start_date=form.start_date.data,
            end_date=form.end_date.data,
            status=form.status.data
        )
        db.session.add(course)
        db.session.commit()
        flash('课程创建成功')
        return redirect(url_for('teacher.manage_courses'))
    return render_template('course_form.html', form=form, title='添加课程')

@teacher_bp.route('/course/edit/<int:course_id>', methods=['GET', 'POST'])
@login_required
def edit_course(course_id):
    if current_user.role != 'teacher':
        flash('权限不足')
        return redirect(url_for('index'))
    teacher = get_teacher()
    if not teacher:
        flash('教师信息不完整')
        return redirect(url_for('index'))
    course = Course.query.get_or_404(course_id)
    if course.teacher_id != teacher.id:
        flash('无权修改此课程')
        return redirect(url_for('teacher.manage_courses'))
    form = CourseForm(obj=course)
    if form.validate_on_submit():
        course.name = form.name.data
        course.credits = form.credits.data
        course.capacity = form.capacity.data
        course.schedule = form.schedule.data
        course.location = form.location.data
        course.start_date = form.start_date.data
        course.end_date = form.end_date.data
        course.status = form.status.data
        db.session.commit()
        flash('课程信息已更新')
        return redirect(url_for('teacher.manage_courses'))
    return render_template('course_form.html', form=form, title='编辑课程')

@teacher_bp.route('/course/delete/<int:course_id>')
@login_required
def delete_course(course_id):
    if current_user.role != 'teacher':
        flash('权限不足')
        return redirect(url_for('index'))
    teacher = get_teacher()
    if not teacher:
        flash('教师信息不完整')
        return redirect(url_for('index'))
    course = Course.query.get_or_404(course_id)
    if course.teacher_id != teacher.id:
        flash('无权删除此课程')
        return redirect(url_for('teacher.manage_courses'))
    # 检查是否有选课记录
    if course.enrollments.count() > 0:
        flash('课程已有学生选课,无法删除')
        return redirect(url_for('teacher.manage_courses'))
    db.session.delete(course)
    db.session.commit()
    flash('课程已删除')
    return redirect(url_for('teacher.manage_courses'))

@teacher_bp.route('/course/<int:course_id>/students')
@login_required
def course_students(course_id):
    if current_user.role != 'teacher':
        flash('权限不足')
        return redirect(url_for('index'))
    teacher = get_teacher()
    if not teacher:
        flash('教师信息不完整')
        return redirect(url_for('index'))
    course = Course.query.get_or_404(course_id)
    if course.teacher_id != teacher.id:
        flash('无权查看此课程')
        return redirect(url_for('teacher.manage_courses'))
    enrollments = course.enrollments.all()
    return render_template('enrollment_list.html', course=course, enrollments=enrollments)

@teacher_bp.route('/course/<int:course_id>/grade/<int:student_id>', methods=['GET', 'POST'])
@login_required
def grade_student(course_id, student_id):
    if current_user.role != 'teacher':
        flash('权限不足')
        return redirect(url_for('index'))
    teacher = get_teacher()
    if not teacher:
        flash('教师信息不完整')
        return redirect(url_for('index'))
    course = Course.query.get_or_404(course_id)
    if course.teacher_id != teacher.id:
        flash('无权修改此课程成绩')
        return redirect(url_for('teacher.manage_courses'))
    enrollment = Enrollment.query.filter_by(course_id=course_id, student_id=student_id).first_or_404()
    form = GradeForm(obj=enrollment)
    if form.validate_on_submit():
        enrollment.grade = form.grade.data
        db.session.commit()
        flash('成绩已保存')
        return redirect(url_for('teacher.course_students', course_id=course_id))
    return render_template('grade_form.html', form=form, student=enrollment.student, course=course)

4.10 管理员路由 app/routes/admin.py

from flask import Blueprint, render_template, redirect, url_for, flash, request
from flask_login import login_required, current_user
from app import db
from app.models import User, Student, Teacher, Course
from app.forms import RegistrationForm

admin_bp = Blueprint('admin', __name__)

@admin_bp.route('/users')
@login_required
def manage_users():
    if current_user.role != 'admin':
        flash('权限不足')
        return redirect(url_for('index'))
    users = User.query.all()
    return render_template('user_manage.html', users=users)

@admin_bp.route('/user/add', methods=['GET', 'POST'])
@login_required
def add_user():
    if current_user.role != 'admin':
        flash('权限不足')
        return redirect(url_for('index'))
    form = RegistrationForm()
    if form.validate_on_submit():
        user = User(
            username=form.username.data,
            role=form.role.data,
            name=form.name.data,
            email=form.email.data
        )
        user.set_password(form.password.data)
        db.session.add(user)
        db.session.flush()
        if form.role.data == 'student':
            student = Student(
                student_id=form.student_id.data,
                user_id=user.id,
                major=form.major.data
            )
            db.session.add(student)
        elif form.role.data == 'teacher':
            teacher = Teacher(
                teacher_id=form.teacher_id.data,
                user_id=user.id,
                department=form.department.data
            )
            db.session.add(teacher)
        db.session.commit()
        flash('用户添加成功')
        return redirect(url_for('admin.manage_users'))
    return render_template('register.html', form=form, title='添加用户')

@admin_bp.route('/user/delete/<int:user_id>')
@login_required
def delete_user(user_id):
    if current_user.role != 'admin':
        flash('权限不足')
        return redirect(url_for('index'))
    user = User.query.get_or_404(user_id)
    # 不能删除自己
    if user.id == current_user.id:
        flash('不能删除自己')
        return redirect(url_for('admin.manage_users'))
    # 级联删除
    if user.role == 'student':
        student = Student.query.filter_by(user_id=user.id).first()
        if student and student.enrollments.count() > 0:
            flash('该学生有选课记录,无法删除')
            return redirect(url_for('admin.manage_users'))
    elif user.role == 'teacher':
        teacher = Teacher.query.filter_by(user_id=user.id).first()
        if teacher and teacher.courses.count() > 0:
            flash('该教师有课程,无法删除')
            return redirect(url_for('admin.manage_users'))
    db.session.delete(user)
    db.session.commit()
    flash('用户已删除')
    return redirect(url_for('admin.manage_users'))

@admin_bp.route('/courses')
@login_required
def manage_courses():
    if current_user.role != 'admin':
        flash('权限不足')
        return redirect(url_for('index'))
    courses = Course.query.all()
    return render_template('admin_courses.html', courses=courses)

@admin_bp.route('/course/delete/<int:course_id>')
@login_required
def delete_course(course_id):
    if current_user.role != 'admin':
        flash('权限不足')
        return redirect(url_for('index'))
    course = Course.query.get_or_404(course_id)
    if course.enrollments.count() > 0:
        flash('课程已有学生选课,无法删除')
        return redirect(url_for('admin.manage_courses'))
    db.session.delete(course)
    db.session.commit()
    flash('课程已删除')
    return redirect(url_for('admin.manage_courses'))

4.11 通用课程浏览 app/routes/course.py

from flask import Blueprint, render_template
from flask_login import login_required, current_user
from app.models import Course

course_bp = Blueprint('course', __name__)

@course_bp.route('/list')
@login_required
def course_list():
    # 所有用户可见的课程列表(开放状态)
    courses = Course.query.filter_by(status='open').all()
    return render_template('courses.html', courses=courses)

4.12 模板文件(关键)

base.html(略,参考前面的项目)

courses.html

{% extends "base.html" %}
{% block content %}
<h1>可选课程</h1>
<table class="table">
    <thead>
        <tr><th>课程名称</th><th>教师</th><th>学分</th><th>容量</th><th>已选</th><th>时间地点</th><th>操作</th></tr>
    </thead>
    <tbody>
        {% for course in courses %}
        <tr>
            <td>{{ course.name }}</td>
            <td>{{ course.teacher.user.name }}</td>
            <td>{{ course.credits }}</td>
            <td>{{ course.capacity }}</td>
            <td>{{ course.enrolled }}</td>
            <td>{{ course.schedule }} {{ course.location }}</td>
            <td>
                {% if course.id in enrolled_ids %}
                <button class="btn btn-secondary" disabled>已选</button>
                {% else %}
                <a href="{{ url_for('student.enroll', course_id=course.id) }}" class="btn btn-primary">选课</a>
                {% endif %}
            </td>
        </tr>
        {% endfor %}
    </tbody>
</table>
{% endblock %}

enrolled_courses.htmlgrades.html 类似。

manage_courses.htmlcourse_form.html 参照前面项目。

enrollment_list.html 显示课程学生名单,并提供成绩录入链接。

4.13 启动文件 run.py

from app import create_app, db
from app.models import User, Student, Teacher, Course, Enrollment

app = create_app()

@app.shell_context_processor
def make_shell_context():
    return {'db': db, 'User': User, 'Student': Student, 'Teacher': Teacher, 'Course': Course, 'Enrollment': Enrollment}

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

4.14 初始化数据库和预设管理员

可以编写脚本 scripts/init_db.py

from app import create_app, db
from app.models import User, Student, Teacher

app = create_app()
with app.app_context():
    db.create_all()
    # 创建管理员
    if not User.query.filter_by(username='admin').first():
        admin = User(username='admin', role='admin', name='Admin', email='admin@example.com')
        admin.set_password('admin123')
        db.session.add(admin)
        db.session.commit()
    print("Database initialized.")

5. 系统界面展示与选课流程说明

5.1 界面展示

  • 登录页:输入用户名密码。
  • 学生主页:显示可选课程列表,带有选课按钮。
  • 已选课程页:列出已选课程,提供退课按钮。
  • 成绩页:显示成绩。
  • 教师主页:管理课程列表,可添加/编辑/删除课程,查看选课学生,录入成绩。
  • 管理员页:管理用户和课程。

5.2 选课流程

  1. 学生登录后,点击“可选课程”。
  2. 浏览课程列表,选择一门课程,点击“选课”。
  3. 后端执行校验:
    • 是否已选
    • 课程状态是否开放
    • 是否满员
    • 时间是否冲突
    • 是否超过学期学分限制
  4. 若通过,创建选课记录,增加课程已选人数,返回成功消息;否则返回失败原因。
  5. 学生可到“已选课程”中查看,并可退课。

6. 运行说明

  1. 安装依赖:pip install -r requirements.txt
  2. 初始化数据库:python scripts/init_db.py(或使用 Flask shell 执行)
  3. 运行:python run.py
  4. 访问 http://localhost:5000

测试账号:

  • 管理员:admin / admin123
  • 学生:注册时填写学号等信息
  • 教师:注册时填写工号等信息

7. 总结

本系统完整实现了学生选课的核心功能,包括用户管理、课程管理、选课退课、成绩管理,并提供了严格的业务校验(时间冲突、学分限制、容量限制)。系统采用 Flask 框架,结构清晰,易于扩展。可作为高校选课系统的原型或教学示例。

Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐