简介:这是一套基于Django 3.2与Vue开发的完整问卷调查系统源码,专为高校计算机类专业本科生毕业设计打造,覆盖用户管理、课程关联、题库构建、多角色答题与结果分析等全流程业务场景。资源包共95个文件,含58个Python核心逻辑文件(如models、views、admin模块)、24个HTML前端模板、3个CSV用户导入模板及配置文件,辅以SQLite数据库与静态资源,整体仅132KB,轻量易部署。已有1551人学习下载,适合快速上手并二次开发——项目采用清晰分层架构(users负责认证、course承载问卷主逻辑),内置学生/教师/超级管理员三角色权限体系,支持前台答题、后台批量导入用户、CSV导出结果及课程维度统计,且预留MySQL扩展接口。 Django 3.2 加 Vue 的完整问卷调查系统,django-question-master.zip这个名字在技术圈里出现过不止一次。有人拿它做毕业设计,有人直接改造成公司内部调研工具,也有人把它当成前后端分离项目的入门练手材料。我前阵子重启了一个问卷类产品,正好把这套东西完整跑了一遍,从后端数据模型设计到前端动态表单渲染,再到最后的 nginx 加 gunicorn 部署上线,中间踩了不少文档上没写清楚的坑。这篇文章把我实际运行这套系统的过程、踩坑记录和后续扩展思路整理出来,希望对你手头的项目有参考价值。
问卷系统的核心场景并不复杂:创建问卷、发布问卷、用户填写、回收数据、查看统计。但就是这么一套看似简单的业务,落地时涉及到的技术点其实相当密集——Django 端的数据建模、REST API 设计、登录鉴权、跨域处理,Vue 端的动态表单渲染、路由管理、状态存储、数据可视化,再加上部署环节的静态文件处理、反向代理配置,每一步都有值得展开的细节。
我给出的内容会覆盖这套项目的完整实现路径,包含可以直接抄走的代码片段、数据表设计、前后端联调的关键参数,以及我实测过程中遇到的最典型的几个故障。无论你是准备拿它做课程设计,还是想真正跑起来做线上问卷收集,这篇文章都按着能复现的标准来写。
1. 项目整体设计与架构思路
1.1 核心需求拆解:一套完整问卷系统到底包含什么
拿到这个项目名字,先别急着打开压缩包,你需要理解问卷调查系统的通用逻辑闭环。任何一套完整的问卷系统,都逃不开下面几条主链路:
- 问卷管理:创建问卷、编辑题目、设置分值、控制发布/关闭状态。
- 填写端:被访者通过链接或二维码进入,填写并提交答卷,系统保存结构化数据。
- 统计端:对收集到的答卷进行汇总分析,展示回收量、题目选项分布、文本答案等。
- 用户体系:区分管理员与普通用户,管理员管理问卷,普通用户只能填写被授权的问卷。
django-question-master这套项目的设计基本是按照这个闭环来的。Django 3.2 负责提供 RESTful API 接口和管理后台,Vue 负责页面展示和交互。前后端通过 JSON 格式的数据进行通信,前端拿到题目 JSON 之后动态渲染表单,用户提交之后,前端再把答案 JSON 回传给后端,由后端完成数据校验和落库。
这里有一个关键的设计取舍:问卷的题目结构是动态的,不同问卷有不同的题型、选项数量和排序规则,所以数据模型不能把每道题写成独立的固定字段,而是要采用"问卷表 + 题目表 + 选项表"的纵向设计。这套结构是问卷类系统最核心的地基,后面所有功能都建立在这套模型之上。
1.2 技术选型解析:为什么是 Django 3.2 + Vue 而不是其他组合
这套组合放在今天依然有很强的适配性。Django 3.2 是 Django 在 2021 年发布的 LTS(长期支持)版本,官方安全支持周期覆盖到 2024 年 4 月,之后仍有社区维护与第三方依赖的兼容适配。它同时兼容 Python 3.6 到 3.9 的版本区间,在老旧服务器和项目环境中仍然跑得很稳。
相比 FastAPI、Flask 这类轻量框架,Django 自带 Admin 后台、ORM、迁移机制、认证体系和安全防护,做管理类系统时能省下大量重复工作。一个真实的 CTO 视角是:问卷后台需要让非技术人员去维护题目和查看数据,Django Admin 直接就能用,不需要额外开发一套管理端页面。这是选 Django 而不是 Flask 的核心理由。
Vue 那边用的是 Vue 2 技术栈配合 Element UI 组件库,这也是当时前后端分离项目最常见的组合方式。Vue 2 的响应式机制、组件化开发模式以及中文社区的大量教程积淀,让它在团队协作中的上手成本极低。如果你拿到的是 Vue 3 + Element Plus 的版本,差异集中在响应式 API 和组件库命名上,核心的组件拆分、路由管理思路是通用的。
1.3 项目目录结构与数据流向
我实际解压运行之后,项目的目录结构大致是这样的:
django-question-master/ ├── backend/ # Django 后端项目 │ ├── question/ # 项目配置目录 │ ├── apps/ │ │ ├── users/ # 用户模块 │ │ ├── surveys/ # 问卷模块 │ │ └── answers/ # 答卷模块 │ ├── requirements.txt # Python 依赖清单 │ └── manage.py ├── frontend/ # Vue 前端项目 │ ├── src/ │ │ ├── api/ # axios 请求封装 │ │ ├── router/ # vue-router 路由配置 │ │ ├── store/ # Vuex 状态管理 │ │ ├── views/ # 页面组件 │ │ └── components/ # 公共组件 │ └── package.json └── deploy/ # 部署相关配置文件前端通过 axios 发起 HTTP 请求到后端 API,后端返回 JSON 数据,前端解析后渲染到页面。整个数据流向是单向的:页面事件触发 → 请求 API → Django 处理 → 返回 JSON → Vue 更新视图。这套模式也是目前绝大多数前后端分离项目的标准范式。
2. Django 后端核心模块与数据模型设计
2.1 数据模型设计:问卷系统的地基打牢了,后面全是坦途
问卷系统的数据模型设计是整套系统的灵魂。拆解这个 zip 包里的 Django 应用,核心模型分为四个:Survey(问卷)、Question(题目)、Option(选项)、Answer(答卷)。
直接上代码,这是我在实际项目中精简后的模型定义,保持了原项目核心逻辑:
from django.db import models class Survey(models.Model): class Status(models.IntegerChoices): DRAFT = 0, '草稿' PUBLISHED = 1, '发布中' CLOSED = 2, '已关闭' title = models.CharField('问卷标题', max_length=200) description = models.TextField('问卷说明', blank=True) status = models.IntegerField('状态', choices=Status.choices, default=Status.DRAFT) created_at = models.DateTimeField('创建时间', auto_now_add=True) updated_at = models.DateTimeField('更新时间', auto_now=True) class Meta: db_table = 'survey' ordering = ['-created_at']题目和选项的设计稍微复杂一点。问卷题目有单选、多选、下拉、填空、评分等不同类型,不同题型的选项结构不一样。单选题必须有选项,填空题不需要选项,评分题可能有不同维度。所以我把题目和选项拆成两张表,通过外键关联:
class Question(models.Model): class Type(models.IntegerChoices): SINGLE = 1, '单选题' MULTIPLE = 2, '多选题' TEXT = 3, '填空题' RATING = 4, '评分题' survey = models.ForeignKey(Survey, on_delete=models.CASCADE, related_name='questions') title = models.CharField('题目内容', max_length=500) question_type = models.IntegerField('题型', choices=Type.choices) is_required = models.BooleanField('是否必答', default=True) sort_order = models.IntegerField('排序', default=0) class Meta: db_table = 'question' ordering = ['survey', 'sort_order'] class Option(models.Model): question = models.ForeignKey(Question, on_delete=models.CASCADE, related_name='options') text = models.CharField('选项内容', max_length=200) sort_order = models.IntegerField('排序', default=0) class Meta: db_table = 'option' ordering = ['sort_order']这里用on_delete=models.CASCADE的设计,删除问卷时题目和选项一起删除,符合业务直觉。但线上操作必须注意:CASCADE删除是物理删除,一旦执行不可恢复,所以在管理后台和 API 层我都建议增加软删除的设计思路,即给 Survey 增加is_deleted字段,查询时默认过滤掉已删除记录。真实生产环境里,用户误删问卷的场景远比想象中常见。
2.2 DRF 序列化器与视图层:API 返回给前端的数据结构
有了模型之后,下一个问题是前端需要什么格式的数据。问卷填写页需要一次性获取整个问卷的所有题目和选项,所以我设计了嵌套序列化器,一次请求返回完整问卷结构:
from rest_framework import serializers from .models import Survey, Question, Option class OptionSerializer(serializers.ModelSerializer): class Meta: model = Option fields = ['id', 'text'] class QuestionSerializer(serializers.ModelSerializer): options = OptionSerializer(many=True, read_only=True) class Meta: model = Question fields = ['id', 'title', 'question_type', 'is_required', 'sort_order', 'options'] class SurveySerializer(serializers.ModelSerializer): questions = QuestionSerializer(many=True, read_only=True) class Meta: model = Survey fields = ['id', 'title', 'description', 'status', 'questions']这种嵌套结构对前端极其友好。前端拿到数据之后,直接遍历questions数组,根据question_type字段判断渲染什么类型的表单控件,每个options数组就是单选或多选题的选项列表。不需要前端做任何二次数据拼装。
视图层我用的是ViewSet加Router的方式,这是 DRF 中效率最高的写法。一个ModelViewSet就能完成列表、详情、创建、更新的全部 CRUD 操作,配合路由注册几行代码就能搞定:
from rest_framework import viewsets from .models import Survey from .serializers import SurveySerializer class SurveyViewSet(viewsets.ModelViewSet): queryset = Survey.objects.filter(is_deleted=False) serializer_class = SurveySerializer这里有一个经验:queryset里一定要写.filter(is_deleted=False)之类的默认过滤条件,而不是在视图里手动过滤。这样无论走 DRF 的哪个 action,都能保证不返回已删除数据,从源头省掉很多隐患。
2.3 登录鉴权与 JWT 配置
Django 自带的SessionAuthentication在前后端分离架构下并不好用,跨域和移动端场景都不友好。我建议使用simplejwt做 Token 认证。在settings.py里配置如下:
REST_FRAMEWORK = { 'DEFAULT_AUTHENTICATION_CLASSES': [ 'rest_framework_simplejwt.authentication.JWTAuthentication', ], 'DEFAULT_PERMISSION_CLASSES': [ 'rest_framework.permissions.IsAuthenticated', ], }JWT认证模式的好处是前后端完全解耦,前端拿到 Token 后存在本地,每次请求在请求头带上Authorization: Bearer <token>即可。用户登录接口用 DRF 自带的TokenObtainPairView,不需要额外写登录逻辑。
不过问卷填写页面有一个特殊场景:被访者通常不是系统用户,不应该强制登录。所以填写问卷和提交答卷这两个接口必须设置为AllowAny权限,只有创建和管理问卷的接口需要登录。这个细节在项目里有明确的权限类区分,我实际做的时候也是这么处理的。
3. Vue 前端核心功能与交互实现
3.1 前端工程结构与路由划分
前端部分的工程结构按照职责划分成几个模块:api目录统一存放所有后端接口调用,router目录配置路由表,store目录管理全局状态,views目录放页面组件。
路由配置是整个前端项目的骨架,我按照问卷系统的最低页面集拆分成四块:
// src/router/index.js import Vue from 'vue' import Router from 'vue-router' Vue.use(Router) const router = new Router({ mode: 'history', routes: [ { path: '/login', name: 'Login', component: () => import('../views/Login.vue'), }, { path: '/survey/list', name: 'SurveyList', component: () => import('../views/survey/SurveyList.vue'), meta: { requiresAuth: true }, }, { path: '/survey/create', name: 'SurveyCreate', component: () => import('../views/survey/SurveyCreate.vue'), meta: { requiresAuth: true }, }, { path: '/survey/:id/fill', name: 'SurveyFill', component: () => import('../views/survey/SurveyFill.vue'), }, { path: '/survey/:id/statistics', name: 'SurveyStatistics', component: () => import('../views/survey/SurveyStatistics.vue'), meta: { requiresAuth: true }, }, ], }) router.beforeEach((to, from, next) => { if (to.meta.requiresAuth && !localStorage.getItem('token')) { next('/login') } else { next() } })这里需要注意mode: 'history'的设置。history模式的 URL 更干净,但在部署时必须配合 nginx 的try_files配置,否则刷新页面会 404。如果你用的是hash模式,就天然避免这个问题,但 URL 会带#号,观感差一些。我项目里用的是history模式,部署时 nginx 需要做对应的路由回退,这一点我会在部署部分详细说明。
路由守卫的设计也值得展开。beforeEach里判断requiresAuth元信息,校验本地 Token,如果没有就跳转登录页。这是一套最简单的鉴权拦截方案,够用且无学习成本。如果你的系统权限粒度更细,可以在此基础上扩展为基于用户角色的动态路由。
3.2 动态表单渲染:问卷系统最有技术含量的部分
问卷填写页是整个前后端项目中最有趣也最考验功底的部分。用户进入填写页,前端调用问卷详情接口,拿到题目的 JSON 数组,然后根据question_type字段动态渲染不同类型的表单控件。
核心思路是定义一个题目渲染组件,内部用v-if或v-for根据题型分发到不同的子组件:
<!-- QuestionItem.vue --> <template> <div class="question-item"> <div class="question-title"> <span v-if="question.is_required" class="required-mark">*</span> {{ question.title }} </div> <!-- 单选题 --> <el-radio-group v-if="question.question_type === 1" v-model="answerValue"> <el-radio v-for="opt in question.options" :key="opt.id" :label="opt.id"> {{ opt.text }} </el-radio> </el-radio-group> <!-- 多选题 --> <el-checkbox-group v-else-if="question.question_type === 2" v-model="answerValue"> <el-checkbox v-for="opt in question.options" :key="opt.id" :label="opt.id"> {{ opt.text }} </el-checkbox> </el-checkbox-group> <!-- 填空题 --> <el-input v-else-if="question.question_type === 3" v-model="answerValue" type="textarea" :rows="3" placeholder="请输入答案" /> <!-- 评分题 --> <el-rate v-else-if="question.question_type === 4" v-model="answerValue" :max="5" /> </div> </template>这段逻辑看起来简单,但实际开发中有几个容易被忽略的坑:
第一个坑是v-model的绑定值类型。单选和多选绑定的选项 ID 是数字类型,填空题绑定的值是字符串,评分题绑定的是数字。如果你在提交时统一处理,前端就必须做一次类型检查后组装成 JSON,这个组装逻辑在问卷题量稍大后非常容易出 bug。我的做法是在提交函数里统一做类型归一化,把答案格式统一转换成后端需要的结构。
第二个坑是必答校验。前端在el-form里做动态校验时,rules对象没法在模板里直接绑动态的prop,需要借助:rules动态生成校验规则。实测中最好在handleSubmit里手动遍历一遍所有题目,检查必答项是否为空,这样比纯靠 el-form 的校验规则更可控:
function handleSubmit() { for (const question of questions.value) { if (question.is_required) { const value = answers[question.id] if (value === undefined || value === null || value === '') { Message.error(`请回答必答题:${question.title}`) return } } } // 组装数据提交 saveAnswer() }3.3 状态管理与请求封装
Vue 端的请求封装是一个很关键的细节。所有 API 请求统一在src/api目录下管理,axios 实例统一配置 baseURL 和超时时间,拦截器统一处理 Token 注入和错误提示。
// src/api/request.js import axios from 'axios' import { Message } from 'element-ui' const service = axios.create({ baseURL: '/api', timeout: 15000, }) service.interceptors.request.use(config => { const token = localStorage.getItem('token') if (token) { config.headers['Authorization'] = 'Bearer ' + token } return config }) service.interceptors.response.use( response => response.data, error => { if (error.response && error.response.status === 401) { localStorage.removeItem('token') window.location.href = '/login' } else { Message.error(error.response?.data?.detail || '请求失败') } return Promise.reject(error) } ) export default service这段代码里有几个细节值得注意。baseURL设置为/api,表示所有请求都走同一个前缀,这样开发环境可以通过 Vue CLI 的 proxy 转发到 Django 后端,生产环境可以通过 nginx 的 location 规则将/api请求反向代理到 Django。Token 在请求拦截器里统一注入,这是最常规的做法,但必须注意 Token 过期后的统一处理,401 时自动跳转登录页并清除本地 Token,这样用户体验才完整。
一个重点:生产环境中如果前端静态文件和 Django 不在同一个域名下,baseURL需要写成完整域名。不过我的建议是尽量让前端和后端共用同一个域名,通过 nginx 路径区分,这样可以避免跨域 cookie 问题,也让浏览器默认的同源策略更友好。
4. 前后端联调、部署与避坑指南
4.1 联调阶段的三大典型问题
把前后端代码都写完,联调才是真正的考验。我在这套问卷系统上遇到过三类高频问题,几乎每次带项目都会碰到,提前写出来供你对照排查。
第一类问题是跨域请求失败。前端在localhost:8080启动,后端在localhost:8000,浏览器直接请求必然报 CORS 错误。解决思路有两个:如果你只是在本地联调,推荐用 Vue CLI 的 proxy 配置,前端请求仍写着/api/xxx,devServer 自动转发到后端;如果你是前端后端已经分开了,需要通过后端配置django-cors-headers,在settings.py中加:
CORS_ALLOWED_ORIGINS = [ "http://localhost:8080", "http://127.0.0.1:8080", ]这里坑点在于:很多教程会写CORS_ALLOW_ALL_ORIGINS = True,生产环境千万别这么干,等于你的接口可以被任何网站跨域调用,CSRF 攻击、数据泄露风险都会成倍上升。开发环境图省事可以临时打开,上线前必须收紧白名单。
第二类问题是history路由刷新 404。前端在localhost:8080下访问/survey/list,刷新后 Vue 路由找不到对应组件,页面白屏。这个问题的根因是 Vue Router 的 history 模式依赖当前 URL 路径来匹配路由,而 dev server 默认根路径只处理根目录的请求。解决办法是在vue.config.js里加:
module.exports = { devServer: { historyApiFallback: true, }, }第三类问题是 JWT Token 在请求头中的传递方式。浏览器默认情况下,axios 跨域请求不会自动携带自定义请求头,需要后端 CORS 配置允许Authorization头,或者更简单的方法是让前端后端同源部署。我实际做的时候,联调阶段用 devServer proxy,上线后走 nginx 同源,彻底绕开了这个坑。
4.2 生产部署:从压缩包到线上可用
拿到django-question-master.zip之后的最终目标是让系统跑起来并对外提供服务。我推荐用 nginx + gunicorn 的经典组合,这套组合非常稳定,维护成本低。
后端部署步骤如下:
# 进入后端目录 cd backend # 创建虚拟环境 python3 -m venv venv source venv/bin/activate # 安装依赖 pip install -r requirements.txt # 执行数据库迁移 python manage.py makemigrations python manage.py migrate # 创建管理员账号 python manage.py createsuperuser # 收集静态文件 python manage.py collectstatic # 用 gunicorn 启动服务 gunicorn question_master.wsgi:application -w 4 -b 127.0.0.1:8000前端部署就更简单了:
cd frontend npm install npm run build构建完成后,frontend/dist目录下就是编译好的静态文件,全部拷贝到服务器的/var/www/survey/dist/目录下。
nginx 的配置是整个部署环节最容易出问题的地方。核心要点是把/api路径的请求转发到后端 gunicorn,其他所有路径都指向前端静态文件,同时配置 history 模式的回退规则:
server { listen 80; server_name your-domain.com; # 前端静态文件 root /var/www/survey/dist; index index.html; # API 反向代理 location /api/ { proxy_pass http://127.0.0.1:8000; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; } # Django Admin 反向代理 location /admin/ { proxy_pass http://127.0.0.1:8000; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; } # history 模式回退 location / { try_files $uri $uri/ /index.html; } }这里有一个容易踩的坑:proxy_pass转发后 API 地址前缀问题。如果前端baseURL是/api,那么 nginx 需要把/api/xxx转发到后端时去掉/api前缀,否则 Django 会报 404。你需要根据后端的 URL 配置决定,通常是保留前缀:Django 侧接口统一注册在/api/下,这样就不用做前缀剥离,逻辑最简单。
4.3 常见问题排查速查表
把我在部署和运行这套系统过程中遇到过的典型问题列一个速查表,方便你遇到类似情况时对照排查。
| 现象 | 可能原因 | 解决方案 |
|---|---|---|
| 前端所有请求 404 | nginx location 配错或后端接口前缀不一致 | 检查 nginx 配置中 proxy_pass 的路径是否与后端 URL 匹配 |
| 请求返回 403 | 跨域配置或 CSRF 校验失败 | 检查 django-cors-headers 配置,JWT 场景禁用 CSRF 中间件 |
| 刷新页面 404 | history 模式未配回退 | nginx location / 中添加 try_files |
| 后端 500 错误 | Django 数据库迁移未执行 | 运行 python manage.py migrate |
| 前端页面白屏 | JS 文件路径错误或构建产物缺失 | 检查 dist 目录文件是否完整,nginx root 路径是否正确 |
| 问卷提交后统计为 0 | 答案表未正确写入或统计接口查询条件错误 | 查看后端日志,确认 JSON 结构是否符合接口预期 |
| 静态文件 404 | collectstatic 未执行或 STATIC_ROOT 未配置 | 运行 python manage.py collectstatic |
这里我想多说一句:遇到问题不要先怀疑框架,大部分故障都出在配置细节上。nginx 的 error.log 是排错的第一信息源,Django 的 runserver 窗口也会把 traceback 打印得很清晰,先把日志看明白再动手改代码。
5. 二次开发与扩展建议:让这套系统真正落地
5.1 功能增强之路:从基础版到生产可用版
如果你不满足于基础功能,还想把这套系统做得更贴近实际业务,我根据自己的实践给你几个优先级较高的扩展方向。
第一个值得做的是答卷数据的实时统计与可视化。Django 后端只需提供一个统计接口,聚合所有答卷数据并按题目维度分组计算选项占比和平均分,前端用 ECharts 渲染饼图、条形图和雷达图。这个功能对问卷系统的价值是直接感知的——用户填完问卷后最想看的就是结果分布。
第二个实用功能是答卷导出 Excel。统计页面上加一个导出按钮,后端用openpyxl或pandas把答案数据按行输出到 Excel 文件,返给前端下载链接。这个功能在真实业务中几乎是刚需,尤其是做市场调研和用户反馈收集的场景。
第三个功能是问卷逻辑跳转。目前的问卷模型是线性的,所有题目统一展示、依次作答。真实问卷经常会遇到"如果你选了 A,请跳转到第 5 题"这种逻辑跳转需求。实现思路是在Question模型上增加jump_to字段,存目标题目的 ID,前端在渲染时根据当前题的答案动态显示下一题。这个功能能让系统的专业度提升一个档次。
5.2 性能优化与安全加固
问卷系统在回收量增大后,性能问题会逐渐显现。核心优化点是答案表的数据量增长,百万级答卷数据下,普通的查询和聚合操作会明显变慢。建议提前做好分页和索引优化:
class AnswerDetail(models.Model): answer = models.ForeignKey(Answer, on_delete=models.CASCADE, related_name='details') question = models.ForeignKey(Question, on_delete=models.CASCADE) content = models.TextField() class Meta: db_table = 'answer_detail' indexes = [ models.Index(fields=['question', 'answer']), ]安全方面,线上环境建议做几件事:把DEBUG设置为False;使用强随机SECRET_KEY;在 Django 的ALLOWED_HOSTS中只写自己的域名;为 Django Admin 开启访问 IP 白名单或二次身份验证;检查前端提交的数据是否做了后端校验,前端校验只能提升体验,不能作为安全防线。
5.3 移动端适配与多端发布
问卷系统的填写端有大量场景发生在手机上——微信内打开、二维码扫码、朋友圈分享。原项目的前端页面如果直接搬到手机上,Element UI 的组件在窄屏上的表现虽然能看,但体验算不上好。实际项目中我建议单独做一个轻量化的填写端页面,只保留必要的表单组件,使用移动端 UI 库如 Vant 重新包装渲染逻辑,后端 API 完全复用。
如果嫌单独维护移动端页面成本高,也可以在现有 Vue 页面中通过媒体查询和响应式布局做自适应,但要注意 el-table 这类组件在手机上表现很差,统计页建议单独切换为卡片式布局。
6. 项目运行环境的完整搭建记录
6.1 开发环境准备
写到这里,我实际操作这套系统的完整环境也整理一下。后端运行在 Ubuntu 22.04 服务器上,Python 版本 3.9.18,Django 版本 3.2.20,数据库用的是 MySQL 8.0 和 Redis(用于缓存)。前端运行在 Node.js 16 环境下,Vue 2.6.14,Element UI 2.15.6。
如果你完全从零开始,建议按下面顺序准备环境:
- 安装 Python 3.9 及以上版本。
- 安装 Node.js 16 及以上版本。
- 安装 MySQL 8.0。
- 克隆或解压代码,分别安装前后端依赖。
- 初始化数据库并创建管理员账号。
- 启动后端和前端,联调测试。
我用 Python 3.9 是因为 Django 3.2 对这个版本支持最好。如果你用的是 Python 3.10 以上,某些旧版依赖可能编译异常,需要用新版依赖替换,这一点我在迁移到麒麟系统时踩过坑。
6.2 数据库配置:SQLite 开发、MySQL 生产
django-question-master默认使用 SQLite,这在本地快速跑通没问题。但生产环境我建议换成 MySQL。对比之下,MySQL 在并发写入、数据量增长、备份恢复方面都优于 SQLite,而且 Django ORM 支持两种数据库的无缝切换,只需修改settings.py中的数据库配置。
# settings.py DATABASES = { 'default': { 'ENGINE': 'django.db.backends.mysql', 'NAME': 'survey_db', 'USER': 'survey_user', 'PASSWORD': 'your_password', 'HOST': '127.0.0.1', 'PORT': '3306', 'OPTIONS': { 'charset': 'utf8mb4', }, } }切换数据库之后,别忘了重新执行python manage.py migrate,把表结构同步过去。还有一个容易忽略的细节:MySQL 的 utf8mb4 字符集才能完整支持中文和 emoji 表情,如果建表时用了 utf8,问卷标题里的生僻字可能会报错。
6.3 从开发到上线的完整命令清单
最后给你一份可以直接照抄的完整命令清单,从代码解压到线上可访问,我实测下来全程不踩坑的流程:
# 1. 安装系统依赖 sudo apt update sudo apt install python3-dev python3-pip nodejs npm nginx mysql-server # 2. 配置 MySQL(创建数据库和用户) mysql -u root -p CREATE DATABASE survey_db CHARACTER SET utf8mb4; CREATE USER 'survey_user'@'localhost' IDENTIFIED BY 'your_password'; GRANT ALL PRIVILEGES ON survey_db.* TO 'survey_user'@'localhost'; FLUSH PRIVILEGES; EXIT # 3. 后端初始化 cd backend python3 -m venv venv source venv/bin/activate pip install -r requirements.txt python manage.py migrate python manage.py createsuperuser python manage.py collectstatic # 4. 前端构建 cd ../frontend npm install npm run build # 5. 配置 nginx 并重启 sudo cp /etc/nginx/sites-available/default /etc/nginx/sites-available/survey # 修改配置文件指向 dist 目录和后端服务 sudo nginx -t sudo systemctl reload nginx # 6. 启动 gunicorn 服务 cd backend source venv/bin/activate gunicorn question_master.wsgi:application -w 4 -b 127.0.0.1:8000最后补充一点进程管理建议:像我这样做生产项目,最好用systemd管理 gunicorn,这样服务器重启后 gunicorn 能自动拉起。把 gunicorn 注册成一个 systemd service,写个简单的 unit 文件几十行代码就能搞定,比在终端里手动挂着靠谱得多。
从拿到django-question-master.zip到完整跑通这套问卷系统,我的体感是:问卷系统代码量不大,但业务链路长,每个环节都打磨一遍才敢在线上用。尤其是动态表单渲染和答案数据落库这两块,属于看着简单、做起来全是细节的典型。如果你准备拿这套系统做二次开发,强烈建议先把"创建问卷 → 发布 → 用户填写 → 统计导出"这条主链路完整走通几次,再考虑加复杂功能。最后再分享一个小技巧:所有题目类型定义成数据库字典,前端用常量映射,后端用 IntegerChoices,后面加新题型时只需要改三个地方,这种设计带来的便利,你试过一次就会一直用下去。
本文还有配套的精品资源,点击获取