Django入门实操:从URL路由到数据库的完整Web开发流程
发布时间:2026/10/14 22:08:00 锦皓数字建站

想用 Python 做 Web 页面开发第一个绕不开的名字就是 Django。我几年前第一次接触 Django 时其实心里很没底——但认真把它的 URL 路由、视图函数和模板系统串起来之后会发现页面开发的底层逻辑一下子通了。这篇文章就适合那些已经会一点 Python但还没搞明白“一个网站页面到底是怎么被创造出来的”的人。我不会堆概念只按我自己实际跑通项目的顺序来说从环境准备到写出一个带数据库、带表单的页面再到把 DEBUG 关掉之后还能正常显示每一环你都可以直接跟着操作。我之前见过不少初学者卡在半路不是因为代码难而是因为他们不知道“先做什么、后做什么”。所以这篇 Django 入门文干脆把它当成一条修路工程先铺地基环境再立骨架项目和应用然后接通水电URL 和视图最后装修模板、静态文件、表单再连上仓库数据库。每一部分我都尽量说清“为什么这么做”以及对应会踩到什么坑。1. 为什么一个“全家桶”式的框架更适合 Web 页面入门1.1 新手做页面最容易失控的点假设你只会 Python想做一个“展示用户留言”的页面。没有框架时你大概会写一个 Python 脚本去读文件再拼一段 HTML 字符串然后用某种方式把字符串发给浏览器。但问题很快就会出现用户留言慢慢多起来你需要处理不同的 URL比如/list、/detail/3页面里要循环输出留言表单提交后要校验内容数据得存进数据库而不是每次重读文件。当你发现这些需求要同时满足时所有代码都开始纠缠在一起改一处崩三处。这就是我推荐新手直接上大型框架的原因Django 把“接收请求 → 匹配 URL → 调用处理函数 → 返回 HTML → 操作数据库”这一整套动作用清晰的目录结构和约定好的接口固定下来。你不需要自己发明轮子只需要把手放进正确的位置。很多人担心“全家桶”太重但对入门来说重反而意味着“有人替你把坑填好了”。1.2 Django 解决的核心问题URL、HTML 和数据的粘合Django 自己也承认它是一个“包含一切”的框架——ORM 帮你操作数据库模板引擎帮你生成 HTML表单库帮你处理用户提交admin 后台帮你管理数据。一个页面请求进来之后Django 会依次做这几件事先根据用户访问的路径找到对应的 URL 规则然后调用这个规则指向的视图函数视图函数里可能读写数据库也可能只是组装一些变量最后把变量交给模板文件渲染成完整的 HTML 返回给浏览器。这个过程中最妙的一点是你几乎不需要关心底层网络细节你只需要按照“一份 URL 对应一个视图一个视图渲染一个模板”的方式组织代码。对新手的价值特别大页面是从哪里来的、数据是怎么出现的心里有条非常清晰的线。当你理解了这条线之后再去学更轻量的框架或者其他后端语言都会觉得是似曾相识。1.3 适合谁读这篇博文如果你符合下面任意一条这篇入门实践就很适合你第一会写print和for循环但没写过一个真正能访问的网页第二曾经照着教程“敲”过 Django 代码但搞不清urls.py、views.py、models.py之间怎么协作第三想用 Python 快速做一个带简单交互的原型页面不想花太多时间在服务器配置上。这篇博文里的例子都很小每一段都可以独立验证你不用先完整看懂所有代码跟着做出来再回头琢磨理解往往会更快。2. 环境准备Python 版本、虚拟环境和 Django 安装2.1 版本选择背后的现实理由很多初学者问的第一个问题是“我该装哪个 Python 版本”我的建议是装 Python 3.10 或者 3.11然后选 Django 4.2 LTS。这不是追新而是求稳。LTS 意味着官方会持续维护安全修复第三方库生态基本都跟得上网上搜到的问题解决方案也多。Python 3.12 当然也可以用但偶尔会遇到一些原生库还没编译好对应 wheel 的情况对一个只想先把页面做出来的人来说这种额外障碍没必要。安装之前最好先确认一下当前环境里有没有其他项目在跑。如果你曾经用过系统自带的 Python又直接pip install过一堆包时间长了依赖会乱。这也是我强调“虚拟环境”的原因——它能让你每个练习项目都拥有一个独立的小仓库装什么包都不会污染到别的项目。2.2 创建虚拟环境与安装先在你打算放项目的目录下打开终端Windows 使用 cmd 或 PowerShellmacOS/Linux 使用自带终端然后执行mkdir django-blog cd django-blog python -m venv venv这里的第一个venv是 Python 自带的虚拟环境模块第二个venv是虚拟环境目录名你可以按喜好改名。执行完成后目录里会出现一个venv文件夹里面有独立的 Python 解释器和 pip。接着激活虚拟环境# Windows venv\Scripts\activate # macOS / Linux source venv/bin/activate激活成功后终端提示符前面会多出(venv)字样。此时再安装 Djangopip install Django4.2,4.3这么做是为了避免装成未来的 Django 5.x。虽然新版功能更多但一些第三方扩展可能还没适配入门阶段先锁死在 LTS 版本里最省心。2.3 验证安装成功的标志安装完成后可以用下面这条命令确认python -m django --version如果能输出类似4.2.11的版本号说明 Django 已经就位。这里有个细节如果你执行django-admin --version报“命令不存在”而python -m django --version正常说明你之前没有正确激活虚拟环境或者系统里存在多个 Python。我建议统一用python -m django这种方式它会严格使用你当前虚拟环境里的 Python不会搞混。到这里环境就准备好了。接下来正式开始生成项目和页面。3. 创建项目和应用看懂 manage.py 生成的每一个目录3.1 startproject 与 startapp 到底做了什么在虚拟环境激活的状态下执行django-admin startproject myblog cd myblog你会得到一个myblog文件夹里面有一个manage.py和另一个也叫myblog的子目录。第一次看到这种嵌套目录时我也懵了一下外层myblog是项目容器里面的myblog是项目的“配置包”存放着settings.py、urls.py、wsgi.py、asgi.py。manage.py是 Django 给你的命令行工具以后启动开发服务器、执行迁移、创建应用都要靠它。接着创建一个应用应用才是真正存放页面逻辑的地方python manage.py startapp blog执行后会出现一个新目录blog里面有views.py、models.py、migrations/等文件。可以这样区分一个 Django 项目可以包含多个应用比如一个博客系统可以有blog应用管文章、comments应用管评论。项目负责全局配置应用负责具体业务模块。3.2 settings.py 里必须改的两个地方刚生成的settings.py文件很长新手不用全看但有两个位置建议第一时间改掉。第一个是INSTALLED_APPS你需要把新建的应用加进去告诉 Django“这个应用我已经启用了”INSTALLED_APPS [ django.contrib.admin, django.contrib.auth, django.contrib.contenttypes, django.contrib.sessions, django.contrib.messages, django.contrib.staticfiles, blog, # 新增这行 ]第二个是语言和时区。默认配置是英文环境和 UTC 时间咱们国内访问时页面时间会差 8 小时。你可以改成LANGUAGE_CODE zh-hans TIME_ZONE Asia/Shanghai USE_TZ True这里注意USE_TZ True表示 Django 使用带时区的时间对象存进数据库的是 UTC渲染时按当前时区转换。日常写练习项目你也可以保持这个默认值不动只需把TIME_ZONE改了。3.3 应用注册与第一个“Hello World”页面先把最简单的动态页面跑通。打开blog/views.py写一个函数from django.http import HttpResponse def index(request): return HttpResponse(Hello, Django Web Page!)然后在项目的myblog/urls.py里把 URL 指过去from django.contrib import admin from django.urls import path, include urlpatterns [ path(admin/, admin.site.urls), path(, include(blog.urls)), ]此时还没blog/urls.py需要新建一个from django.urls import path from . import views urlpatterns [ path(, views.index), ]接着启动服务器python manage.py runserver浏览器访问http://127.0.0.1:8000/看到Hello, Django Web Page!说明你从零搭起的页面已经生效了。这个过程中你其实已经走完了 Django 最核心的链路浏览器 → URL 配置 → 视图函数 → 返回内容。后面所有复杂功能都是在这条链路上分支扩展。4. URL 路由、视图函数与请求生命周期4.1 一次浏览器请求在 Django 里的完整旅程当你在浏览器地址栏输入网址并回车Django 做的事情可以拆成四步。第一步开发服务器把原始 HTTP 请求包装成一个HttpRequest对象第二步Django 从urls.py的urlpatterns列表里从上到下匹配请求路径第三步找到匹配的视图函数后把request对象传进去第四步视图函数返回一个HttpResponse对象Django 再把它转成实际的 HTTP 响应发回浏览器。你可以把 URL 配置想象成一个快递分拣台每个快递请求上面有地址分拣员URL dispatcher看到/blog/3就把包裹扔到对应编号的通道视图函数里。如果所有通道都没有匹配的地址Django 就会返回 404 页面。这个过程决定了你写页面时的思维顺序先想清楚用户会访问什么路径再去编写对应的处理函数。4.2 path() 函数与动态参数页面路径不可能永远是写死的比如文章详情页/article/1、/article/2你不可能为每一篇都写一行 URL。Django 提供了动态路径参数from django.urls import path from . import views urlpatterns [ path(, views.index), path(article/int:article_id/, views.article_detail), ]对应视图函数def article_detail(request, article_id): return HttpResponse(f正在查看第 {article_id} 篇文章)这里int:article_id是一个路径转换器int表示匹配整数并把匹配到的值作为参数article_id传给视图函数。类似的还有str、slug等。当用户访问/article/999时视图函数收到的article_id就是整数 999。如果访问/article/abc则无法匹配会走 404。4.3 为什么视图函数必须返回 HttpResponse初学者最容易忘的是视图函数末尾必须return一个响应对象。如果你只写了def index(request):然后什么都不返回页面会报错。原因是 Django 需要从视图函数的返回值中提取 HTTP 状态码和响应体没有响应对象它就没法继续向浏览器回话。render()是视图函数中最常用的快捷方式它的作用是把模板文件和变量结合起来最终返回一个完整的 HTML 响应。比如from django.shortcuts import render def index(request): return render(request, blog/index.html, {name: Ming})这里render的三个参数分别是请求对象、模板路径、传给模板的上下文数据。它会读取模板文件渲染完成后自动封装成HttpResponse。理解了这个你再看后面所有基于模板的页面都不会觉得神秘。5. 模板系统用 Django Template Language 搭建真实页面5.1 模板的基本语法变量、标签和过滤器为了不再返回“裸字符串”我们需要把 HTML 抽出来放到模板文件里。Django 模板默认存放在应用目录下的templates文件夹。在blog目录下新建blog/templates/blog/index.html目录结构决定模板的命名空间避免不同应用之间的模板文件重名。模板不是单纯的 HTML它允许你嵌入动态表达式。变量用双花括号包裹比如{{ name }}逻辑语句用{% %}包裹比如循环和判断。举个例子!DOCTYPE html html headtitleMy Blog/title/head body h1欢迎{{ name }}/h1 {% if articles %} ul {% for article in articles %} li{{ article.title }}/li {% endfor %} /ul {% else %} p还没有文章/p {% endif %} /body /html过滤器通过管道符号|使用例如{{ article.pub_date|date:Y-m-d }}可以把日期对象格式化成2025-01-01。过滤器还能处理默认值、计算长度等记不住就去查文档用多了自然就熟。5.2 模板继承告别复制粘贴页面一个真实网站往往有导航栏、页脚它们几乎出现在每个页面上。如果每个模板都复制一份导航栏代码改一次要改十几个文件。Django 的解决办法是模板继承。先写一个基础模板base.html!DOCTYPE html html head title{% block title %}默认标题{% endblock %}/title /head body nav这里是导航/nav {% block content %}{% endblock %} footer这里是页脚/footer /body /html子模板中通过{% extends base.html %}声明继承然后覆写对应 block{% extends base.html %} {% block title %}文章列表{% endblock %} {% block content %} p这是子页面独有的内容/p {% endblock %}这样做的好处是页面的公共结构只需要维护一份如果你想让某个页面与众不同只要在它的 block 里写新内容就行。模板继承和{% include %}不同include是把一个模板文件嵌入到当前模板中适合复用小块组件比如{% include footer.html %}。5.3 页面中展示动态数据的小例子我们结合前面的 URL 和视图来一个完整小例子。假设视图函数从列表里取出数据def article_list(request): articles [ {title: Django 入门, pub_date: 2025-01-01}, {title: 模板系统实践, pub_date: 2025-01-02}, ] return render(request, blog/index.html, {articles: articles})模板里用{% for %}循环输出标题。这样做虽然数据还是硬编码在代码里但已经能让你直观体会到“视图准备数据 → 模板负责展示”的分工。把硬编码换成数据库内容只差后面的模型和查询一步。6. 静态文件与表单让页面带样式、能交互6.1 静态文件的配置和使用到目前为止页面还是纯 HTML没有 CSS 样式也没有图片。Django 把这类文件统称为“静态文件”。开发模式下你需要在settings.py里确认STATIC_URL static/然后在应用目录里创建blog/static/blog/style.css模板中就能这样引用{% load static %} link relstylesheet href{% static blog/style.css %}这里有一个隐形坑{% load static %}必须在使用了{% static %}的模板文件开头声明如果是子模板继承了带样式的base.html你不需要在每个子模板里重复加载但 base.html 本身必须{% load static %}。加载后Django 会拼出完整的静态文件路径比如/static/blog/style.css。6.2 最简单的 GET/POST 表单处理页面要“交互”最基础的方式是表单。我们做一个留言板用户填名字和内容提交后页面显示出来。先写一个视图from django.shortcuts import render from django.http import HttpResponse def message_board(request): if request.method POST: name request.POST.get(name, ) content request.POST.get(content, ) return HttpResponse(f收到来自 {name} 的留言{content}) return render(request, blog/message_form.html)模板里放表单form methodpost {% csrf_token %} input typetext namename placeholder你的名字 textarea namecontent placeholder留言内容/textarea button typesubmit提交/button /form这里request.method用来区分用户是第一次打开页面GET还是提交了表单POST。request.POST.get(name)是从 POST 请求体里取出表单字段值。当你点击提交后页面会显示“收到来自 XX 的留言XXX”。整个过程没有任何数据库参与但你已经完成了“浏览器 → 服务器 → 浏览器”的一次完整交互闭环。6.3 CSRF 防护Django 为什么强制要求 token上面模板里的{% csrf_token %}初看似乎多余去掉之后你会立刻遇到403 Forbidden错误。这是 Django 的安全设计CSRF跨站请求伪造攻击会诱导用户在不知情的情况下向某个已登录网站发送恶意请求。为了避免这个问题Django 要求所有 POST 表单必须携带一个一次性随机 token这个 token 与用户的会话绑定服务器收到请求后会验证它是否合法。你不需要成为安全专家才能用 Django但要理解这条规则不是 Django 故意刁难你而是它在默认情况下帮你挡住了最常见的攻击类型。如果你用 JavaScript 的fetch提交表单同样需要把 token 放进请求头这一点等你日后写前后端分离项目时会再遇到。7. 数据库模型与 admin不写 SQL 也能管理页面数据7.1 ORM 模型定义与迁移前文的页面数据都是硬编码在 Python 列表里这离真实项目还很远。Django 的 ORM对象关系映射让你用 Python 类来描述数据库表结构。以留言为例在blog/models.py中写from django.db import models class Message(models.Model): name models.CharField(max_length50) content models.TextField() created_at models.DateTimeField(auto_now_addTrue) def __str__(self): return f{self.name}: {self.content[:20]}写完模型后需要执行两条命令python manage.py makemigrations python manage.py migratemakemigrations会根据模型变化生成迁移文件migrate把迁移文件真正执行到数据库里。默认情况下Django 会使用项目根目录下的db.sqlite3文件作为数据库对入门练习来说完全够用不需要额外安装 MySQL 或 PostgreSQL。你可以打开迁移文件看一眼会发现里面就像一份“建表说明书”包含了字段类型和约束。7.2 在页面中读取数据库内容现在留言视图可以改写为从数据库读取from django.shortcuts import render from .models import Message def message_board(request): if request.method POST: name request.POST.get(name, ) content request.POST.get(content, ) Message.objects.create(namename, contentcontent) messages Message.objects.order_by(-created_at) return render(request, blog/message_list.html, {messages: messages})Message.objects.create(...)等价于在数据库里插入一条记录Message.objects.order_by(-created_at)按创建时间倒序取出全部留言。模板里可以这样展示ul {% for msg in messages %} li{{ msg.name }}{{ msg.content }}{{ msg.created_at|date:Y-m-d H:i }}/li {% empty %} li还没有留言/li {% endfor %} /ul此时如果你清空数据库重新跑流程提交几条留言后刷新页面会发现数据已经持久化即使重启服务器历史留言也不会丢。这就是 ORM 带来的直观成就感——你不用写任何 SQL却完成了一个带存储的真实功能。7.3 admin 后台免费送你一个数据管理界面Django 自带的后台管理界面是它最吸引新手的特性之一。首先创建管理员账号python manage.py createsuperuser按照提示输入用户名、邮箱、密码。然后打开blog/admin.py注册模型from django.contrib import admin from .models import Message admin.site.register(Message)访问http://127.0.0.1:8000/admin/用刚创建的账号登录你就能看到一个可视化管理页面可以增删改查留言数据。你可以在后台删除某条不当留言再回前台刷新验证。这个后台对于快速原型、内容管理、甚至小型内部系统都非常实用也是很多人爱上 Django 的原因之一。8. 上线前检查清单DEBUG、ALLOWED_HOSTS 和静态文件收集8.1 DEBUGFalse 后的页面直接白屏原因是这些开发时你一直开着DEBUG TrueDjango 会为你提供详细的报错页面并自动用开发服务器处理静态文件。但把项目部署到公网时你必须把DEBUG设为False否则一旦代码报错浏览器会直接把异常堆栈和本地路径暴露给用户非常危险。但新手往往刚把DEBUG改成False就傻眼了页面白屏CSS 全丢了甚至访问后台也打不开。原因是runserver默认只在 DEBUG 模式下服务静态文件关闭调试后静态文件需要由 Django 收集到一个统一目录再由部署层比如 Nginx 或者其他 WSGI 服务器去托管。开发时你可以继续用runserver调试但一旦为了保证安全改了DEBUG就要同步处理静态文件。8.2 静态文件收集与白名单配置在settings.py末尾追加ALLOWED_HOSTS [your-domain.com, www.your-domain.com] STATIC_ROOT BASE_DIR / staticfiles这里的ALLOWED_HOSTS用来声明哪些域名可以访问你的 Django 服务。如果你部署后只是临时测试可以写成ALLOWED_HOSTS [*]但正式环境不要这么做会让你的站点可以被任意 Host 请求访问。然后执行python manage.py collectstaticDjango 会把所有应用里的静态文件复制到STATIC_ROOT指定的目录也就是staticfiles文件夹。如果你用runserver测试DEBUGFalse它仍然不负责服务这些静态文件你需要借助符合 WSGI 规范的中间件来支持或者让前置的 Web 服务器去处理。不过入门阶段你只要知道流程是这样开发时静态文件自动处理部署时手动收集。8.3 我个人每次部署前必查的三个点根据我自己的经验上线前与其看一堆部署文档不如先检查这三件事。第一DEBUG是否真的为False并且ALLOWED_HOSTS里写的是实际域名而不是*。第二collectstatic是否已经执行过并且staticfiles目录里的 CSS/JS 文件引用的路径和模板中{% static %}输出的一致。第三数据库有没有运行过完整迁移最好是在全新环境从零开始migrate一遍防止某些表在开发环境里早就建立换台机器就漏了。你还可以用一条命令检查完大部分配置问题python manage.py check --deploy它会输出几条安全警告比如你仍开着 DEBUG、没有设置安全密钥的存储方式等。哪怕是警告也值得逐条读懂。这个命令提示的内容往往就是新手最容易忽略的上线死角。说回个人体会我最初做完这个入门流程后并没有急着去做大项目而是把所有小片段故意“改坏”再修好。比如把视图函数的返回类型改成字符串、忘记在表单里写 CSRF token、在模板继承时写错 block 名字。把每个错误都亲眼看到一次比看十遍文档记得牢。这也算是我推荐的学习方式——先让页面跑起来再把它弄坏最后自己修好这个过程里建立的底层逻辑能帮你应对后面更复杂的功能开发。
锦
锦皓数字建站
深耕本土企业品牌数字化升级,专注原创端正雅致商务官网,从视觉设计到稳定运维全程保驾护航。