前言
在高校学生管理系统中,综合素质评价是衡量学生全面发展的重要指标。本文将详细介绍如何使用Django框架设计并实现一个科研成果与竞赛加分模块,涵盖数据模型设计、视图函数开发、模板页面渲染等完整流程。
通过本文的学习,你将掌握:
- Django多模型关联设计的方法与技巧
- 审核流程的状态机实现
- 加分统计与汇总的计算逻辑
一、需求分析与功能设计
1.1 业务场景分析
高校学生的加分项主要来源于两个方面:
- 科研成果:学术论文、专利、科研项目、科研获奖等
- 竞赛获奖:各类学科竞赛、创新创业大赛等
每项成果需要经过提交-审核-确认的流程,确保加分项的真实性和公正性。
1.2 功能模块划分
| 模块 | 功能描述 | 用户角色 |
|---|---|---|
| 科研成果管理 | 提交、查看科研成果 | 学生 |
| 竞赛获奖管理 | 提交、查看获奖记录 | 学生 |
| 加分总览 | 查看加分汇总统计 | 学生 |
| 科研审核 | 审核学生提交的科研成果 | 管理员 |
| 竞赛审核 | 审核学生提交的获奖记录 | 管理员 |
提示:合理的模块划分有助于代码的维护和扩展,建议遵循单一职责原则。
二、数据模型设计
2.1 科研成果模型
首先创建ResearchAchievement模型,用于存储学生的科研成果信息。
python
from django.db import models
from django.conf import settings
class ResearchAchievement(models.Model):
"""科研成果"""
ACHIEVEMENT_TYPES = [
('paper', '学术论文'),
('patent', '专利'),
('project', '科研项目'),
('award', '科研获奖'),
('report', '研究报告'),
]
LEVEL_CHOICES = [
('national', '国家级'),
('provincial', '省级'),
('municipal', '市级'),
('school', '校级'),
]
student = models.ForeignKey(
settings.AUTH_USER_MODEL,
on_delete=models.CASCADE,
related_name='research_achievements',
verbose_name='学生'
)
title = models.CharField('成果名称', max_length=200)
achievement_type = models.CharField('成果类型', max_length=20, choices=ACHIEVEMENT_TYPES)
level = models.CharField('成果级别', max_length=20, choices=LEVEL_CHOICES)
bonus_points = models.DecimalField('加分', max_digits=5, decimal_places=1, default=0)
模型设计的要点:
- 外键关联 :使用
ForeignKey关联用户模型,支持反向查询 - 选项字段 :使用
choices参数限制可选值,保证数据一致性 - 精确度控制 :
DecimalField适合存储分数,避免浮点精度问题
2.2 审核状态字段
科研成果需要经过审核流程,我们添加状态相关字段。
python
STATUS_CHOICES = [
('pending', '待审核'),
('approved', '已通过'),
('rejected', '已驳回'),
]
status = models.CharField('状态', max_length=20, choices=STATUS_CHOICES, default='pending')
review_comment = models.TextField('审核意见', blank=True)
reviewer = models.ForeignKey(
settings.AUTH_USER_MODEL,
on_delete=models.SET_NULL,
null=True, blank=True,
related_name='reviewed_research',
verbose_name='审核人'
)
review_time = models.DateTimeField('审核时间', null=True, blank=True)
状态机设计说明:
| 状态 | 可转换状态 | 触发条件 |
|---|---|---|
| pending | approved/rejected | 管理员审核操作 |
| approved | 无终态 | 审核通过 |
| rejected | 无终态 | 审核驳回 |
2.3 竞赛信息模型
竞赛信息作为基础数据,需要单独建模管理。
python
class Competition(models.Model):
"""竞赛信息"""
name = models.CharField('竞赛名称', max_length=100)
code = models.CharField('竞赛代码', max_length=20, unique=True)
level = models.CharField('竞赛级别', max_length=20, choices=ResearchAchievement.LEVEL_CHOICES)
organizer = models.CharField('主办单位', max_length=100)
description = models.TextField('竞赛描述', blank=True)
registration_start = models.DateField('报名开始时间')
registration_end = models.DateField('报名截止时间')
competition_date = models.DateField('比赛时间', null=True, blank=True)
is_active = models.BooleanField('是否开放', default=True)
竞赛模型的设计考虑:
- 唯一标识 :
code字段设置unique=True,确保竞赛代码唯一 - 时间管理:记录报名和比赛时间,便于前端展示竞赛状态
- 开关控制 :
is_active字段控制竞赛是否对学生可见
2.4 竞赛获奖记录模型
学生获奖记录需要关联竞赛信息和学生用户。
python
class CompetitionRecord(models.Model):
"""竞赛获奖记录"""
AWARD_LEVELS = [
('first', '一等奖'),
('second', '二等奖'),
('third', '三等奖'),
('excellence', '优秀奖'),
('participation', '参与奖'),
]
student = models.ForeignKey(
settings.AUTH_USER_MODEL,
on_delete=models.CASCADE,
related_name='competition_records',
verbose_name='学生'
)
competition = models.ForeignKey(
Competition,
on_delete=models.CASCADE,
related_name='records',
verbose_name='竞赛'
)
award_level = models.CharField('获奖等级', max_length=20, choices=AWARD_LEVELS)
award_date = models.DateField('获奖日期')
bonus_points = models.DecimalField('加分', max_digits=5, decimal_places=1, default=0)
2.5 加分汇总模型
为了提高查询效率,设计加分汇总模型存储学生的总分统计。
python
class BonusRecord(models.Model):
"""加分汇总记录"""
student = models.OneToOneField(
settings.AUTH_USER_MODEL,
on_delete=models.CASCADE,
related_name='bonus_record',
verbose_name='学生'
)
research_points = models.DecimalField('科研加分', max_digits=6, decimal_places=1, default=0)
competition_points = models.DecimalField('竞赛加分', max_digits=6, decimal_places=1, default=0)
other_points = models.DecimalField('其他加分', max_digits=6, decimal_places=1, default=0)
total_points = models.DecimalField('总加分', max_digits=6, decimal_places=1, default=0)
def calculate_total(self):
"""计算总分"""
self.total_points = self.research_points + self.competition_points + self.other_points
self.save()
return self.total_points
提示:使用
OneToOneField建立一对一关系,确保每个学生只有一条汇总记录。
三、视图函数开发
3.1 加分总览视图
加分总览页面展示学生的加分统计和最近提交记录。
python
from django.db.models import Sum
from .models import ResearchAchievement, CompetitionRecord, BonusRecord
@login_required
def bonus_overview(request):
"""加分总览"""
if not request.user.is_student():
messages.error(request, '只有学生可以查看加分')
return redirect('dashboard:dashboard')
# 获取或创建加分记录
bonus_record, created = BonusRecord.objects.get_or_create(student=request.user)
# 计算科研加分(只统计已审核通过的)
research_points = ResearchAchievement.objects.filter(
student=request.user, status='approved'
).aggregate(total=Sum('bonus_points'))['total'] or 0
# 计算竞赛加分
competition_points = CompetitionRecord.objects.filter(
student=request.user, status='approved'
).aggregate(total=Sum('bonus_points'))['total'] or 0
# 更新汇总记录
bonus_record.research_points = research_points
bonus_record.competition_points = competition_points
bonus_record.calculate_total()
return render(request, 'bonus/overview.html', {'bonus_record': bonus_record})
视图函数的关键点:
- 权限检查 :使用
is_student()方法验证用户角色 - 聚合查询 :
aggregate函数配合Sum实现分组求和 - 空值处理 :使用
or 0处理查询结果为None的情况
3.2 科研成果提交视图
学生提交科研成果的处理逻辑。
python
@login_required
def research_add(request):
"""添加科研成果"""
if not request.user.is_student():
messages.error(request, '只有学生可以添加科研成果')
return redirect('dashboard:dashboard')
if request.method == 'POST':
achievement = ResearchAchievement.objects.create(
student=request.user,
title=request.POST.get('title'),
achievement_type=request.POST.get('achievement_type'),
level=request.POST.get('level'),
journal=request.POST.get('journal', ''),
publish_date=request.POST.get('publish_date') or None,
authors=request.POST.get('authors', ''),
patent_number=request.POST.get('patent_number', ''),
bonus_points=request.POST.get('bonus_points', 0),
description=request.POST.get('description', ''),
)
messages.success(request, '科研成果提交成功,等待审核')
return redirect('bonus:research_list')
return render(request, 'bonus/research_form.html')
3.3 审核视图实现
管理员审核科研成果的核心逻辑。
python
from django.utils import timezone
@login_required
def research_review(request):
"""科研成果审核(管理员)"""
if not request.user.is_admin():
messages.error(request, '权限不足')
return redirect('dashboard:dashboard')
achievements = ResearchAchievement.objects.filter(status='pending')
if request.method == 'POST':
achievement_id = request.POST.get('achievement_id')
achievement = get_object_or_404(ResearchAchievement, id=achievement_id)
action = request.POST.get('action')
if action == 'approve':
achievement.status = 'approved'
achievement.bonus_points = request.POST.get('bonus_points', achievement.bonus_points)
else:
achievement.status = 'rejected'
achievement.review_comment = request.POST.get('review_comment', '')
achievement.reviewer = request.user
achievement.review_time = timezone.now()
achievement.save()
messages.success(request, '审核完成')
return redirect('bonus:research_review')
return render(request, 'bonus/research_review.html', {'achievements': achievements})
审核流程说明:
- 权限验证:只有管理员可以访问审核页面
- 查询待审核 :过滤
status='pending'的记录 - 状态更新:根据操作类型更新状态和加分值
- 记录审核信息:保存审核人、审核时间、审核意见
四、URL路由配置
4.1 路由设计
将所有加分相关路由集中在bonus应用下。
python
# bonus/urls.py
from django.urls import path
from . import views
app_name = 'bonus'
urlpatterns = [
# 学生功能
path('', views.bonus_overview, name='overview'),
path('research/', views.research_list, name='research_list'),
path('research/add/', views.research_add, name='research_add'),
path('competitions/', views.competition_list, name='competition_list'),
path('my-competitions/', views.my_competitions, name='my_competitions'),
path('competition/add/', views.competition_record_add, name='competition_record_add'),
# 管理员功能
path('research/review/', views.research_review, name='research_review'),
path('competition/review/', views.competition_review, name='competition_review'),
]
4.2 路由命名规范
| 路由模式 | 命名示例 | 用途 |
|---|---|---|
| 列表页 | research_list |
展示多条记录 |
| 详情页 | research_detail |
展示单条记录详情 |
| 添加页 | research_add |
新增记录表单 |
| 编辑页 | research_edit |
修改记录表单 |
| 审核页 | research_review |
审核操作页面 |
五、模板页面实现
5.1 加分总览模板
展示加分统计卡片和最近记录列表。
html
{% extends 'base.html' %}
{% block content %}
<!-- 加分统计卡片 -->
<div class="row g-4 mb-4">
<div class="col-md-3">
<div class="card stat-card">
<div class="stat-icon bg-primary">
<i class="bi bi-star"></i>
</div>
<div class="stat-content">
<div class="stat-value">{{ bonus_record.total_points }}</div>
<div class="stat-label">总加分</div>
</div>
</div>
</div>
<!-- 其他统计卡片... -->
</div>
{% endblock %}
5.2 科研成果表单模板
提交科研成果的表单页面。
html
<form method="post">
{% csrf_token %}
<div class="row">
<div class="col-md-6 mb-3">
<label class="form-label">成果名称 <span class="text-danger">*</span></label>
<input type="text" name="title" class="form-control" required>
</div>
<div class="col-md-6 mb-3">
<label class="form-label">成果类型 <span class="text-danger">*</span></label>
<select name="achievement_type" class="form-select" required>
<option value="paper">学术论文</option>
<option value="patent">专利</option>
<option value="project">科研项目</option>
<option value="award">科研获奖</option>
</select>
</div>
</div>
<button type="submit" class="btn btn-primary">提交审核</button>
</form>
5.3 审核页面模板
管理员审核科研成果的交互界面。
html
{% for item in achievements %}
<div class="card mb-3">
<div class="card-body">
<h5>{{ item.title }}</h5>
<p><strong>学生:</strong>{{ item.student.real_name }}</p>
<form method="post">
{% csrf_token %}
<input type="hidden" name="achievement_id" value="{{ item.id }}">
<div class="mb-2">
<label>加分值</label>
<input type="number" name="bonus_points" value="{{ item.bonus_points }}">
</div>
<div class="d-flex gap-2">
<button type="submit" name="action" value="approve" class="btn btn-success">通过</button>
<button type="submit" name="action" value="reject" class="btn btn-danger">驳回</button>
</div>
</form>
</div>
</div>
{% endfor %}
六、Admin后台配置
6.1 注册模型到Admin
为管理员提供友好的后台管理界面。
python
# bonus/admin.py
from django.contrib import admin
from .models import ResearchAchievement, Competition, CompetitionRecord, BonusRecord
@admin.register(ResearchAchievement)
class ResearchAchievementAdmin(admin.ModelAdmin):
list_display = ['title', 'student', 'achievement_type', 'level', 'bonus_points', 'status']
list_filter = ['status', 'achievement_type', 'level']
search_fields = ['title', 'student__real_name']
@admin.register(Competition)
class CompetitionAdmin(admin.ModelAdmin):
list_display = ['name', 'code', 'level', 'organizer', 'is_active']
list_filter = ['level', 'is_active']
search_fields = ['name', 'code']
6.2 Admin配置说明
| 配置项 | 作用 |
|---|---|
list_display |
列表页显示的字段 |
list_filter |
右侧过滤器字段 |
search_fields |
搜索框可搜索的字段 |
七、数据库迁移
7.1 创建迁移文件
模型定义完成后,需要生成数据库迁移文件。
bash
python manage.py makemigrations bonus
python manage.py migrate
迁移命令执行结果:
Migrations for 'bonus':
bonus\migrations\0001_initial.py
- Create model Competition
- Create model ResearchAchievement
- Create model CompetitionRecord
- Create model BonusRecord
7.2 迁移注意事项
提示:在执行迁移前,建议检查以下几点:
- 确保
AUTH_USER_MODEL配置正确- 确保外键关联的模型已存在
- 备份现有数据库数据
八、功能测试验证
8.1 测试用例设计
| 测试场景 | 预期结果 | 验证点 |
|---|---|---|
| 学生提交科研 | 状态为pending | 数据库记录正确 |
| 管理员审核通过 | 状态变为approved | 加分计入汇总 |
| 管理员审核驳回 | 状态变为rejected | 不计入加分 |
| 加分汇总计算 | 总分正确 | aggregate查询正确 |
8.2 常见问题排查
| 问题 | 可能原因 | 解决方案 |
|---|---|---|
| 加分为0 | 未审核通过 | 检查status字段 |
| 外键错误 | 用户模型配置 | 检查AUTH_USER_MODEL |
| 模板报错 | 字段名不匹配 | 检查模板变量名 |
总结
本文详细介绍了Django框架下科研成果与竞赛加分模块的设计与实现。从需求分析、模型设计、视图开发到模板渲染,覆盖了完整的功能开发流程。
核心设计要点回顾:
- 模型关联:合理使用ForeignKey和OneToOneField建立关联关系
- 状态管理:通过choices字段实现简单的状态机
- 聚合统计:使用Django ORM的aggregate函数进行分组计算
下一篇文章将介绍如何为学生管理系统设计现代化的登录界面,实现左右分栏布局和科技感视觉效果。
如果这篇文章对你有帮助,欢迎点赞👍、收藏⭐、关注🔔,你的支持是我持续创作的动力!