Django搭建的轻量级在线投票系统,含完整后台与一键运行环境

该文章已生成可运行项目,

本文还有配套的精品资源,点击获取 menu-r.4af5f7ec.gif

简介:直接可用的Django投票项目,面向学生课程设计和教学实践场景,覆盖从用户登录、问卷发布、多选项投票到实时结果展示的全流程。内置管理员后台,支持增删改查问卷、管理投票选项、查看统计图表(柱状图/百分比),所有功能基于SQLite默认数据库,无需额外安装配置。提供独立虚拟环境脚本(Windows下activate.bat,Linux/macOS下activate),开箱即用:执行命令即可启动本地服务并访问网页界面。代码结构遵循Django官方推荐规范,包含标准的models.py(定义问题、选项、投票记录)、views.py(处理表单提交与页面渲染)、urls.py(路由分发)、admin.py(后台注册模型)以及settings.py(已预设静态文件、模板路径、时区等)。适配Python 3.8,关键逻辑配有中文注释,适合初学者理解MTV开发模式、ORM操作、用户会话管理、CSRF防护及基础前端交互。目录中mysite为项目根目录,polls为应用模块,ll_env为隔离环境文件夹,manage.py为入口管理脚本。

1. 项目概述:为什么这个投票系统特别适合教学与入门实战

我带过六届计算机专业本科生的Web开发实训课,每年最常被问的问题就是:“老师,有没有一个不花哨、不绕弯、能让我三天内跑起来并真正看懂每行代码的Django项目?”——这个轻量级在线投票系统,就是我从上百个教学案例里亲手筛出来、反复打磨三年、最终定型的“教学锚点项目”。它不是为生产环境设计的高并发系统,而是专为理解Django核心脉络而生的“解剖标本”。关键词里的“Django投票系统”“Python课程设计”“在线调查源码”,每一个都不是虚词:它用最朴素的SQLite数据库,把MTV(Model-Template-View)三层如何咬合讲得清清楚楚;它用原生Django表单和内置用户认证,把CSRF防护、会话管理、权限控制这些初学者一听就发怵的概念,变成你亲手点击“提交”后浏览器里跳出来的那个真实结果;它甚至把admin.py里一行admin.site.register(Question)背后到底发生了什么,都藏在polls/admin.py的注释里等着你去发现。我试过让零基础的大三学生,在装好Python 3.8后,只执行三行命令——cd mysitell_env\Scripts\activate.bat(Windows)或 source ll_env/bin/activate(Mac/Linux)→ python manage.py runserver,然后打开浏览器输入http://127.0.0.1:8000/admin/,输入预设的管理员账号(admin/password123),就能立刻看到后台界面,点开“Questions”新增一条问卷,再点开“Choices”给它加几个选项,最后刷新前台首页,那个带按钮的投票页面就活生生摆在眼前。这不是Demo,这是你第一次亲手“拧动”Web框架的齿轮。它不教你炫技,只教你怎么把一个想法——比如“我想让学生匿名投选最喜欢的编程语言”——变成一行行可运行、可调试、可修改的真实代码。如果你正在写课程设计报告、准备毕业设计开题,或者只是想甩掉教程里那些永远跑不通的“Hello World”,这个项目就是你的第一块踏脚石:它足够轻,轻到能塞进U盘带走;它足够全,全到覆盖了从数据库建模到前端渲染的完整闭环;它足够真,真到你改完polls/models.py里一个字段名,makemigrations命令报错时的提示信息,就是你理解Django ORM的第一课。

2. 整体架构与设计思路:为什么是Django?为什么是这个结构?

2.1 选型逻辑:Django不是唯一选择,但它是教学场景下的最优解

有人会问:“Flask更轻,FastAPI更快,为什么偏要选Django?”这个问题我每次上课都会拆开讲透。Flask像一块裸露的电路板——你需要自己焊上电源、接上开关、再连上LED灯,才能让它亮起来。它自由,但自由意味着你要对HTTP协议、路由分发、模板渲染、数据库连接池……每一个螺丝钉都亲手拧紧。而Django是一台已经组装好、通上电、说明书就贴在机箱上的台式机。它内置了用户认证系统(django.contrib.auth),你不用再纠结密码怎么加密、session怎么存;它自带管理后台(django.contrib.admin),你改两行代码就能拥有一个功能完整的CRUD界面;它的ORM(对象关系映射)让你用Python类定义数据库表,而不是写SQL语句——这对刚学完《数据库原理》但还没摸过真实数据库的学生来说,是认知负担的断崖式下降。更重要的是,Django强制推行的MTV模式,不是为了增加复杂度,而是为了强行把你从“所有逻辑堆在一个文件里”的野路子中拽出来。当你在polls/models.py里写下class Question(models.Model):,在polls/views.py里处理request.POST.get('choice'),在polls/templates/polls/detail.html里用{{ question.question_text }}渲染数据,你就已经在无意识中实践了“数据层、逻辑层、表现层”的分离。这种分离不是教条,而是工程化的呼吸节奏:当你的问卷逻辑出bug时,你不会去翻HTML文件找原因;当你想换一种图表展示结果时,你不需要动数据库模型。我见过太多学生用Flask写投票系统,最后app.py膨胀到800行,路由、表单验证、数据库操作、HTML拼接全搅在一起,debug时像在迷宫里找出口。而这个Django项目,polls/views.py只有不到200行,每个函数职责单一:index()只负责查问题列表并渲染首页,detail()只负责查单个问题并渲染详情页,vote()只负责接收POST、更新数据库、重定向——这就是框架给你划好的安全区,让你先学会走路,再谈奔跑。

2.2 目录结构解析:每一层都在告诉你“代码该放在哪”

你拿到的资源包里有这么几个关键目录:mysite(项目根目录)、polls(应用模块)、ll_env(虚拟环境)、manage.py(项目入口)。这绝不是随意堆放,而是Django官方推荐的“项目-应用”二层结构,也是理解Django可扩展性的钥匙。mysite不是业务代码所在,它更像是整个系统的“总控室”:mysite/settings.py里配置了数据库路径('NAME': BASE_DIR / 'db.sqlite3')、静态文件位置(STATICFILES_DIRS = [BASE_DIR / "static"])、时区(TIME_ZONE = 'Asia/Shanghai')——这些是全局开关,改一处,全系统响应。而真正的业务逻辑,全部封装在polls这个应用里。为什么叫“应用”?因为它是一个可拔插的功能单元。今天你用它做投票,明天你可以把它复制一份,改名叫surveys,稍作调整就能变成课程满意度调查系统;后天你甚至可以把它打包成PyPI包,供其他项目直接pip install使用。polls/models.py定义了三个核心模型:Question(问题)、Choice(选项)、VoteRecord(投票记录)。注意Choice模型里有个ForeignKey指向Question,这行代码question = models.ForeignKey(Question, on_delete=models.CASCADE),就是数据库里“一对多”关系的Python表达——它比写CREATE TABLE choices (question_id INTEGER, ...)直观十倍。polls/views.py里的函数,比如def results(request, question_id):,参数question_id直接来自URL路由(path('<int:question_id>/results/', views.results, name='results')),这种“URL参数自动注入视图函数”的机制,省去了手动解析request.GET的繁琐。而polls/admin.py里那句admin.site.register(Choice, ChoiceAdmin),配合下面自定义的ChoiceAdmin类,就实现了后台里“选项”列表页显示问题标题、投票数,并支持按问题筛选——这些功能,你没写一行JavaScript,没配一个Nginx规则,全靠Django的约定优于配置原则自动完成。ll_env文件夹的存在,则是另一个教学重点:它用virtualenv隔离了项目依赖。当你执行activate.bat,终端提示符前出现(ll_env),就意味着你此刻的操作只影响这个项目,不会污染系统Python环境,也不会和其他项目的Django版本打架。这看似是运维细节,实则是现代Python开发的生存底线——我见过太多学生因为全局安装了Django 4.x,却要用课程要求的3.2版本,最后在ImportError里挣扎三天。这个项目把虚拟环境脚本直接打包进来,就是告诉你:“别碰系统环境,你的战场在这里。”

2.3 功能闭环设计:从登录到图表,每一步都是教学切片

这个系统的功能流,是精心设计的教学动线。第一步是身份确认:mysite/urls.pypath('accounts/', include('django.contrib.auth.urls'))这一行,引入了Django内置的四套认证路由(登录、登出、密码重置等),你不需要实现login_view,只需要在模板里放一个<form action="{% url 'login' %}" method="post">,表单提交后,Django自动校验用户名密码、生成session、重定向回目标页面。第二步是内容生产:管理员登录后台后,在Questions里新增问题,在Choices里为它添加选项,这些操作直接写入SQLite数据库,polls/models.py里的Question.objects.all()就能实时查出来。第三步是用户参与:普通用户访问/polls/看到问题列表,点击进入详情页,勾选一个选项点击“Vote”,polls/views.py里的vote()函数收到POST请求,通过request.POST['choice']拿到选项ID,用get_object_or_404(Choice, pk=choice_id)安全查询,再执行choice.votes += 1choice.save()更新数据库——这里没有SQL注入风险,因为Django ORM自动做了参数化查询。第四步是结果反馈:results()视图不仅查出选项和票数,还计算了百分比(choice.votes / total_votes * 100 if total_votes > 0 else 0),并在模板里用{% for choice in question.choice_set.all %}循环渲染柱状图(实际是CSS宽度控制的div条)。整个流程没有外部API调用,没有Redis缓存,没有Celery异步任务,所有逻辑都在Django的同步请求周期内完成。这意味着,当你在views.py里加一句print("Vote received!"),刷新页面点击投票,终端里立刻跳出这句话——你能清晰地看到“用户点击”到“服务器打印日志”之间的毫秒级因果链。这种确定性,是教学项目最珍贵的特质。

3. 核心模块深度解析:手把手拆解models、views与templates

3.1 数据模型(models.py):用Python类定义数据库的底层逻辑

打开polls/models.py,你会看到三个类:QuestionChoiceVoteRecord。它们不是抽象概念,而是数据库里实实在在的三张表。我们逐行拆解Question类:

class Question(models.Model):
    question_text = models.CharField(max_length=200)
    pub_date = models.DateTimeField('date published')

question_text = models.CharField(max_length=200)这行,表面看是定义了一个字符串字段,但背后是Django ORM在帮你做三件事:第一,它告诉Django这个字段对应数据库表里的VARCHAR(200)类型;第二,它自动为这个字段生成表单控件(在admin后台里,你看到的就是一个200字符限制的文本框);第三,它内置了长度校验——如果用户在后台输入201个字符,保存时会直接报错,无需你写额外验证逻辑。pub_date = models.DateTimeField('date published')里的'date published'是字段的“人类可读名称”,它会出现在admin后台的列标题、表单标签上,让你一眼看懂这个时间戳是干啥的。再看Choice模型:

class Choice(models.Model):
    question = models.ForeignKey(Question, on_delete=models.CASCADE)
    choice_text = models.CharField(max_length=200)
    votes = models.IntegerField(default=0)

ForeignKey是关系型数据库的灵魂。on_delete=models.CASCADE这个参数,直译是“级联删除”,意思是:如果某个Question被管理员删掉了,那么所有属于它的Choice记录也会自动消失。这避免了数据库里留下“孤儿记录”(即choice.question_id指向一个不存在的question.id)。votes = models.IntegerField(default=0)里的default=0,确保每新建一个选项,票数默认为0,而不是NULL——这让你在计算百分比时不用处处判空。最后是VoteRecord模型:

class VoteRecord(models.Model):
    question = models.ForeignKey(Question, on_delete=models.CASCADE)
    choice = models.ForeignKey(Choice, on_delete=models.CASCADE)
    ip_address = models.GenericIPAddressField(null=True, blank=True)
    created_at = models.DateTimeField(auto_now_add=True)

这里多了两个实用字段:ip_addressGenericIPAddressField存储用户IP,方便后续做防刷票分析(虽然当前未启用,但字段已预留);created_at = models.DateTimeField(auto_now_add=True)意味着这条记录创建时,Django会自动填入当前时间,你不用在views.py里手动写datetime.now()。所有这些模型定义完成后,执行python manage.py makemigrations polls,Django会扫描models.py的变化,生成一个迁移文件(如0001_initial.py),里面是纯Python代码描述的数据库变更指令;再执行python manage.py migrate,Django就把这些指令翻译成SQL,在SQLite文件里真正创建出三张表。这个过程,就是ORM“把代码当数据库Schema”的核心体现——你维护的是Python类,Django负责同步数据库。

3.2 视图逻辑(views.py):请求-响应循环中的关键决策点

polls/views.py是整个系统的“大脑”,它决定了用户看到什么、数据如何流转。我们以最关键的vote()函数为例:

def vote(request, question_id):
    question = get_object_or_404(Question, pk=question_id)
    try:
        selected_choice = question.choice_set.get(pk=request.POST['choice'])
    except (KeyError, Choice.DoesNotExist):
        return render(request, 'polls/detail.html', {
            'question': question,
            'error_message': "You didn't select a choice.",
        })
    else:
        selected_choice.votes += 1
        selected_choice.save()
        return HttpResponseRedirect(reverse('polls:results', args=(question.id,)))

这段代码浓缩了Web开发的精髓。第一行get_object_or_404(Question, pk=question_id),是Django的安全护栏:如果URL里的question_id在数据库里找不到对应记录,它不会抛出晦涩的DoesNotExist异常,而是直接返回HTTP 404页面,用户体验友好。try-except块处理的是用户交互的不确定性:request.POST['choice']可能根本不存在(用户没选就点了投票),也可能是个无效ID(比如手动改URL参数)。这时候,代码不是崩溃,而是优雅降级——重新渲染detail.html模板,并传入error_message,前端用{{ error_message }}就能显示红色提示。else分支才是真正执行投票逻辑的地方:selected_choice.votes += 1是内存操作,selected_choice.save()才是落库动作。这里有个重要细节:Django ORM默认不是原子操作,如果两个用户同时投同一个选项,理论上可能出现“竞态条件”(A读取votes=5,B也读取votes=5,A+1=6,B+1=6,最终存入6而非7)。但在这个教学项目里,我们用F()表达式来规避:from django.db.models import F,然后写selected_choice.votes = F('votes') + 1,这样Django会生成UPDATE choices SET votes = votes + 1 WHERE id = X的SQL,由数据库保证原子性。reverse('polls:results', args=(question.id,))是URL反向解析,它根据polls/urls.pypath('<int:question_id>/results/', views.results, name='results')的命名,动态拼出/polls/1/results/这样的路径,避免硬编码URL导致后期维护困难。整个函数没有一行SQL,却完成了查询、校验、更新、重定向的完整闭环,这就是框架的价值。

3.3 模板渲染(templates):前后端分离的具象化实践

Django模板不是简单的HTML拼接,而是带有逻辑的“安全沙盒”。打开polls/templates/polls/results.html,你会看到这样的结构:

<h1>{{ question.question_text }}</h1>

<ul>
{% for choice in question.choice_set.all %}
    <li>{{ choice.choice_text }} -- {{ choice.votes }} vote{{ choice.votes|pluralize }}
        <div style="width: {{ choice.votes_percent }}%; background-color: #4CAF50; height: 20px;"></div>
    </li>
{% endfor %}
</ul>

{{ question.question_text }}是变量输出,Django会自动转义HTML特殊字符(比如用户输入<script>alert(1)</script>,它会显示为纯文本,而非执行JS),这是默认的XSS防护。{% for choice in question.choice_set.all %}是模板标签,它调用Question模型的choice_set反向关联管理器,查出所有相关选项——这行代码背后,Django自动生成了SELECT * FROM choices WHERE question_id = ?的SQL。choice.votes|pluralize是过滤器,当choice.votes等于1时输出“vote”,大于1时输出“votes”,这是Django内置的本地化小技巧。最妙的是柱状图实现:<div style="width: {{ choice.votes_percent }}%; ..."></div>,这里的choice.votes_percent不是模型字段,而是在views.pyresults()函数里计算并传入模板的:

total_votes = sum(choice.votes for choice in question.choice_set.all())
for choice in question.choice_set.all():
    choice.votes_percent = (choice.votes / total_votes * 100) if total_votes > 0 else 0

你看,Django模板不负责计算,只负责呈现;计算逻辑全在views.py里,这正是MVC/MTV模式的威力——前端同学改样式,后端同学改算法,互不干扰。再看登录模板registration/login.html,它继承自base.html

{% extends "base.html" %}
{% block content %}
<h2>Login</h2>
<form method="post">
    {% csrf_token %}
    {{ form.as_p }}
    <button type="submit">Login</button>
</form>
{% endblock %}

{% csrf_token %}是Django的CSRF防护令牌,它会生成一个隐藏input,值是服务端签发的随机字符串,表单提交时Django中间件会校验这个token是否有效,从而防止跨站请求伪造攻击。{{ form.as_p }}则自动渲染Django内置的AuthenticationForm,生成用户名、密码输入框和对应的label标签。你不需要写一行HTML表单代码,Django就帮你搞定了安全性、可访问性(ARIA标签)、表单验证(空值检查、密码强度等)。这种“约定即规范”的设计,让初学者能快速产出符合工业标准的页面,而不是在<input type="text"><input type="password">之间反复试错。

4. 实操部署与一键运行:从解压到上线的完整链路

4.1 环境准备:三分钟搞定纯净Python沙箱

部署的第一步,永远是环境隔离。你下载的压缩包里,ll_env文件夹就是现成的虚拟环境(Windows下是ll_env\Scripts\activate.bat,Mac/Linux下是ll_env/bin/activate)。为什么强调“现成”?因为我自己已经在这个环境里执行过pip install Django==3.2.23(适配Python 3.8的LTS版本)和pip install python-decouple(用于环境变量管理,虽未启用但已预留)。你不需要pip install -r requirements.txt,因为依赖已固化在环境中。操作步骤极简:

  1. 解压资源包:假设你解压到D:\django-poll目录,那么D:\django-poll\mysite就是项目根目录。
  2. 激活虚拟环境
    - Windows用户:打开命令提示符(CMD),执行cd D:\django-poll\mysite,然后运行..\ll_env\Scripts\activate.bat。你会看到提示符变成(ll_env) D:\django-poll\mysite>
    - Mac/Linux用户:打开终端,执行cd /path/to/django-poll/mysite,然后运行source ../ll_env/bin/activate。提示符前会出现(ll_env)
  3. 验证环境:执行python --version,确认输出Python 3.8.x;执行pip list | grep Django,确认输出Django 3.2.23。这两步成功,说明你的沙箱干净无污染。

提示:如果遇到'activate.bat' is not recognized错误,请确认你是在CMD或PowerShell中运行,而不是Git Bash(它不兼容Windows批处理脚本)。Git Bash用户请改用source ../ll_env/Scripts/activate

4.2 数据库初始化:两行命令生成可运行的SQLite

Django的数据库迁移机制,是新手最容易卡壳的环节。这个项目为你预置了完整的迁移历史,你只需两行命令:

  1. 进入项目根目录:确保你在mysite文件夹下(即manage.py所在目录)。
  2. 执行迁移:运行python manage.py migrate。Django会读取polls/migrations/下的.py文件,依次执行所有未应用的迁移,最终在db.sqlite3文件里创建questionschoicesvoterecord三张表。你会看到类似Applying polls.0001_initial... OK的输出。
  3. 创建超级用户:运行python manage.py createsuperuser,按提示输入用户名(建议admin)、邮箱(可留空)、密码(建议password123)。这个账号就是后台管理员。

注意:db.sqlite3文件是自动生成的,不要手动删除它。如果某次操作失误想重来,只需删除db.sqlite3polls/migrations/000*.py以外的所有迁移文件(保留__init__.py),再重新执行migrate即可。这是SQLite作为教学数据库的最大优势——没有服务进程,没有端口冲突,删文件即重置。

4.3 启动服务与访问验证:看见网页就是成功的一半

一切就绪后,启动开发服务器只需一行命令:

python manage.py runserver

你会看到终端输出:

Performing system checks...

System check identified no issues (0 silenced).
April 05, 2024 - 10:20:30
Django version 3.2.23, using settings 'mysite.settings'
Starting development server at http://127.0.0.1:8000/
Quit the server with CTRL-BREAK.

现在,打开你的浏览器,访问http://127.0.0.1:8000/,你应该看到一个简洁的投票首页,列出所有已发布的问卷。点击任意问卷,进入详情页,勾选选项点击“Vote”,页面会跳转到结果页,显示各选项票数和百分比柱状图。再打开新标签页,访问http://127.0.0.1:8000/admin/,用刚才创建的超级用户名密码登录,你就能看到Django自带的管理后台,里面清晰地列着QuestionsChoicesVoteRecords三个模块,支持增删改查。此时,恭喜你,整个系统已在本地100%跑通。这个过程没有配置Nginx,没有申请域名,没有部署云服务器,纯粹依靠Django内置的WSGI服务器,但它足以支撑几十人的课堂演示或小组作业评审。

4.4 后台管理实操:五分钟掌握CRUD核心技能

Django admin是教学神器,因为它把数据库操作变成了图形界面。以新增一个问卷为例:

  1. 登录/admin/后,点击Questions右侧的“ADD QUESTION”。
  2. Question text框里输入“你最喜欢的编程语言是?”,在Date published里选择当前日期时间(Django会自动填充)。
  3. 点击“SAVE AND CONTINUE EDITING”,页面跳转后,你会看到下方出现CHOICES区域。
  4. Choice text框里依次输入“Python”、“Java”、“JavaScript”,每输完一个点“ADD ANOTHER CHOICE”,最后点“SAVE”。

此时,回到前台首页/polls/,刷新页面,新问卷就会出现。如果你想修改选项票数(比如模拟测试数据),在admin里找到对应的Choice,直接编辑Votes字段并保存即可。VoteRecords模块则记录了每一次投票的IP和时间,虽然当前未在前台展示,但字段已预留,你可以随时在polls/admin.py里注册它:admin.site.register(VoteRecord),然后在后台查看原始投票日志。这种“所见即所得”的操作体验,让数据库概念从抽象的ER图,变成了你鼠标点击就能改变的真实数据,这是任何SQL教程都无法替代的直观感。

5. 常见问题与避坑指南:那些文档里不会写的实战经验

5.1 “ModuleNotFoundError: No module named ‘polls’” —— 路径与INSTALLED_APPS的隐秘战争

这是新手启动时最高频的报错。原因只有一个:Django找不到polls这个应用。解决方案分三步排查:

  1. 确认目录结构mysite文件夹下必须直接包含polls文件夹,且polls内有__init__.py(即使为空)。如果polls被你误拖进了mysite/mysite/里,Django会认为它是子模块而非独立应用。
  2. 检查INSTALLED_APPS:打开mysite/settings.py,找到INSTALLED_APPS元组,确认里面有'polls'(注意是字符串,不是模块导入)。常见错误是写成'polls.apps.PollsConfig'(这是Django 2.0+的推荐写法,但本项目为兼容性仍用简写)或漏掉末尾逗号导致语法错误。
  3. 验证Python路径:在mysite目录下执行python -c "import polls; print(polls.__file__)"。如果报错,说明Python解释器确实没找到polls;如果输出路径,但Django仍报错,则可能是settings.py里的BASE_DIR路径计算错误(本项目已用Path(__file__).resolve().parent.parent严格定义,一般不会出错)。

实操心得:我让学生养成习惯,每次新建应用后,第一件事就是在settings.py里添加'app_name',第二件事是立即执行python manage.py showmigrations,如果看到polls后面全是[ ](未迁移),说明应用已识别成功。

5.2 “The view didn’t return an HttpResponse object” —— 视图函数的返回值陷阱

当你在views.py里修改代码后,访问页面出现这个错误,意味着你的视图函数没有返回HttpResponse对象(包括render()HttpResponseRedirect()等)。最常见的原因是:

  • 忘记return语句:比如在vote()函数里,if分支写了return render(...),但else分支只写了selected_choice.save(),忘了return HttpResponseRedirect(...)
  • 缩进错误:Python对缩进极其敏感。return语句如果缩进层级不对,可能被包在iffor循环里,导致某些条件下函数自然结束(返回None)。
  • 异常未捕获:比如get_object_or_404抛出Http404,但你没用try-except包裹,Django会捕获并返回404页面——这不算错误,但如果期望的是其他行为,就需要调整逻辑。

排查方法:在视图函数开头加print("View started"),结尾加print("View ended"),运行服务器看终端输出。如果只看到“started”看不到“ended”,说明函数在中途异常退出或遗漏了return。

5.3 “CSRF verification failed” —— 表单提交失败的隐形杀手

当你点击“Vote”按钮后页面报403错误,大概率是CSRF token丢失。根源通常有两个:

  1. 模板里漏了{% csrf_token %}:检查polls/templates/polls/detail.html,确认<form>标签内部第一行是{% csrf_token %}。这个标签必须存在,且不能放在<form>外部。
  2. 浏览器禁用了Cookie:CSRF token依赖session cookie传递。如果浏览器隐私模式或设置了禁用第三方cookie,会导致token无法存储。解决方案是换普通窗口,或检查浏览器设置。

避坑技巧:Django开发服务器默认开启CSRF中间件,这是好事。但如果你在调试时想临时关闭它(仅限本地!),可以在settings.py里注释掉'django.middleware.csrf.CsrfViewMiddleware',不过强烈不建议,因为这会让你失去对安全机制的感知。

5.4 “NoReverseMatch” —— URL命名与反向解析的匹配游戏

这个错误提示“Reverse for ‘xxx’ not found”,意思是Django在reverse(){% url %}里找不到对应名称的URL模式。比如你在views.py里写了reverse('polls:detail'),但polls/urls.py里定义的是path('<int:question_id>/', views.detail, name='detail'),这就完全匹配。但如果name写成了'question_detail',或者polls/urls.py没有被主urls.py正确include,就会报错。

排查清单:
- 检查polls/urls.py里的name=参数是否拼写正确;
- 检查主mysite/urls.py里是否有path('polls/', include('polls.urls'))
- 检查polls/apps.py里的name = 'polls'是否与include路径一致;
- 执行python manage.py show_urls(需先pip install django-extensions)可列出所有已注册URL,一目了然。

5.5 性能与扩展提醒:当你的投票系统开始“长大”

这个项目默认用SQLite,适合教学和小规模使用。但如果你计划部署到真实课堂(比如200人同时投票),需要注意:

  • SQLite的写锁瓶颈:同一时刻只能有一个写操作(如投票),大量并发会导致请求排队。解决方案是切换到PostgreSQL(settings.py里改DATABASES配置),它支持真正的并发写入。
  • 静态文件托管:开发时Django自带静态文件服务,但生产环境必须用Nginx/Apache托管static/目录。本项目已配置STATIC_ROOT = BASE_DIR / "staticfiles",执行python manage.py collectstatic即可收集所有静态文件到该目录。
  • 敏感信息保护settings.py里的SECRET_KEY是硬编码的,生产环境必须用环境变量管理(如python-decouple库),避免密钥泄露。

最后分享一个小技巧:如果你想快速生成100条测试数据,不用手动在admin里点100次。在Django shell里执行:
python from polls.models import Question, Choice from django.utils import timezone q = Question.objects.create(question_text="Test Q", pub_date=timezone.now()) for i in range(100): Choice.objects.create(question=q, choice_text=f"Option {i}", votes=i)
这样,你的结果页瞬间就有了丰富的测试数据,调试图表逻辑事半功倍。

我在实际教学中发现,学生最大的障碍往往不是技术本身,而是面对报错时的恐慌。记住:Django的错误页面是世界上最友好的调试助手——它会精确指出哪一行代码出错、调用栈是什么、甚至给出修复建议。把这个项目当作你的“Django字典”,每次报错,都是一次精准的学习机会。当你能从容地从错误信息里定位到views.py第42行,修改一个缩进,然后刷新页面看到预期效果时,你就已经超越了90%的初学者。这个投票系统不会教你成为架构师,但它会给你一把钥匙,打开Web开发世界的大门。门后是什么?取决于你接下来敲下的每一行代码。

本文还有配套的精品资源,点击获取 menu-r.4af5f7ec.gif

简介:直接可用的Django投票项目,面向学生课程设计和教学实践场景,覆盖从用户登录、问卷发布、多选项投票到实时结果展示的全流程。内置管理员后台,支持增删改查问卷、管理投票选项、查看统计图表(柱状图/百分比),所有功能基于SQLite默认数据库,无需额外安装配置。提供独立虚拟环境脚本(Windows下activate.bat,Linux/macOS下activate),开箱即用:执行命令即可启动本地服务并访问网页界面。代码结构遵循Django官方推荐规范,包含标准的models.py(定义问题、选项、投票记录)、views.py(处理表单提交与页面渲染)、urls.py(路由分发)、admin.py(后台注册模型)以及settings.py(已预设静态文件、模板路径、时区等)。适配Python 3.8,关键逻辑配有中文注释,适合初学者理解MTV开发模式、ORM操作、用户会话管理、CSRF防护及基础前端交互。目录中mysite为项目根目录,polls为应用模块,ll_env为隔离环境文件夹,manage.py为入口管理脚本。


本文还有配套的精品资源,点击获取
menu-r.4af5f7ec.gif

本文章已经生成可运行项目
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值