资讯动态

Django投票系统实战:从环境配置到生产部署

发布时间:2026/9/10 5:52:16 来源:尧图企业网站定制
简介这是一套面向Python初学者与Web开发入门者的Django实战项目源码完整实现了一个功能完备的投票系统涵盖模型设计、视图逻辑、模板渲染及后台管理等核心模块。资源基于Django 2.1.2与Python 3.7.0构建适配PyCharm开发环境可直接运行学习MVC架构实践与典型Web应用开发流程。压缩包共36个文件包含14个Python源码如models.py、views.py、urls.py等关键模块、7个PNG界面截图含admin-login.png、index.png等真实页面效果、4个HTML模板文件、1个README.md项目说明文档以及XML配置、CSS样式与GIF动图等辅助资源整体大小5.67MB结构清晰、开箱即用。已有120人学习下载读者可获得从项目初始化django-admin startproject到功能迭代的完整目录组织范式、可运行的前后端代码、配套界面截图及本地部署指引是理解Django项目标准结构与快速上手Web开发的优质教学参考。1. 用 Django 快速搭一个可运行、可扩展、带管理后台的投票系统不是 demo是能上线的最小生产级骨架你下载了一个叫python开发基于Django的投票系统源码项目说明.zip的压缩包解压后看到manage.py、settings.py、polls/和requirements.txt——但直接python manage.py runserver却报错ModuleNotFoundError: No module named django或者页面打开后“Question does not exist”又或者管理员登录进不去后台。这不是代码写错了而是Django 投票系统天然依赖明确的环境隔离、应用注册顺序、数据库迁移时机和 URL 路由层级。它不像 Flask 那样靠一个文件就能跑通也不像静态页面那样丢到 Nginx 就能访问。这个项目本质是 Django 官方 Tutorial 的工程化延伸它把“创建问题→添加选项→记录投票→统计结果→后台管理”这一闭环封装成可独立部署、支持多用户、带权限控制、留有 API 扩展点的结构。适合刚学完 Django MTV 模式、想拿真实项目练手的开发者也适合需要快速交付内部调研/问卷/评选类轻量 Web 工具的运维、产品或测试人员——你不需要重写模型但必须理解models.py中Question与Choice的外键约束如何影响 admin 注册方式以及views.py里get_object_or_404和redirect的组合为什么比裸HttpResponse更安全。2. 从零复现用标准 Django 流程还原投票系统核心结构2.1 环境初始化与依赖安装避开 Python 版本和 pip 源导致的常见阻塞Django 对 Python 版本有明确要求。当前主流稳定版本 Django 4.2 要求 Python ≥ 3.8若你本地是 Python 3.7 或更低pip install django会默认装旧版如 3.2而项目requirements.txt中若指定Django4.2.0就会失败。先确认 Python 版本python --version # 输出应为 Python 3.8.10 或更高若版本不符推荐使用pyenv管理多版本Linux/macOS或直接下载 python.org 官方安装包Windows。接着创建虚拟环境并激活python -m venv venv_polls source venv_polls/bin/activate # Linux/macOS # venv_polls\Scripts\activate.bat # Windows提示绝对不要用sudo pip install django或全局安装。Django 项目强依赖环境隔离混装会导致ImportError: cannot import name patterns等静默崩溃。安装依赖时优先使用国内镜像源加速pip install -i https://pypi.tuna.tsinghua.edu.cn/simple/ -r requirements.txt典型requirements.txt内容应包含Django4.2.15 django-compressor4.4 Pillow10.2.0注意Django4.2.15是精确锁定版本避免因Django4.2导致升级到 5.x 后django.conf.urls.patterns等已移除 API 报错。若pip install过程中提示Failed building wheel for PillowLinux 用户需先安装系统依赖sudo apt-get install libjpeg-dev zlib1g-devUbuntu/Debian或brew install jpeg zlibmacOS。2.2 数据库迁移与初始数据加载让Question表真正落地Django 投票系统依赖两个核心模型Question问题和Choice选项定义在polls/models.py中。但模型只是 Python 类不执行迁移就不会生成数据库表。执行前先检查迁移状态python manage.py showmigrations # 输出类似 # polls # [ ] 0001_initial # [ ] 0002_auto_20240315_1020若全是[ ]说明尚未迁移。执行python manage.py makemigrations polls python manage.py migratemakemigrations会扫描polls/models.py变更生成polls/migrations/0001_initial.pymigrate则将该文件转换为 SQL 并执行。成功后验证表是否存在python manage.py dbshell # 进入 SQLite 命令行默认数据库输入 .tables # 应看到 polls_question、polls_choice 等表名注意若settings.py中DATABASES配置为 MySQL/PostgreSQL需确保服务已启动且账号密码正确。常见错误django.db.utils.OperationalError: (1045, Access denied for user...)表明settings.py中USER/PASSWORD未更新为实际值。为快速验证功能可加载初始测试数据。项目通常附带polls/fixtures/initial_data.json。加载命令python manage.py loaddata polls/fixtures/initial_data.json该 JSON 文件应包含Question实例含pub_date字段格式严格遵循 Django fixture 规范。若报错No fixture named initial_data found说明路径错误——确认文件位于polls/fixtures/initial_data.json且polls是已注册的 app检查settings.py中INSTALLED_APPS是否含polls。2.3 URL 路由与视图绑定解决 “The current path, ‘/’, didn’t match any of these.” 错误Django 投票系统的 URL 结构是分层的根路由urls.py分发到polls/urls.py后者再映射/questions/1/→views.detail。若直接访问http://127.0.0.1:8000/报 404大概率是根路由未包含polls.urls。检查mysite/urls.py项目主路由文件是否包含from django.contrib import admin from django.urls import path, include # ← 必须导入 include urlpatterns [ path(admin/, admin.site.urls), path(polls/, include(polls.urls)), # ← 关键将 /polls/ 下所有请求交给 polls 应用处理 path(, include(polls.urls)), # ← 可选若希望根路径即投票页启用此行并注释上一行 ]再检查polls/urls.py是否正确定义了具体路径from django.urls import path from . import views app_name polls # ← 必须设置否则 {% url polls:detail %} 模板标签失效 urlpatterns [ path(, views.index, nameindex), path(int:question_id/, views.detail, namedetail), path(int:question_id/results/, views.results, nameresults), path(int:question_id/vote/, views.vote, namevote), ]int:question_id是路径参数捕获器Django 会自动将其转为整数传给views.detail(request, question_id)。若浏览器访问http://127.0.0.1:8000/polls/1/仍 404请用python manage.py show_urls需先pip install django-extensions查看实际注册的 URL 列表确认polls:index是否存在且路径为^polls/$。3. 核心功能实现模型设计、视图逻辑与模板渲染的协同闭环3.1Question与Choice模型的关键字段设计及其业务约束polls/models.py中的模型不是随意定义的每个字段都承载明确业务语义和数据库约束from django.db import models from django.utils import timezone class Question(models.Model): question_text models.CharField(max_length200) # 问题标题最长200字符 pub_date models.DateTimeField(date published) # 发布时间用于排序和时效判断 # ↓ 新增字段控制是否允许投票避免过期问题 is_active models.BooleanField(defaultTrue) def __str__(self): return self.question_text # ↓ 自定义方法判断问题是否“最近发布”过去一天内 def was_published_recently(self): now timezone.now() return now - timezone.timedelta(days1) self.pub_date now class Choice(models.Model): question models.ForeignKey(Question, on_deletemodels.CASCADE) # 外键关联on_deleteCASCADE 表示删除问题时自动删所有选项 choice_text models.CharField(max_length200) votes models.IntegerField(default0) # 投票计数非实时聚合提升写性能 def __str__(self): return self.choice_text关键点解析pub_date使用DateTimeField而非DateField因为投票可能按小时粒度截止was_published_recently()方法返回布尔值不能在数据库层面过滤它是 Python 方法但可在模板中用{% if question.was_published_recently %}显示“NEW”标签is_active字段是常见扩展点管理员可在后台勾选/取消控制问题是否接受新投票避免硬编码逻辑。注意修改模型后必须重新makemigrationsmigrate。若仅新增字段如is_activeDjango 会提示You are trying to add a non-nullable field...此时需输入1并提供默认值如True或选择2允许为空nullTrue, blankTrue。3.2 视图函数中的事务控制与防重复提交机制polls/views.py中的vote视图是核心业务逻辑所在必须处理并发写入和用户误操作from django.http import HttpResponse, HttpResponseRedirect from django.shortcuts import get_object_or_404, render from django.urls import reverse from django.db import transaction from .models import Question, Choice def vote(request, question_id): question get_object_or_404(Question, pkquestion_id) try: selected_choice question.choice_set.get(pkrequest.POST[choice]) except (KeyError, Choice.DoesNotExist): return render(request, polls/detail.html, { question: question, error_message: You didnt select a choice., }) else: # ↓ 关键使用 select_for_update() 加行锁防止并发投票导致计数错误 with transaction.atomic(): selected_choice Choice.objects.select_for_update().get(pkselected_choice.pk) selected_choice.votes 1 selected_choice.save() return HttpResponseRedirect(reverse(polls:results, args(question.id,)))逻辑拆解get_object_or_404替代Question.objects.get()避免DoesNotExist异常需手动捕获request.POST[choice]直接取表单namechoice的值对应input typeradio namechoice value{{ choice.id }}select_for_update()在数据库层面锁定该Choice记录确保同一选项被多人同时点击时votes 1不会因读-改-写竞争而丢失计数reverse(polls:results, args(question.id,))生成/polls/1/results/URL比硬编码字符串更安全URL 改变时无需改视图。若未加transaction.atomic()和select_for_update()高并发下votes可能少于实际投票数——这是 Django 投票系统最典型的“数据一致性”坑。3.3 模板继承与上下文传递让index.html动态显示最新问题列表Django 模板系统通过{% extends %}和{% block %}实现复用。polls/templates/polls/index.html通常继承自base.html!-- polls/templates/polls/base.html -- !DOCTYPE html html headtitle{% block title %}Polls{% endblock %}/title/head body div classcontent {% block content %}{% endblock %} /div /body /html!-- polls/templates/polls/index.html -- {% extends polls/base.html %} {% block title %}Latest Polls{% endblock %} {% block content %} h1Latest questions:/h1 {% if latest_question_list %} ul {% for question in latest_question_list %} lia href{% url polls:detail question.id %}{{ question.question_text }}/a/li {% endfor %} /ul {% else %} pNo polls are available./p {% endif %} {% endblock %}views.index必须将latest_question_list传入模板def index(request): latest_question_list Question.objects.filter( is_activeTrue # ← 过滤掉已关闭的问题 ).order_by(-pub_date)[:5] # ← 取最近5条 context {latest_question_list: latest_question_list} return render(request, polls/index.html, context)提示order_by(-pub_date)中的-表示降序若漏写问题会按发布时间从旧到新排列不符合“最新”语义。[:5]是切片Django 会将其转为LIMIT 5SQL而非 Python 层面截取高效。4. 后台管理与部署准备让非技术人员也能维护问题与选项4.1admin.py中的定制化注册暴露is_active字段并优化列表展示Django Admin 是内置的强力管理后台但默认只显示模型基础字段。要让运营人员能开关问题、查看投票总数需定制polls/admin.pyfrom django.contrib import admin from .models import Question, Choice admin.register(Question) class QuestionAdmin(admin.ModelAdmin): list_display (question_text, pub_date, was_published_recently, is_active) list_filter (pub_date, is_active) # 右侧边栏筛选器 search_fields (question_text,) # 顶部搜索框 date_hierarchy pub_date # 按日期分层导航 # ↓ 允许在列表页直接编辑 is_active 字段 list_editable (is_active,) admin.register(Choice) class ChoiceAdmin(admin.ModelAdmin): list_display (question, choice_text, votes) list_filter (question,) # 按所属问题筛选list_display定义列表页显示的列was_published_recently是模型方法Django 会自动调用并显示 True/False 图标list_editable让is_active可直接勾选修改无需点进详情页。部署后访问http://127.0.0.1:8000/admin/用python manage.py createsuperuser创建的账号登录即可图形化管理所有问题。4.2 静态文件收集与生产环境配置绕过DEBUGTrue的安全陷阱开发时DEBUGTrue允许 Django 自动提供 CSS/JS但上线必须关闭否则会暴露敏感路径和 SQL 错误详情。修改settings.pyDEBUG False # ← 必须设为 False ALLOWED_HOSTS [your-domain.com, 192.168.1.100] # ← 替换为实际域名或 IP禁止 [*]静态文件CSS、图片需集中收集到STATIC_ROOT目录供 Nginx/Apache 服务python manage.py collectstatic --noinput该命令将polls/static/和django.contrib.admin.static/等所有静态资源复制到STATIC_ROOT如./staticfiles/。Nginx 配置示例location /static/ { alias /path/to/your/project/staticfiles/; } location / { proxy_pass http://127.0.0.1:8000; proxy_set_header Host $host; }注意collectstatic前确保STATICFILES_DIRS正确指向各 app 的static/目录且STATIC_ROOT是空目录Django 不会自动创建。4.3 使用 Gunicorn 启动服务替代runserver的生产级方案python manage.py runserver仅用于开发绝不能用于生产环境单线程、无进程管理、无 HTTPS 支持。推荐 Gunicornpip install gunicorn gunicorn mysite.wsgi:application --bind 0.0.0.0:8000 --workers 3mysite.wsgi:application指向 WSGI 入口模块--workers 3启动 3 个 worker 进程处理并发请求。配合 systemd 或 supervisor 管理进程生命周期。例如 systemd service 文件/etc/systemd/system/gunicorn-polls.service[Unit] DescriptionGunicorn for Django Polls Afternetwork.target [Service] Typenotify Userwww-data Groupwww-data WorkingDirectory/path/to/your/project ExecStart/path/to/venv/bin/gunicorn mysite.wsgi:application --bind 0.0.0.0:8000 --workers 3 [Install] WantedBymulti-user.target启用服务sudo systemctl daemon-reload sudo systemctl enable gunicorn-polls sudo systemctl start gunicorn-polls5. 进阶技巧用 Django Shell 快速验证模型关系与数据状态5.1 在 Django Shell 中交互式调试外键关联与查询性能当Question.objects.get(id1).choice_set.all()返回空列表但数据库明明有数据时Shell 是最快定位问题的工具。启动python manage.py shell然后逐行执行 from polls.models import Question, Choice q Question.objects.get(id1) q.choice_set.all() # 查看关联选项 QuerySet [Choice: Option A, Choice: Option B] q.choice_set.count() # 快速计数比 len(q.choice_set.all()) 更优不加载全部对象 2 Choice.objects.filter(question__pub_date__year2024).count() # 跨表查询查2024年发布的问题的所有选项数 15question__pub_date__year是 Django 的双下划线链式查询语法等价于 SQL 的JOIN。若choice_set为空检查Choice表中question_id是否真为1用dbshell查或确认Question实例q的id是否正确q.id。5.2 用django-debug-toolbar定位 N1 查询问题投票首页若加载缓慢可能是 N1 查询模板中循环{{ question.choice_set.all }}导致每条问题都触发一次SELECT * FROM polls_choice WHERE question_id ?。安装调试工具pip install django-debug-toolbar在settings.py中添加INSTALLED_APPS [debug_toolbar] MIDDLEWARE [debug_toolbar.middleware.DebugToolbarMiddleware] INTERNAL_IPS [127.0.0.1]在urls.py中加入if settings.DEBUG: import debug_toolbar urlpatterns [ path(__debug__/, include(debug_toolbar.urls)), ] urlpatterns访问http://127.0.0.1:8000/polls/右上角出现调试面板。点击 “SQL” 标签若看到 10 条SELECT ... FROM polls_choice语句说明存在 N1。修复方法是在views.index中使用select_related或prefetch_relateddef index(request): latest_question_list Question.objects.prefetch_related(choice_set).filter( is_activeTrue ).order_by(-pub_date)[:5] # ...prefetch_related会预先执行一次SELECT ... FROM polls_choice并缓存结果后续question.choice_set.all()直接从内存取将 10 次查询降至 2 次。5.3 为投票结果页添加图表用 Chart.js 渲染votes数据polls/templates/polls/results.html可嵌入前端图表。在模板中引入 Chart.js CDNscript srchttps://cdn.jsdelivr.net/npm/chart.js/script canvas idresultsChart width400 height200/canvas script const ctx document.getElementById(resultsChart).getContext(2d); new Chart(ctx, { type: bar, data: { labels: [{% for choice in question.choice_set.all %}{{ choice.choice_text }}{% if not forloop.last %},{% endif %}{% endfor %}], datasets: [{ label: Votes, data: [{% for choice in question.choice_set.all %}{{ choice.votes }}{% if not forloop.last %},{% endif %}{% endfor %}], backgroundColor: rgba(54, 162, 235, 0.2), borderColor: rgba(54, 162, 235, 1), borderWidth: 1 }] }, options: { responsive: false } }); /script此代码将question.choice_set.all的选项文本和票数动态注入 Chart.js 配置生成柱状图。无需后端 API纯前端渲染适合轻量需求。若需服务端渲染如 PDF 导出则需用matplotlib或plotly生成图像二进制流。提示{% for %}中的{% if not forloop.last %},{% endif %}确保 JSON 数组末尾无逗号避免 JS 解析错误。这是 Django 模板处理数组的常用模式。本文还有配套的精品资源点击获取

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价