学生选课系统
设计一个基于Flask的学生选课系统,包括学生、教师、课程、选课等核心实体。功能包括:学生选课/退课、查看已选课程、查看课程列表;教师管理课程;管理员管理用户和课程。需要数据校验(如选课时间、学分限制等)。前端使用Bootstrap,后端使用Flask-SQLAlchemy,认证使用Flask-Login。
将按照以下结构组织:
-
业务需求与功能结构设计
-
业务需求描述
-
功能模块划分
-
功能结构图(思维导图)
-
-
UML建模
-
用例图
-
类图
-
序列图(选课流程)
-
-
项目文件结构
-
核心功能实现
-
数据库模型
-
认证与权限
-
课程管理
-
选课/退课逻辑与校验(学分限制、时间冲突、人数限制)
-
视图与模板
-
-
系统界面展示与选课流程说明
-
运行说明
学生选课系统
1. 业务需求与功能结构设计
1.1 业务需求描述
学生选课系统旨在为高校师生提供便捷的在线选课服务。系统支持三种角色:
- 学生:查看课程列表、选课、退课、查看已选课程及成绩。
- 教师:管理自己开设的课程(增删改查)、查看选课学生名单、录入成绩。
- 管理员:管理用户(学生、教师)、管理课程(审核、开课)、查看系统统计。
核心业务流程:
- 管理员导入/添加学生和教师账号。
- 教师创建课程,设置课程容量、学分、上课时间等。
- 学生在规定时间内浏览课程,进行选课/退课操作。
- 系统校验选课冲突(时间、学分上限、课程容量),并反馈结果。
- 学生可查看已选课程及成绩。
- 教师可录入学生成绩。
1.2 功能模块划分
- 用户管理:注册(管理员)、登录、个人信息维护。
- 课程管理:教师创建课程、修改课程信息、删除课程;管理员审核课程(可选);学生查看课程列表。
- 选课管理:学生选课、退课;查看已选课程;系统校验(时间冲突、学分限制、容量限制)。
- 成绩管理:教师录入成绩;学生查看成绩。
- 系统管理:管理员管理用户、课程状态等。
1.3 功能结构图
2. UML 建模
2.1 用例图
2.2 类图
2.3 序列图(选课流程)
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.html 和 grades.html 类似。
manage_courses.html 和 course_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 选课流程
- 学生登录后,点击“可选课程”。
- 浏览课程列表,选择一门课程,点击“选课”。
- 后端执行校验:
- 是否已选
- 课程状态是否开放
- 是否满员
- 时间是否冲突
- 是否超过学期学分限制
- 若通过,创建选课记录,增加课程已选人数,返回成功消息;否则返回失败原因。
- 学生可到“已选课程”中查看,并可退课。
6. 运行说明
- 安装依赖:
pip install -r requirements.txt - 初始化数据库:
python scripts/init_db.py(或使用 Flask shell 执行) - 运行:
python run.py - 访问
http://localhost:5000
测试账号:
- 管理员:admin / admin123
- 学生:注册时填写学号等信息
- 教师:注册时填写工号等信息
7. 总结
本系统完整实现了学生选课的核心功能,包括用户管理、课程管理、选课退课、成绩管理,并提供了严格的业务校验(时间冲突、学分限制、容量限制)。系统采用 Flask 框架,结构清晰,易于扩展。可作为高校选课系统的原型或教学示例。
AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。
更多推荐


所有评论(0)