Python+Django实战|线上问卷与投票调研系统:自定义题型、问卷发布、链接分享、答卷收集、数据可视化、报表导出

一、项目背景与痛点

企业内部调研、校园学情调查、社团活动投票、公众意见征集、线上满意度测评等场景中,问卷投票是高频使用的信息收集方式。目前主流方案分为纸质问卷和第三方在线问卷平台,两类方式均存在明显短板,核心痛点如下:

  • 纸质问卷效率低下:线下发放、人工回收、手动统计数据,耗时耗力,样本量大时极易出现统计错误,且数据无法长期归档;
  • 第三方平台限制多:主流在线问卷工具充斥广告,免费版限制题目数量、答卷份数,高级统计、导出功能需付费开通;
  • 数据隐私存在风险:调研数据托管在第三方服务器,企业涉密调研、校园内部问卷存在数据泄露隐患,无法自主管控;
  • 题型灵活性不足:部分工具仅支持单选/多选,不兼容填空题、评分题、文本题,无法满足复杂调研需求;
  • 无精细化权限管理:多人协作编辑问卷、分区收集答卷的需求无法实现,问卷发布、截止时间难以统一管控;
  • 数据复盘不便:答卷数据仅简单展示,缺少多维度图表分析,也无法一键导出Excel用于线下归档、二次分析。

针对纸质问卷与商用在线平台的各类问题,本次基于Python+Django4.2+MySQL+Ajax+ECharts 搭建轻量化私有化问卷投票系统,支持多题型自定义、问卷编辑发布、链接分享、答卷在线收集、实时统计、图表可视化、报表导出全套能力。本项目开辟线上调研、表单收集 全新赛道,和往期所有项目业务、功能、代码完全无重合

二、核心目标与定位

本项目核心目标:搭建私有化、无广告的线上问卷投票平台,实现问卷分类管理→多题型自定义编辑→设置发布/截止时间→生成分享链接→用户在线答卷→后台自动收集数据→多维度图表统计→Excel报表导出完整闭环,替代纸质问卷与商用第三方平台,实现调研流程线上化、数据自主化、分析可视化

项目精准定位:轻量化表单调研系统,采用Django原生MVT架构,部署简单、资源占用小;划分超级管理员、问卷创建者、普通答卷用户三类角色,权限分级隔离;适配企业、高校、社团、线下门店等多类调研场景,主打题型灵活、部署免费、数据安全、统计全面。

核心设计理念:题型多样化、发布流程化、答卷轻量化、统计可视化、数据可导出,解决传统问卷低效、商用平台受限、数据不安全的核心问题。


三、整体技术方案

项目基于Django原生MVT分层架构开发,MySQL存储问卷、题目、选项、答卷、分类全量数据,Ajax实现页面无刷新编辑与提交,后端完成答卷数据聚合计算,ECharts生成饼图、柱状图等统计图表,支持Excel文件导出,自定义中间件实现权限拦截。整体分层架构流程图如下:

plain 复制代码
┌─────────────┐     ┌─────────────┐     ┌─────────────┐     ┌─────────────┐
│ 问卷编辑层  │────▶│ 状态配置层  │────▶│ 链接分享层  │────▶│ 答卷提交层  │
│ 分类选择、多题型新增、题目排序 │ 设置发布/截止时间、答题限制 │ 生成公开链接、二维码分享 │ 在线填写、答案提交、数据入库 │
       │                    │                    │                    │
       ▼                    ▼                    ▼                    ▼
┌─────────────┐     ┌─────────────┐     ┌─────────────┐
│ 数据聚合层  │────▶│ 可视化层  │────▶│ 报表导出层  │
│ 答卷汇总、选项占比、得分计算 │ 饼图/柱状图展示统计结果 │ 全量数据一键导出Excel │
       │                    │                    │
       ▼
┌─────────────────────────────────────────────────────────────────────────┐
│                          底层技术底座                                      │
│  - 后端框架:Python3.11 + Django4.2 原生MVT架构                            │
│  - 数据存储:MySQL 存储问卷、题目、选项、答卷全业务数据                    │
│  前端交互:Ajax 实现无刷新编辑、答题、状态切换                             │
│  数据计算:ORM聚合查询完成答题占比、平均分等统计指标                       │
│  可视化:ECharts 实现各类统计图表渲染                                      │
│  文件导出:Python处理Excel,实现答卷报表批量导出                           │
│  权限体系:自定义中间件,区分创建者、管理员、答卷用户权限                  │
│  时间管控:datetime 判定问卷是否在有效期内                                │
└─────────────────────────────────────────────────────────────────────────

完整技术栈清单

  • Web后端:Python 3.11、Django 4.2 原生MVT轻量化架构
  • 数据库:MySQL 8.0,结构化存储问卷、题目、选项、答卷数据
  • 异步交互:Ajax 完成页面无刷新编辑、答题操作
  • 数据统计:Django ORM 聚合函数,计算选项占比、平均分、答题人数
  • 可视化组件:ECharts 制作饼图、柱状图、折线图
  • 文件处理:openpyxl 库实现Excel答卷报表导出
  • 时间校验:datetime 模块管控问卷发布、截止时效
  • 权限控制:自定义中间件,实现多角色访问隔离

四、核心能力模块详解

1. 问卷分类管理模块

对问卷进行归类整理,方便批量维护与检索:

  • 自定义分类:管理员可创建调研、投票、测评、意见征集等分类标签;
  • 分类绑定:创建问卷时选择对应分类,实现精细化归档;
  • 分类筛选:后台可按分类快速筛选问卷,提升管理效率;
  • 分类统计:自动统计每个分类下问卷数量与答卷总量。

2. 多题型问卷编辑模块(核心亮点)

支持主流调研题型,灵活组合适配各类调研场景:

  • 丰富题型:包含单选题、多选题、单项评分题、文本题、下拉选择题五大常用题型;
  • 题目排序:支持上下拖拽调整题目顺序,自由排版问卷;
  • 选项维护:单选/多选可批量新增、删除选项;
  • 草稿保存:编辑过程可暂存草稿,避免内容丢失,后续继续编辑。

3. 问卷发布与时效管控模块

精细化配置问卷规则,管控发布与截止全周期:

  • 时间配置:自定义问卷开始时间、截止时间,超时自动关闭答题入口;
  • 答题限制:支持设置单人仅可作答一次,防止重复刷票、恶意提交;
  • 状态流转:草稿→待发布→已发布→已截止四状态自动切换;
  • 分享能力:发布后自动生成访问链接与简易二维码,支持对外传播。

4. 在线答卷模块

轻量化答题页面,操作简洁,适配PC端访问:

  • 页面自适应:问卷标题、说明、题目依次展示,排版清晰;
  • 答题校验:必填项未填写、选择项为空时给出提示,禁止空提交;
  • 重复拦截:根据用户账号判定,单人仅限提交一次答卷;
  • 提交反馈:答题完成后即时提示提交成功,展示简单汇总信息。

5. 答卷数据统计模块

自动汇总答题数据,量化调研结果:

  • 基础统计:统计总答卷数、有效答卷数、答题时长分布;
  • 选项分析:单选/多选题自动计算每个选项的选择人数、占比;
  • 评分统计:评分题自动计算平均分、最高分、最低分;
  • 文本汇总:集中展示所有填空题、文本题原始答案,便于人工查阅。

6. 数据可视化模块

将枯燥数据转为图表,直观展示调研结论:

  • 饼图:展示各选项占比,直观体现投票倾向;
  • 柱状图:对比不同选项、不同评分的选择人数;
  • 图表联动:切换题目自动刷新对应图表;
  • 图表自适应:适配页面大小,查看体验良好。

7. 答卷报表导出模块

支持离线归档与二次数据分析:

  • 全量导出:一键将所有答卷数据导出为Excel文件;
  • 字段完整:导出内容包含答题人、答题时间、每道题答案;
  • 分类导出:支持按时间段、答题状态筛选后再导出;
  • 格式规范:Excel表头清晰,可直接用于办公归档。

8. 权限管理模块

多角色分工,保障问卷与数据安全:

  • 创建者权限:编辑、发布、管理本人创建的问卷,查看统计数据;
  • 管理员权限:全量问卷管理、分类维护、全局数据查看;
  • 答卷用户:仅可正常答题,无编辑、后台访问权限;
  • 访问拦截:未发布、已截止的问卷禁止外部访问。

五、创新价值与亮点

  1. 全题型自定义编辑:整合单选、多选、评分、文本等多种题型,覆盖绝大多数调研场景,灵活性远超简易投票系统;
  2. 私有化部署数据安全:所有问卷与答卷数据存储在自有服务器,杜绝第三方平台数据泄露风险,适合企业涉密调研;
  3. 时效+重复双重限制:通过时间+账号双重控制,避免重复作答、超时答题,保证数据有效性;
  4. 统计+可视化+导出一体化:自动计算指标、生成图表、支持Excel导出,一站式完成调研全流程;
  5. 轻量化零广告:无弹窗、无功能限制,免费部署使用,长期运维成本极低。

六、应用前景与落地场景

  • 企业内部调研:员工满意度调查、制度意见征集、岗位测评、活动投票;
  • 校园教学场景:学情调查、课程评价、社团招新投票、校园活动征集;
  • 线下门店/机构:客户满意度问卷、服务体验调研、活动意向投票;
  • 社团/社群运营:内部投票、活动报名、意见收集;
  • 毕业设计/求职项目:表单调研类系统,题型动态渲染、数据统计为核心技术点,差异化明显。

七、完整代码结构示例

1. 项目整体目录结构

python 复制代码
django-questionnaire-system/
├── manage.py
├── questionnaire_project/     # 项目全局配置
│   ├── settings.py           # 数据库、Excel、静态资源、权限配置
│   ├── urls.py               # 全局路由分发
│   └── middleware.py         # 角色、问卷访问权限中间件
├── apps/                     # 模块化业务拆分
│   ├── user_auth/            # 用户登录、角色权限模块
│   ├── qn_category/          # 问卷分类管理模块
│   ├── qn_edit/              # 问卷创建、题目编辑、草稿模块
│   ├── qn_publish/           # 发布、时效、分享链接模块
│   ├── answer_submit/        # 在线答题、答案提交模块
│   ├── data_stat/            # 答卷统计、指标计算模块
│   ├── chart_view/           # 数据可视化图表封装模块
│   └── excel_export/         # Excel报表导出模块
├── core/                     # 公共工具类
│   ├── time_check.py         # 问卷时效校验工具
│   ├── stat_calc.py          # 占比、平均分计算工具
│   ├── excel_tool.py         # Excel生成与导出工具
│   └── answer_filter.py      # 答卷数据筛选工具
├── static/                   # 样式、ECharts、前端脚本
├── templates/                # 编辑页、答题页、统计页模板
├── media/                    # 临时文件、二维码存储
├── requirements.txt          # 项目依赖包
└── readme.md                 # 部署、使用文档
</pre>

### 2. 核心可运行代码片段
#### 示例1:问卷、题目、选项、答卷核心数据模型
```python
from django.db import models
from django.contrib.auth.models

# 题型枚举
QUESTION_TYPE = (
    ("single", "单选题"),
    ("multi", "多选题"),
    ("score", "评分题"),
    ("text", "文本题"),
)
# 问卷状态枚举
QUESTIONNAIRE_STATUS = (
    ("draft", "草稿"),
    ("publish", "已发布"),
    ("over", "已截止"),
)

class QnCategory(models.Model):
    """问卷分类模型"""
    name = models.CharField(max=50, verbose_name="分类名称")
    create_time = models.DateTimeField(auto_now_add=True)

    def __str__(self):
        return self.name

class Questionnaire(models.Model):
    """问卷主表"""
    title = models.CharField(max=100, verbose_name="问卷标题")
    desc = models.TextField(blank=True, verbose_name="问卷说明")
    category = models.ForeignKey(QnCategory, on_delete=models.CASCADE, verbose_name="所属分类")
    creator = models.ForeignKey(User, on_delete=models.CASCADE, verbose_name="创建人")
    start_time = models.DateTime(verbose_name="开始时间")
    end_time = models.DateTime(verbose_name="截止时间")
    status = models.CharField(max=10, choices=QUESTIONNAIRE_STATUS, default="draft", verbose_name="状态")
    create_time = models.DateTime(auto_now_add=True)

    def __str__(self):
        return self.title

class Question(models.Model):
    """题目模型"""
    qn = models.ForeignKey(Questionnaire, on_delete=models.CASCADE, verbose_name="所属问卷")
    title = models.CharField(max=200, verbose_name="题目内容")
    q_type = models.CharField(max=10, choices=QUESTION_TYPE, verbose_name="题型")
    sort = models.IntegerField(default=0, verbose_name="排序权重")
    is_required = models.BooleanField(default=True, verbose_name="是否必填")
    create_time = models.DateTime(auto_now_add=True)

class QuestionOption(models.Model):
    """题目选项模型"""
    question = models.ForeignKey(Question, on_delete=models.CASCADE, verbose_name="所属题目")
    option_text = models.CharField(max=100, verbose_name="选项内容")
    create_time = models.DateTime(auto_now_add=True)

class AnswerRecord(models.Model):
    """答卷总表"""
    qn = models.ForeignKey(Questionnaire, on_delete=models.CASCADE, verbose_name="所属问卷")
    answer_user = models.ForeignKey(User, on_delete=models.CASCADE, verbose_name="答题人")
    answer_time = models.DateTime(auto_now_add=True, verbose_name="答题时间")

class AnswerDetail(models.Model):
    """单题答案明细"""
    answer = models.ForeignKey(AnswerRecord, on_delete=models.CASCADE, verbose_name="所属答卷")
    question = models.ForeignKey(Question, on_delete=models.CASCADE, verbose_name="所属题目")
    option_ids = models.CharField(max=255, blank=True, verbose_name="选择项ID")
    text_content = models.TextField(blank=True, verbose_name="文本答案")
    score_num = models.IntegerField(null=True, blank=True, verbose_name="评分分数")
示例2:问卷时效校验工具(core/time_check.py)
python 复制代码
from datetime import datetime
from apps.qn_publish.models import Questionnaire

class QnTimeCheck:
    """问卷时效校验工具"""
    @classmethod
    def is_available(cls, qn_obj):
        """判断问卷是否可正常答题"""
        now = datetime.now()
        # 状态+时间双重校验
        if qn_obj.status != "publish":
            return False, "问卷暂未发布"
        if now < qn.start_time:
            return False, "问卷尚未开始"
        if now > qn.end_time:
            return False, "问卷已截止"
        return True, "正常可答题"
示例3:答卷提交核心视图
python 复制代码
from django.views import View
from django.contrib.auth.decorators import login_required
from django.utils.decorators import method_decorator
from django.shortcuts import redirect
from django.contrib import messages
from apps.qn_edit.models import Questionnaire, Question
from apps.answer_submit.models import AnswerRecord, AnswerDetail
from core.time_check import QnTimeCheck

@method_decorator(login_required, name="dispatch")
class AnswerSubmitView(View):
    def post(self, request):
        qn_id = request.POST.get("qn_id")
        try:
            qn = Questionnaire.objects.get(id=qn)
        except Questionnaire.DoesNotExist:
            messages.error(request, "问卷不存在")
            return redirect("index")

        # 校验问卷状态
        avail, msg = QnTimeCheck.is_available(qn)
        if not avail:
            messages.error(request, msg)
            return redirect("index")

        # 校验是否重复答题
        if AnswerRecord.objects.filter(qn=qn, answer_user=request.user).exists():
            messages.error(request, "您已完成该问卷,请勿重复提交")
            return redirect("index")

        # 创建总答卷记录
        answer_main = AnswerRecord.objects.create(qn=qn, answer_user=request.user)

        # 遍历题目保存答案(简化逻辑)
        question_list = Question.objects.filter(qn=qn).order_by("sort")
        for question in question_list:
            q_id = str(question.id)
            if question.q_type in ("single", "multi"):
                opt_ids = request.POST.getlist(f"opt_{q_id}")
                AnswerDetail.objects.create(
                    answer=answer_main,
                    question=question,
                    option_ids=",".join(opt_ids)
                )
            elif question.q_type == "score":
                score = request.POST.get(f"score_{q_id}")
                AnswerDetail.objects.create(answer=answer, question=question, score_num=score)
            elif question.q_type == "text":
                text = request.POST.get(f"text_{q_id}")
                AnswerDetail.objects.create(answer=answer, question=question, text_content=text)

        messages.success(request, "问卷提交成功,感谢参与!")
        return redirect("index")

八、总结与展望

本篇博客聚焦线上问卷、投票、调研 全新赛道,基于Python+Django搭建全功能问卷系统,和往期日志、天气、在线考试、图书、网盘、考勤、会议室、招聘、二手、美食、进销存、租赁等所有项目完全独立。项目核心技术包含动态题型渲染、时间时效校验、答卷数据聚合统计、ECharts可视化、Excel导出,业务贴合企业、校园、社群等高频调研场景,代码规范、可直接私有化部署。

该项目既适合学习Django动态表单设计、聚合查询、文件导出等核心知识点,同时作为毕业设计、简历实战项目,在表单类系统中具备很高的差异化竞争力。

后续迭代规划

  1. 新增匿名答题模式,保护答题人隐私,适配敏感调研场景;
  2. 新增问卷密码访问功能,仅持有密码的用户可进入答题页面;
  3. 支持问卷分页展示,题目数量过多时提升页面加载速度;
  4. 新增答题进度保存,支持中途退出、下次继续作答。

相关推荐
Cloud_Shy6181 小时前
解读《Effective Python 3rd Edition》:从练气到老魔(第五章 Item 36 - 39)
开发语言·人工智能·笔记·python
zmzb01031 小时前
Python课后习题训练记录Day128
开发语言·python
AIFQuant1 小时前
全球行情自动更新、多品种展示、性能优化实战指南
python·性能优化·金融·node.js·restful
蜂蜜黄油呀土豆1 小时前
ReWOO 与 Plan-and-Execute:解耦的规划
python·ai·大模型
去码头整点薯条ing1 小时前
某红书笔记接口逆向【x-s参数】
javascript·爬虫·python
xxie1237941 小时前
参数Parameter,形参Formal Parameter,实参Actual Argument
开发语言·python
love530love1 小时前
Hermes-Agent 本地化部署与详细交互式配置实战指南 [LM Studio + QQ ]
人工智能·windows·python·aigc·agent·hermes·hermes-agent
高洁011 小时前
人人可用的智能体来了
python·深度学习·机器学习·数据挖掘·知识图谱
装不满的克莱因瓶1 小时前
NLP中的卷积神经网络CNN——从图像卷积到文本特征提取的跨界应用
人工智能·pytorch·python·深度学习·神经网络·自然语言处理·cnn