1. 项目概述:Python+Vue大学生问卷调查系统全栈实践
去年为某高校学生会开发问卷调查系统时,我深刻体会到传统纸质问卷的痛点:活动结束后收回的2000份问卷,需要5个同学连续统计三天。这促使我设计了一套基于Python和Vue的在线问卷系统,最终实现实时统计和可视化,组织者能在活动结束同时拿到分析报告。
本系统采用前后端分离架构,后端使用Django/Flask提供RESTful API服务,前端基于Vue 3构建动态表单界面。核心解决三大问题:
- 问卷设计灵活性:支持12种题型(包括矩阵题和评分题)
- 数据采集实时性:采用WebSocket推送统计结果
- 分析智能化:自动生成词云和交叉分析报表
实测在校园网环境下,系统可稳定支撑800+并发提交,相比传统方式效率提升约15倍。下面将从技术选型到实现细节完整复盘整个开发过程。
2. 技术架构设计
2.1 后端技术栈选型
在Django和Flask之间的选择,我们做了如下对比测试:
| 指标 | Django (3.2) | Flask (2.0) |
|---|---|---|
| 开发速度 | ★★★★★ | ★★★☆☆ |
| 灵活性 | ★★☆☆☆ | ★★★★★ |
| 自带Admin | 完整后台 | 需扩展 |
| JWT支持 | 需drf插件 | 直接集成 |
| ORM性能 | 850 QPS | 920 QPS |
最终选择Django作为主框架,因其:
- 内置Auth系统完美适配校园LDAP认证
- Admin后台节省80%基础CRUD开发量
- 完善的文档和中文社区支持
关键依赖包:
python复制# requirements.txt
djangorestframework==3.12.4
django-cors-headers==3.7.0
mysqlclient==2.1.0
redis==3.5.3
pandas==1.3.3
nltk==3.6.2
2.2 前端技术方案
采用Vue 3组合式API带来两大优势:
- 逻辑复用:将表单验证、题目渲染等封装成Composables
- 性能提升:相比Options API打包体积减少40%
技术矩阵:
bash复制├── vue3
├── pinia # 状态管理
├── axios # HTTP客户端
├── echarts5 # 可视化
└── element-plus # UI组件
特别优化:
- 动态加载echarts组件减少首屏加载时间
- 使用v-memo优化大型表单渲染性能
3. 核心模块实现
3.1 问卷引擎设计
数据库模型关键设计:
python复制class Question(models.Model):
TYPE_CHOICES = [
('RADIO', '单选题'),
('CHECKBOX', '多选题'),
('MATRIX', '矩阵题') # 新增题型
]
survey = models.ForeignKey(Survey, on_delete=models.CASCADE)
text = models.TextField()
type = models.CharField(max_length=20, choices=TYPE_CHOICES)
required = models.BooleanField(default=True)
order = models.PositiveIntegerField() # 题目顺序控制
class Option(models.Model):
question = models.ForeignKey(Question, on_delete=models.CASCADE)
text = models.CharField(max_length=200)
has_text_field = models.BooleanField(default=False) # 支持"其他"选项
动态表单渲染技巧:
vue复制<template v-for="(question, qIdx) in questions" :key="question.id">
<component
:is="question.type"
:question="question"
@change="handleAnswerChange"
v-memo="[question.type]"
/>
</template>
3.2 实时统计实现
采用双通道数据传输方案:
- REST API:初始数据加载
- WebSocket:实时更新
Django Channels配置示例:
python复制# routing.py
application = ProtocolTypeRouter({
"http": get_asgi_application(),
"websocket": AuthMiddlewareStack(
URLRouter([
path("ws/survey/<survey_id>/", SurveyConsumer),
])
),
})
前端处理逻辑:
javascript复制const socket = new WebSocket(`wss://${location.host}/ws/survey/${surveyId}/`)
socket.onmessage = (event) => {
const data = JSON.parse(event.data)
if (data.type === 'STATS_UPDATE') {
store.updateStats(data.payload) // Pinia更新状态
}
}
4. 性能优化实践
4.1 数据库优化
- 索引策略:
python复制class Answer(models.Model):
question = models.ForeignKey(Question, on_delete=models.CASCADE, db_index=True)
session_key = models.CharField(max_length=40, db_index=True)
created_at = models.DateTimeField(auto_now_add=True, index=True)
- 查询优化:
python复制# 错误做法:N+1查询
answers = Answer.objects.filter(question__survey=survey)
for a in answers:
print(a.question.text) # 每次循环都查询question表
# 正确做法:select_related
answers = Answer.objects.select_related('question').filter(question__survey=survey)
4.2 缓存策略
使用Redis三级缓存:
- 问卷模板缓存(1小时过期)
- 统计结果缓存(5分钟过期)
- 热门问卷预加载
配置示例:
python复制CACHES = {
"default": {
"BACKEND": "django_redis.cache.RedisCache",
"LOCATION": "redis://127.0.0.1:6379/1",
"OPTIONS": {
"CLIENT_CLASS": "django_redis.client.DefaultClient",
"COMPRESSOR": "django_redis.compressors.zlib.ZlibCompressor",
}
}
}
5. 典型问题解决方案
5.1 高并发提交处理
遇到问题:招新季单日提交峰值达12万次,出现数据库连接池耗尽
解决方案:
- 引入连接池:
python复制DATABASES = {
'default': {
'ENGINE': 'django.db.backends.mysql',
'CONN_MAX_AGE': 60, # 连接复用
'POOL_SIZE': 20, # 连接池大小
}
}
- 批量插入优化:
python复制# 原始方案:逐条插入(约1200 QPS)
for answer in answers:
Answer.objects.create(...)
# 优化方案:bulk_create(提升至8500 QPS)
Answer.objects.bulk_create(
[Answer(...), Answer(...)],
batch_size=500
)
5.2 跨域问题处理
Django配置要点:
python复制# settings.py
CORS_ALLOWED_ORIGINS = [
"https://survey.example.com",
"http://localhost:8080"
]
CORS_ALLOW_CREDENTIALS = True
CSRF_TRUSTED_ORIGINS = CORS_ALLOWED_ORIGINS.copy()
前端axios配置:
javascript复制axios.defaults.withCredentials = true
axios.defaults.xsrfCookieName = 'csrftoken'
axios.defaults.xsrfHeaderName = 'X-CSRFToken'
6. 部署实践
6.1 容器化方案
Docker-compose配置:
yaml复制version: '3.8'
services:
redis:
image: redis:6
ports: ["6379:6379"]
db:
image: mysql:5.7
environment:
MYSQL_ROOT_PASSWORD: survey1234
MYSQL_DATABASE: survey_db
volumes:
- mysql_data:/var/lib/mysql
web:
build: .
command: gunicorn survey.wsgi:application --bind 0.0.0.0:8000
volumes:
- .:/code
ports:
- "8000:8000"
depends_on:
- redis
- db
volumes:
mysql_data:
6.2 性能监控
使用Prometheus+Grafana监控关键指标:
- API响应时间(P99 < 300ms)
- 数据库查询耗时
- 内存使用情况
配置示例:
python复制# prometheus_client监控
from prometheus_client import start_http_server, Counter
API_REQUESTS = Counter('api_requests_total', 'Total API requests')
@api_view(['POST'])
def submit_survey(request):
API_REQUESTS.inc()
# 业务逻辑
这套系统在部署到校园服务器后,平均响应时间控制在200ms以内,即使在高峰期也能保持稳定服务。通过这次开发,我总结了三点重要经验:
- 对于表单类应用,前端状态管理要尽可能本地化,减少不必要的网络请求
- Django的ModelSerializer能节省大量时间,但复杂场景需要自定义Serializer
- WebSocket连接要注意心跳维护,避免意外断开影响用户体验
项目源码已整理成完整可运行的版本,包含详细的部署文档和测试数据。对于想要学习全栈开发的同学,这个项目涵盖了从数据库设计到前端优化的完整链路,特别适合作为毕业设计或实战练习。