Skip to content

章节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.pyINSTALLED_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)对应:

MVTMVC职责
ModelModel数据层,定义数据结构与业务逻辑,与数据库交互
ViewController逻辑层,处理请求、调用 Model、返回响应
TemplateView表现层,负责数据展示与用户界面渲染

请求响应生命周期

用户浏览器                         Django 服务器
    │                                   │
    │  ── 1. 发送 HTTP 请求 ──────────► │
    │                                   │
    │                              ┌────┴────┐
    │                              │  WSGI/  │
    │                              │  ASGI   │
    │                              └────┬────┘
    │                                   │
    │                              ┌────┴────┐
    │                              │ 中间件   │
    │                              │ (请求钩子)│
    │                              └────┬────┘
    │                                   │
    │                              ┌────┴────┐
    │                              │ URL 路由 │
    │                              │ (urls.py)│
    │                              └────┬────┘
    │                                   │
    │                              ┌────┴────┐
    │                              │ 视图函数 │
    │                              │ (views)  │
    │                              └────┬────┘
    │                                   │
    │                         ┌─────────┼─────────┐
    │                         │                   │
    │                    ┌────┴────┐         ┌────┴────┐
    │                    │  Model  │         │ Template│
    │                    │ (数据库) │         │ (渲染)   │
    │                    └─────────┘         └─────────┘
    │                                   │
    │                              ┌────┴────┐
    │                              │ 中间件   │
    │                              │ (响应钩子)│
    │                              └────┬────┘
    │                                   │
    │  ◄──── 2. 返回 HTTP 响应 ──────── │
    │                                   │

关键流程说明:

  1. 用户请求 → 浏览器发送 HTTP 请求到服务器
  2. WSGI/ASGI 网关 → 将请求转化为 Django 可处理的对象
  3. 中间件处理(请求阶段)→ 执行通用的请求预处理(如认证、日志)
  4. URL 路由匹配urls.py 根据请求路径找到对应的视图函数
  5. 视图函数执行 → 处理业务逻辑,调用 Model 查询数据库
  6. 模板渲染 → 将数据注入模板,生成 HTML
  7. 中间件处理(响应阶段)→ 执行通用的响应后处理
  8. 返回响应 → 将 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>
slugASCII 字母、数字、连字符、下划线<slug:slug>
uuidUUID 格式<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.methodstrHTTP 方法:GET、POST、PUT、DELETE 等
request.GETQueryDictGET 请求的查询参数(?key=value
request.POSTQueryDictPOST 请求的表单数据
request.bodybytes原始请求体
request.pathstr请求路径(不含域名)
request.path_infostr请求路径信息
request.METAdictHTTP 请求头及环境信息
request.COOKIESdictCookie 数据
request.FILESMultiValueDict上传的文件
request.userUser当前用户对象(需启用认证中间件)
request.sessionSessionBase会话对象
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>&copy; 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
模板系统 变量输出;{% %} 标签语句;过滤器用 `

五、练习

基础练习

  1. 环境搭建:使用虚拟环境创建一个 Django 项目,命名为 myblog,并创建 blog 应用
  2. 路由配置:在 blog 应用中配置以下路由:
    • /index 视图
    • /about/about 视图
    • /articles/<int:pk>/article_detail 视图
    • 使用命名空间 blog

进阶练习

  1. HttpRequest 练习:编写一个视图 /request-info/,以表格形式展示请求的方法、路径、GET 参数、User-Agent 和客户端 IP
  2. 模板继承
    • 创建一个基础模板 base.html,包含导航栏、内容区和页脚
    • 创建 index.htmlabout.html 继承基础模板
    • 使用 {% block %} 定义可覆盖区域

挑战练习

  1. 简易文章展示系统
    • 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"},
]

Python 学习资料