
简介这是一份面向初学者的Python Django入门级Web工程源码覆盖用户登录、页面渲染、SQLite数据存储等基础功能适合刚接触Django框架或希望快速搭建Web应用起点的开发者参考也可作为课程设计或毕业设计的项目底稿。压缩包共40个文件以13个py源码文件与24个pyc编译文件为主另含HTML页面模板、txt运行说明与sqlite3数据库整体仅155KB体量小、结构清晰。目前已有480人学习下载。通过阅读核心配置、路由、视图与模型等模块可以理清Django的MTV分层、路由映射与请求处理流程结合页面模板目录与SQLite数据库能够直接运行并体验用户登录与页面渲染的完整链路为后续扩展功能奠定基础。1. 为什么“简单Django项目”反而最考验设计功力拿到一个“基于Python的简单Django Web项目设计源码”时不少人第一反应是Django 不是有startproject一键生成骨架吗怎么还需要“设计”但恰恰是这个“简单”二字最能区分一个人是背过教程还是真正写过项目。我见过太多初学者把教程里的投票应用跑通后换一个需求就不知道怎么组织文件、怎么改配置、怎么让多个页面共用一份布局。所谓“设计源码”真正的门槛不在 Django 本身而在于你如何用最小的文件数量搭出一个结构清晰、能继续迭代的 Web 项目。这篇文章会从一个最简单的任务管理应用入手带你完整走过环境搭建、项目骨架、视图与模板协作、数据库迁移的全过程并把我在实际开发中踩过的坑一并交代清楚。如果你是刚学完 Django 基础、想独立完成一个小项目却不知道从哪下手的开发者这篇内容正好对症如果你已经写过几个项目也可以重点看第五章的避坑清单那些问题几乎每个项目都会遇到。2. 环境准备用虚拟环境隔离你的第一个 Django 项目2.1 环境创建的三个必做步骤动手写 Django 项目之前环境隔离是第一道防线。很多初学者图省事直接在全局环境里pip install django结果不同项目依赖的 Django 版本互相打架最后只能重装 Python。常见的做法是用venv为每个项目建立独立的 Python 环境这样项目的依赖清单清晰别人拿到你的源码后也能按requirements.txt一键复现。# 创建项目目录并进入 mkdir django-todo cd django-todo # 创建虚拟环境venv 是 Python 3.3 自带不需要额外安装 python -m venv venv # 激活虚拟环境Windows 与 macOS/Linux 命令不同 # Windows: venv\Scripts\activate # macOS / Linux: source venv/bin/activate # 安装 Django这里固定主版本避免后续升级带来不可控变化 pip install django4.2.* # 生成依赖清单方便别人复现你的环境 pip freeze requirements.txt这段命令里有两个关键点需要注意。第一venv目录必须和项目代码放在同一层级并在提交源码时通过.gitignore把它排除掉否则别人 clone 下来会带着一堆无用的本机文件。第二requirements.txt是“设计源码”的一部分它记录了项目的依赖底线没有这个文件你的代码在其他机器上大概率跑不起来。环境激活后建议顺手验证一下安装结果。python -m django --version能看到具体版本号确认无误后再进行下一步。如果你用的是 Anaconda 或 pyenv原理相同核心目标只有一个让这个项目的依赖与系统全局环境彻底隔离。2.2 项目骨架与“设计源码”的对应关系环境就绪后接下来是用django-admin生成项目骨架。这里我一般会先建一个项目级的配置目录再创建一个应用目录两个步骤分开做让配置与业务代码各归其位。# 生成项目配置目录项目名用 config避免与业务模块重名 django-admin startproject config . # 创建业务应用todo 是应用名代表“任务管理”这个业务模块 python manage.py startapp todo # 查看生成的目录结构 tree .执行完这两条命令后你会看到config目录下是项目的全局配置settings.py、urls.py 等todo目录下是业务代码models.py、views.py、migrations 等。顶层还有一个manage.py它是所有 Django 管理命令的入口。这个结构对“设计源码”来说有一个重要含义config管“这个网站是什么”todo管“这个网站能做什么”。之所以把项目配置目录命名为config而不是默认的mysite是因为默认名称在后续 import 时容易与业务模块产生歧义。比如你建一个应用叫site再有一个全局目录也叫siteimport 时就会互相干扰。用config是社区里比较普遍的做法团队协作时也容易一眼分清职责边界。此时项目还不能运行任何业务页面但骨架已经完整。你可以先跑一次python manage.py runserver浏览器访问http://127.0.0.1:8000看到 Django 的默认欢迎页就说明环境与骨架都正常。3. 核心文件逐个拆从 settings.py 到 urls.py 的配置要点3.1 settings.py新手最容易忽略的三个安全配置settings.py是整个项目的配置中心。新手往往只改INSTALLED_APPS和DATABASES却忽略三个直接影响开发效率与安全性的配置项ALLOWED_HOSTS、LANGUAGE_CODE与TIME_ZONE、STATIC_URL。# config/settings.py # 允许访问的主机列表。开发环境先留空或填 localhost # 部署时必须改成你的域名或服务器 IP否则访问直接报 400。 ALLOWED_HOSTS [localhost, 127.0.0.1] # 语言与时区改为中文和上海时区避免后续 datetime 显示偏差 LANGUAGE_CODE zh-hans TIME_ZONE Asia/Shanghai # 静态文件 URL 前缀。模板里用 {% static %} 时最终渲染出的路径 # 就是 STATIC_URL 文件相对路径。 STATIC_URL static/第一个配置ALLOWED_HOSTS是新手最容易“翻车”的地方。本地开发时用127.0.0.1没问题但如果你用手机或局域网 IP 访问项目Django 会直接拒绝请求报DisallowedHost错误。原因就是 Django 默认只允许ALLOWED_HOSTS列表里的主机访问。把局域网 IP 加进列表即可解决。第二个配置是LANGUAGE_CODE和TIME_ZONE。Django 默认语言是英文时区是 UTC。如果你不做修改Django Admin 后台全是英文而且datetime.now()获取到的时间会比北京时间慢 8 小时。这个坑在后文第五节会专门展开。第三个配置是STATIC_URL。它决定模板中静态文件的请求前缀。很多人把 CSS、JS 文件放到项目根目录就完事结果页面样式死活不加载就是因为没有同时配置静态文件查找路径。开发环境最简单的做法是在设置里追加一行STATICFILES_DIRS把项目根目录下的static文件夹加入查找范围。3.2 urls.py 与应用级路由的分层设计Django 的路由设计讲究“项目级路由分发应用级路由收口”。项目级urls.py只负责把不同应用的请求分发到对应模块具体路径由应用内部自己管理。这样做的好处是一个大型项目几十个应用每个应用都能独立调整路径不用反复改动顶级路由文件。# config/urls.py项目级路由 from django.contrib import admin from django.urls import path, include urlpatterns [ # Django Admin 自带的路由暂时保留 path(admin/, admin.site.urls), # 所有以 todo/ 开头的请求交给 todo 应用自己的 urls.py 处理 path(todo/, include(todo.urls)), ]# todo/urls.py应用级路由需要手动创建 from django.urls import path from . import views urlpatterns [ # 访问 /todo/ 时调用 views 里的 task_list 视图函数 path(, views.task_list, nametask_list), ]这里要解释一下nametask_list的作用它是这条路由的“别名”。模板中可以用{% url task_list %}直接根据名称生成 URL而不是硬编码/todo/。当你改了路径只需调整urls.py模板无需任何改动。这种设计在源码阅读时尤其重要——别人拿到你的项目通过name就能快速追踪页面逻辑。我在一个模拟项目X中见过反面教材所有路由全部写在项目级urls.py里应用一多几百行的路由文件根本没法维护。每次加一个功能都要在那个庞然大物里搜半天。分层路由的设计从项目第一天就立好规矩后续扩展才不会失控。4. 让页面真正跑起来视图、模板与静态资源的协作4.1 视图函数的最简形态与上下文传参视图是 Django 处理请求的核心逻辑单元。一个最简单的视图函数接收request对象返回render渲染后的模板响应。以任务管理应用为例我们希望首页能显示所有任务记录。# todo/views.py from django.shortcuts import render from .models import Task def task_list(request): # 从数据库取出所有任务按创建时间倒序排列 tasks Task.objects.all().order_by(-created_at) # 把任务列表放进模板上下文模板里通过 tasks 变量访问 return render(request, todo/task_list.html, {tasks: tasks})这里的逻辑可以拆成两部分理解。第一行查询Task.objects.all()会执行SELECT * FROM task之类的 SQL 语句.order_by(-created_at)表示按创建时间倒序减号前缀代表反向排序。然后把查询结果字典传给render函数第三个参数就是模板上下文。模板上下文的概念值得多说一句Django 模板只认字典形式的上下文键名就是模板里可用的变量名。这里传了{tasks: tasks}模板里就能用{% for task in tasks %}遍历任务。如果你传的是一个对象而不是字典模板直接报错。4.2 模板继承与静态文件的路径配置模板文件放在应用目录下的templates文件夹里但有一个容易绕晕的细节Django 在多个应用间查找模板时会按INSTALLED_APPS顺序搜索如果你的两个应用都有同名模板后加载的应用会覆盖先加载的。为了避免这种“模板污染”规范做法是在应用下的templates目录里再套一层与应用同名的子目录。!-- todo/templates/todo/task_list.html -- {% extends base.html %} {% block content %} div classcontainer h1我的任务清单/h1 ul {% for task in tasks %} li{{ task.title }} - {% if task.done %}已完成{% else %}待完成{% endif %}/li {% empty %} li还没有任务去添加一个吧。/li {% endfor %} /ul /div {% endblock %}!-- templates/base.html项目级通用模板 -- !DOCTYPE html html langzh-hans head meta charsetUTF-8 title{% block title %}任务管理{% endblock %}/title !-- 通过 static 标签引用 CSS 文件 -- {% load static %} link relstylesheet href{% static css/style.css %} /head body main {% block content %} {% endblock %} /main /body /htmlbase.html是全局布局模板task_list.html通过{% extends base.html %}继承它并用{% block content %}填充页面主体。这种设计让公共的导航、页脚、CSS 引用只写一次每个页面只需关心自己的内容块。这里大概率会遇到一个经典问题CSS 文件放在哪个目录答案是你的项目根目录下建一个static目录然后在settings.py里配置STATICFILES_DIRS。没有这个配置{% static %}标签会报TemplateSyntaxError页面样式全丢。# config/settings.py 追加 STATICFILES_DIRS [ BASE_DIR / static, ]配置完成后把style.css放到static/css/下刷新页面就能看到样式生效。整个视图、模板、静态文件的协作路径就打通了。5. Django 项目避坑5 个高频“翻车”现场与排查方法5.1 迁移报错表已存在却无法关联现象运行python manage.py makemigrations提示No changes detected但数据库中根本没有对应的表。原因最常见的情况是你手动删过数据库文件或者把 app 的migrations目录清空过。Django 通过迁移记录文件追踪模型变化记录与数据库不一致时就会“失忆”。解决先确认 app 是否真的加入INSTALLED_APPS再删掉数据库和migrations目录中的所有文件init.py 除外然后重新执行makemigrations和migrate。不要保留任何旧的迁移记录。5.2 静态文件 404开发环境与生产环境姿势完全不同现象本地开发时页面样式加载正常换成runserver 0.0.0.0:8000通过局域网 IP 访问CSS 全部 404。原因runserver在默认DEBUGTrue时才会自动托管静态文件。当你用0.0.0.0启动Django 仍然会处理静态文件请求但浏览器请求地址变成了http://局域网IP:8000/static/css/style.css如果ALLOWED_HOSTS没有把该 IP 加进去请求会被拒绝。解决把局域网 IP 加进ALLOWED_HOSTS同时确认STATIC_URL与STATICFILES_DIRS配置正确。生产环境则必须用collectstatic把静态文件收集到指定目录再由 Nginx 托管Django 本身不负责生产环境的静态文件服务。5.3 时区与 datetime.now() 的“玄学”偏差现象任务创建时间显示比当前时间慢了 8 小时或者 Admin 后台时间与本地时间不一致。原因settings.py里TIME_ZONE没改成Asia/Shanghai同时USE_TZ为True时数据库存的是 UTC 时间展示时没有做时区转换。解决把TIME_ZONE Asia/Shanghai和LANGUAGE_CODE zh-hans配置好。如果你不想处理时区转换的复杂度可以把USE_TZ FalseDjango 就会直接使用本地时间不再做 UTC 转换。但要注意USE_TZ False后所有涉及时间的逻辑都要多留个心建议还是规范使用USE_TZ True。5.4 CSRF 验证失败表单提交被拦的真相现象在模板里写了一个form methodpost提交时报CSRF verification failed。原因Django 默认开启 CSRF 防护所有 POST 请求必须带上 CSRF token。模板里忘记加{% csrf_token %}标签或者使用了 Ajax 但没有在请求头中携带 token都会被拦截。解决在模板表单内加入{% csrf_token %}。Ajax 请求则在 JavaScript 中从 cookie 读取csrftoken然后写入请求头X-CSRFToken。记住一点凡是 POST 请求必须通过某种方式携带 CSRF token。5.5 ALLOWED_HOSTS换 IP 访问就报错的排查路径现象本地用127.0.0.1:8000一切正常手机通过局域网 IP 访问时页面提示DisallowedHost。原因ALLOWED_HOSTS列表只包含localhost和127.0.0.1局域网 IP 不在允许范围。Django 出于安全考虑默认拒绝所有未列入白名单的主机访问。解决把当前机器的局域网 IP例如192.168.1.100加入ALLOWED_HOSTS然后重启runserver。如果只是临时调试可以直接设置ALLOWED_HOSTS [*]但不建议长期这么做。6. 从“能跑”到“能看”用一个 Bootstrap 模板给项目加分6.1 用模板继承改造现有页面“设计源码”如果只是功能跑通远远不够。一个能拿得出手的项目至少得有一套看得过去的界面。最快的方式是引入现成的 CSS 框架这里以 Bootstrap 为例进行改造。!-- templates/base.html改造后 -- !DOCTYPE html html langzh-hans head meta charsetUTF-8 meta nameviewport contentwidthdevice-width, initial-scale1.0 title{% block title %}任务管理{% endblock %}/title !-- 直接引用 public CDN省去本地静态文件配置 -- link hrefhttps://cdn.jsdelivr.net/npm/bootstrap5.3.3/dist/css/bootstrap.min.css relstylesheet {% block extra_head %}{% endblock %} /head body classbg-light nav classnavbar navbar-expand-lg navbar-dark bg-primary div classcontainer a classnavbar-brand href{% url task_list %}任务管理/a /div /nav main classcontainer py-4 {% block content %}{% endblock %} /main /body /html改造后的base.html引入了 Bootstrap 的公共 CDN内容分发网络即把静态资源放在第三方服务器上用户访问时从就近节点加载速度更快且不占用你的服务器流量。这样做的好处是项目不需要下载维护一份 Bootstrap 源文件模板可以直接使用 Bootstrap 的类名。注意一个细节CDN 方式只适合开发阶段或对加载速度不敏感的场景。正式部署到内网或生产环境最好把 Bootstrap 文件下载到项目的static目录下避免外网中断导致样式全崩。6.2 学会这个排查习惯省掉一半调试时间最后一个技巧也是我每次项目必用的排查习惯开启 Django 的 Debug 日志输出。在settings.py里配置一个简单的日志字典Django 会把你所有 SQL 查询、页面渲染耗时、异常堆栈全部打印在控制台。遇到“页面空白”“数据不对”这类问题先看日志不要盲目改代码。# config/settings.py 追加日志配置 LOGGING { version: 1, disable_existing_loggers: False, handlers: { console: { class: logging.StreamHandler, }, }, loggers: { # 打印所有 SQL 语句排查数据库查询问题非常有效 django.db.backends: { handlers: [console], level: DEBUG, }, }, }我自己的习惯是项目出现任何异常第一件事不是打开源码而是看终端输出。SQL 日志能告诉你 Django 到底执行了什么查询、查询了几次。比如页面加载慢看到django.db.backends日志里一个页面触发了 50 条 SQL马上就能判断是 N1 查询问题然后去优化 ORM 逻辑。这个习惯帮我省过无数个小时的排查时间。哪怕现在写一个“简单”项目我也会从第一天就把日志配好。一个整洁、可观测的开发环境比多写几个功能更能体现一个开发者的成熟度。希望这些内容能帮你把 Django 项目从“能跑”推进到“能看、能维护”少走我当年走过的弯路。本文还有配套的精品资源点击获取