Appearance
章节1:Django基础与MVT架构
一、章节概述
本章从零开始介绍 Django Web 框架的核心概念与基础操作。学生将学会如何安装 Django、创建项目与应用,理解 MVT 架构模式及请求响应生命周期,掌握 URL 路由配置、视图函数编写、模板语法与模板继承等基本技能,为后续深入学习奠定坚实基础。
二、学习目标
| 目标分类 | 具体目标 |
|---|---|
| 知识目标 | 理解 Django 的 MVT 架构模式及其与 MVC 的区别;掌握请求响应生命周期 |
| 技能目标 | 能够独立安装 Django 并创建项目与应用;熟练使用 path/re_path 配置路由;能编写视图函数处理 HttpRequest 并返回 HttpResponse;能使用模板语法渲染动态页面 |
| 素养目标 | 培养遵循 Django 最佳实践的习惯;理解 Web 框架的设计思想 |
三、核心知识点
3.1 Django 安装与项目创建
定义
Django 是一个基于 Python 的高级 Web 框架,遵循"电池全配"(Batteries included)理念,内置了 ORM、认证、Admin 后台等丰富的开箱即用功能。采用 MTV(Model-Template-View)架构模式。
语法与操作
▶ 安装 Django
bash
# 使用 pip 安装指定版本(推荐使用虚拟环境)
pip install django==5.0.2
# 验证安装
python -m django --version
# 输出示例:5.0.2▶ 创建项目与应用
bash
# 创建项目
django-admin startproject myproject
# 进入项目目录
cd myproject
# 创建应用
python manage.py startapp blog▶ 项目结构解析
myproject/
├── manage.py # Django 项目管理入口脚本
├── myproject/ # 项目配置目录
│ ├── __init__.py
│ ├── settings.py # 全局配置文件(数据库、中间件、app 注册等)
│ ├── urls.py # 根 URL 路由配置
│ ├── asgi.py # ASGI 网关入口
│ └── wsgi.py # WSGI 网关入口
└── blog/ # 应用目录
├── __init__.py
├── admin.py # Admin 后台注册
├── apps.py # 应用配置
├── migrations/ # 数据库迁移文件
├── models.py # 数据模型定义
├── tests.py # 测试文件
└── views.py # 视图函数定义▶ 注册应用到项目
在 myproject/settings.py 的 INSTALLED_APPS 中添加应用名称:
python
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
# ... 其他内置应用
'blog', # 注册自定义应用
]▶ 启动开发服务器
bash
python manage.py runserver
# 访问 http://127.0.0.1:8000/
# 可选:指定端口
python manage.py runserver 8080
python manage.py runserver 0.0.0.0:8000 # 局域网可访问3.2 MVT 模式与请求响应生命周期
定义
MVT(Model-View-Template) 是 Django 特有的架构模式,与经典的 MVC(Model-View-Controller)对应:
| MVT | MVC | 职责 |
|---|---|---|
| Model | Model | 数据层,定义数据结构与业务逻辑,与数据库交互 |
| View | Controller | 逻辑层,处理请求、调用 Model、返回响应 |
| Template | View | 表现层,负责数据展示与用户界面渲染 |
请求响应生命周期
用户浏览器 Django 服务器
│ │
│ ── 1. 发送 HTTP 请求 ──────────► │
│ │
│ ┌────┴────┐
│ │ WSGI/ │
│ │ ASGI │
│ └────┬────┘
│ │
│ ┌────┴────┐
│ │ 中间件 │
│ │ (请求钩子)│
│ └────┬────┘
│ │
│ ┌────┴────┐
│ │ URL 路由 │
│ │ (urls.py)│
│ └────┬────┘
│ │
│ ┌────┴────┐
│ │ 视图函数 │
│ │ (views) │
│ └────┬────┘
│ │
│ ┌─────────┼─────────┐
│ │ │
│ ┌────┴────┐ ┌────┴────┐
│ │ Model │ │ Template│
│ │ (数据库) │ │ (渲染) │
│ └─────────┘ └─────────┘
│ │
│ ┌────┴────┐
│ │ 中间件 │
│ │ (响应钩子)│
│ └────┬────┘
│ │
│ ◄──── 2. 返回 HTTP 响应 ──────── │
│ │关键流程说明:
- 用户请求 → 浏览器发送 HTTP 请求到服务器
- WSGI/ASGI 网关 → 将请求转化为 Django 可处理的对象
- 中间件处理(请求阶段)→ 执行通用的请求预处理(如认证、日志)
- URL 路由匹配 →
urls.py根据请求路径找到对应的视图函数 - 视图函数执行 → 处理业务逻辑,调用 Model 查询数据库
- 模板渲染 → 将数据注入模板,生成 HTML
- 中间件处理(响应阶段)→ 执行通用的响应后处理
- 返回响应 → 将 HTML 返回给浏览器渲染显示
3.3 URL 路由配置与反向解析
定义
URL 路由是将用户请求的 URL 地址映射到对应视图函数的机制。Django 支持 path()(精确匹配)和 re_path()(正则匹配)两种方式,并提供 reverse() 函数用于反向解析 URL。
语法
▶ path() 基本路由
python
from django.urls import path
from . import views
urlpatterns = [
path('articles/', views.article_list), # 精确匹配
path('articles/<int:id>/', views.article_detail), # 路径参数
path('articles/<slug:slug>/', views.article_by_slug),
]路径转换器(Path Converters):
| 转换器 | 匹配内容 | 示例 |
|---|---|---|
str | 任意非空字符串(默认) | <str:name> |
int | 零或正整数 | <int:year> |
slug | ASCII 字母、数字、连字符、下划线 | <slug:slug> |
uuid | UUID 格式 | <uuid:id> |
path | 任意非空字符串(含斜杠) | <path:path> |
▶ re_path() 正则路由
python
from django.urls import re_path
urlpatterns = [
# 匹配年份为 4 位数字的 URL
re_path(r'^articles/(?P<year>[0-9]{4})/$', views.articles_by_year),
# 匹配月份
re_path(r'^articles/(?P<year>[0-9]{4})/(?P<month>[0-9]{2})/$', views.articles_by_month),
]▶ 路由分发(include)
python
# myproject/urls.py(根路由)
from django.urls import path, include
urlpatterns = [
path('admin/', admin.site.urls),
path('blog/', include('blog.urls')), # 将 /blog/ 下的路由交给 blog 应用
path('api/', include('api.urls')), # 将 /api/ 下的路由交给 api 应用
]
# blog/urls.py(应用路由)
from django.urls import path
from . import views
app_name = 'blog' # 命名空间,用于反向解析
urlpatterns = [
path('', views.index, name='index'),
path('<int:pk>/', views.detail, name='detail'),
]▶ 反向解析 reverse()
python
from django.urls import reverse
from django.shortcuts import redirect
# 在视图函数中
def my_view(request):
# 生成 URL:/blog/42/
url = reverse('blog:detail', args=[42])
# 或使用 kwargs
url = reverse('blog:detail', kwargs={'pk': 42})
return redirect(url)
# 在模板中使用 {% url %}
# <a href="{% url 'blog:detail' pk=42 %}">查看文章</a>代码示例
python
# blog/urls.py —— 完整的路由配置示例
from django.urls import path, re_path
from . import views
app_name = 'blog'
urlpatterns = [
# 首页
path('', views.index, name='index'),
# 文章列表(分页)
path('page/<int:page>/', views.article_list, name='article_list'),
# 文章详情
path('article/<int:pk>/', views.article_detail, name='article_detail'),
# 按分类筛选
path('category/<slug:category_slug>/', views.category_view, name='category'),
# 按年份归档(正则匹配)
re_path(r'^archive/(?P<year>[0-9]{4})/$', views.archive, name='archive'),
# 搜索
path('search/', views.search, name='search'),
]3.4 视图函数与 HttpRequest 对象
定义
视图函数(View Function) 是处理 HTTP 请求并返回 HTTP 响应的 Python 函数,是 Django 处理业务逻辑的核心入口。HttpRequest 对象封装了来自客户端的所有请求数据。
语法
▶ 基本视图函数
python
from django.http import HttpResponse, JsonResponse
from django.shortcuts import render, get_object_or_404
def index(request):
"""返回纯文本响应"""
return HttpResponse("Hello, Django!")
def about(request):
"""返回 HTML 响应"""
return HttpResponse("<h1>关于我们</h1><p>这是 Django 课程</p>")
def user_list(request):
"""返回 JSON 响应"""
users = [
{"id": 1, "name": "Alice"},
{"id": 2, "name": "Bob"},
]
return JsonResponse(users, safe=False) # safe=False 允许非 dict 对象
def article_detail(request, pk):
"""返回渲染的模板"""
article = get_object_or_404(Article, pk=pk) # 不存在则返回 404
return render(request, 'blog/detail.html', {'article': article})▶ HttpRequest 对象常用属性和方法
| 属性/方法 | 类型 | 说明 |
|---|---|---|
request.method | str | HTTP 方法:GET、POST、PUT、DELETE 等 |
request.GET | QueryDict | GET 请求的查询参数(?key=value) |
request.POST | QueryDict | POST 请求的表单数据 |
request.body | bytes | 原始请求体 |
request.path | str | 请求路径(不含域名) |
request.path_info | str | 请求路径信息 |
request.META | dict | HTTP 请求头及环境信息 |
request.COOKIES | dict | Cookie 数据 |
request.FILES | MultiValueDict | 上传的文件 |
request.user | User | 当前用户对象(需启用认证中间件) |
request.session | SessionBase | 会话对象 |
request.is_ajax() | bool | 判断是否为 AJAX 请求(已弃用) |
request.get_host() | str | 获取主机名 |
request.is_secure() | bool | 是否使用 HTTPS |
代码示例
python
# blog/views.py —— 处理不同类型请求的视图
from django.http import HttpResponse, JsonResponse, HttpResponseBadRequest
from django.shortcuts import render, redirect
from django.views.decorators.http import require_http_methods
import json
def search_view(request):
"""GET 请求:获取查询参数"""
query = request.GET.get('q', '') # 获取 ?q=xxx
page = request.GET.get('page', 1) # 获取 ?page=1
return HttpResponse(f"搜索: {query},页码: {page}")
@require_http_methods(["POST"])
def create_article(request):
"""POST 请求:处理 JSON 数据"""
try:
data = json.loads(request.body) # 解析 JSON 请求体
title = data.get('title')
content = data.get('content')
# ... 保存到数据库
return JsonResponse({"status": "success", "title": title})
except json.JSONDecodeError:
return HttpResponseBadRequest("无效的 JSON 数据")
def request_info(request):
"""展示 HttpRequest 的详细信息"""
info = f"""
<h2>请求信息</h2>
<ul>
<li>方法: {request.method}</li>
<li>路径: {request.path}</li>
<li>主机: {request.get_host()}</li>
<li>用户代理: {request.META.get('HTTP_USER_AGENT', '未知')}</li>
<li>客户端 IP: {request.META.get('REMOTE_ADDR')}</li>
<li>是否安全: {request.is_secure()}</li>
<li>编码: {request.encoding or 'UTF-8'}</li>
</ul>
"""
return HttpResponse(info)3.5 模板语法与模板继承
定义
Django 模板系统 是一种将 Python 数据渲染为 HTML(或其他文本格式)的文本引擎。它使用特定的模板标签和过滤器,实现数据展示与逻辑控制。模板继承 允许创建基础骨架模板,子模板通过覆盖特定块来复用布局。
语法
▶ 模板配置
python
# settings.py
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [BASE_DIR / 'templates'], # 全局模板目录
'APP_DIRS': True, # 自动查找应用下的 templates/
'OPTIONS': {
'context_processors': [
'django.template.context_processors.debug',
'django.template.context_processors.request',
'django.contrib.auth.context_processors.auth',
'django.contrib.messages.context_processors.messages',
],
},
},
]▶ 模板变量与过滤器
html
<!-- 模板变量 -->
<p>{{ article.title }}</p>
<p>{{ article.pub_date|date:"Y-m-d" }}</p>
<p>{{ article.content|truncatewords:30 }}</p>
<!-- 常用过滤器 -->
{{ value|default:"默认值" }}
{{ value|length }} <!-- 长度 -->
{{ value|upper }} <!-- 大写 -->
{{ value|lower }} <!-- 小写 -->
{{ value|date:"Y年m月d日" }} <!-- 日期格式化 -->
{{ value|safe }} <!-- 标记为安全 HTML(不过滤) -->
{{ value|join:", " }} <!-- 列表拼接 -->
{{ value|first }} <!-- 第一个元素 -->
{{ value|slice:":3" }} <!-- 切片 -->▶ 模板标签
html
<!-- 条件判断 -->
{% if article.is_published %}
<p>已发布</p>
{% elif article.status == "draft" %}
<p>草稿</p>
{% else %}
<p>未知状态</p>
{% endif %}
<!-- 循环遍历 -->
{% for item in items %}
<li>{{ forloop.counter }}. {{ item.name }}</li>
{% empty %}
<li>暂无数据</li>
{% endfor %}
<!-- 循环内置变量 -->
{{ forloop.counter }} <!-- 从 1 开始计数 -->
{{ forloop.counter0 }} <!-- 从 0 开始计数 -->
{{ forloop.first }} <!-- 是否为第一项 -->
{{ forloop.last }} <!-- 是否为最后一项 -->
{{ forloop.revcounter }} <!-- 倒数计数 -->▶ 模板继承
基础模板 templates/base.html:
html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>{% block title %}我的博客{% endblock %}</title>
<link rel="stylesheet" href="{% static 'css/style.css' %}">
{% block extra_head %}{% endblock %}
</head>
<body>
<header>
<nav>
<a href="{% url 'index' %}">首页</a>
<a href="{% url 'blog:article_list' %}">文章</a>
<a href="{% url 'about' %}">关于</a>
</nav>
</header>
<main>
{% block content %}
<!-- 子模板填充内容 -->
{% endblock %}
</main>
<footer>
<p>© 2025 我的博客</p>
</footer>
{% block extra_js %}{% endblock %}
</body>
</html>子模板 templates/blog/detail.html:
html
{% extends "base.html" %}
{% load static %}
{% block title %}{{ article.title }} - 我的博客{% endblock %}
{% block content %}
<article>
<h1>{{ article.title }}</h1>
<p class="meta">
作者: {{ article.author }} |
发布时间: {{ article.pub_date|date:"Y年m月d日 H:i" }} |
分类: {{ article.category.name }}
</p>
<div class="content">
{{ article.content|safe }}
</div>
{% if article.tags.all %}
<div class="tags">
标签:
{% for tag in article.tags.all %}
<span class="tag">{{ tag.name }}</span>
{% endfor %}
</div>
{% endif %}
</article>
{% endblock %}▶ include 包含标签
html
<!-- 引入公共组件 -->
{% include "components/sidebar.html" %}
{% include "components/comments.html" with comments=article.comments.all %}代码示例
python
# blog/views.py —— 结合模板的完整视图
from django.shortcuts import render, get_object_or_404
from .models import Article, Category
def index(request):
"""博客首页:展示最新文章列表"""
articles = Article.objects.filter(is_published=True).order_by('-pub_date')[:5]
categories = Category.objects.all()
context = {
'articles': articles,
'categories': categories,
'page_title': '首页',
}
return render(request, 'blog/index.html', context)
def article_detail(request, pk):
"""文章详情页"""
article = get_object_or_404(Article, pk=pk)
context = {
'article': article,
'page_title': article.title,
}
return render(request, 'blog/detail.html', context)html
<!-- templates/blog/index.html -->
{% extends "base.html" %}
{% block title %}{{ page_title }}{% endblock %}
{% block content %}
<section class="hero">
<h1>欢迎来到我的博客</h1>
<p>分享技术与思考</p>
</section>
<section class="articles">
<h2>最新文章</h2>
{% if articles %}
{% for article in articles %}
<article class="card">
<h3>
<a href="{% url 'blog:article_detail' pk=article.pk %}">
{{ article.title }}
</a>
</h3>
<p class="summary">{{ article.content|truncatewords:30 }}</p>
<p class="meta">
{{ article.pub_date|date:"Y-m-d" }} |
阅读: {{ article.views }}
</p>
</article>
{% endfor %}
{% else %}
<p>还没有发布任何文章</p>
{% endif %}
</section>
{% endblock %}四、小结
| 知识点 | 核心要点 |
|---|---|
| Django 安装与项目创建 | 使用 pip install django 安装;django-admin startproject 创建项目;python manage.py startapp 创建应用;记得在 INSTALLED_APPS 中注册应用 |
| MVT 架构 | Model(数据模型)→ View(逻辑处理)→ Template(数据展示);请求经过中间件 → URL 路由 → 视图 → Model/Template → 返回响应 |
| URL 路由 | path() 用于精确匹配;re_path() 用于正则匹配;include() 实现路由分发;reverse() 和 {% url %} 实现反向解析 |
| 视图函数 | 接受 HttpRequest 参数,返回 HttpResponse;常用:render() 渲染模板、redirect() 重定向、JsonResponse() 返回 JSON |
| 模板系统 | 变量输出;{% %} 标签语句;过滤器用 ` |
五、练习
基础练习
- 环境搭建:使用虚拟环境创建一个 Django 项目,命名为
myblog,并创建blog应用 - 路由配置:在
blog应用中配置以下路由:/→index视图/about/→about视图/articles/<int:pk>/→article_detail视图- 使用命名空间
blog
进阶练习
- HttpRequest 练习:编写一个视图
/request-info/,以表格形式展示请求的方法、路径、GET 参数、User-Agent 和客户端 IP - 模板继承:
- 创建一个基础模板
base.html,包含导航栏、内容区和页脚 - 创建
index.html和about.html继承基础模板 - 使用
{% block %}定义可覆盖区域
- 创建一个基础模板
挑战练习
- 简易文章展示系统:
- 在
views.py中定义一个包含 5 篇模拟文章的列表(硬编码数据) - 首页展示所有文章标题(带链接)
- 详情页展示文章的完整内容
- 使用模板继承和
reverse反向解析
- 在
参考代码(练习 5)
python
# 模拟数据
mock_articles = [
{"id": 1, "title": "Django 入门", "content": "Django 是一个...", "pub_date": "2025-01-15"},
{"id": 2, "title": "Python 基础", "content": "Python 是一种...", "pub_date": "2025-01-16"},
]