第8章 · Django 框架基础

用 Python 最成熟的 Web 框架,从零跑通一个能看的博客。代码尽量少——下课前要看见效果。

🎯 6 步走完,浏览器里看见自己的博客文章列表

关于 Django

Django 是 Python 写的 Web 框架,把网站开发里 80% 的脏活——数据库连接、用户系统、后台管理——都替你写好了。Instagram、Pinterest、Mozilla 都是用它做的。

它的玩法很简洁:你声明有什么数据(模型)、什么 URL 对应什么页面(路由)、页面长什么样(模板),剩下交给它。

今天的任务,建一个 blog 项目,里面装一个 article 应用。下课前,浏览器里能打开自己的博客,看见自己刚刚填的几篇文章。

1 安装 Django

装框架

SSH 连上阿里云服务器(Windows 用 PowerShell / MobaXterm / PuTTY,Mac 直接用 Terminal),登录后执行:

SHELL
# 国内网络慢的话用清华镜像
$ pip3 install django -i https://pypi.tuna.tsinghua.edu.cn/simple

# 验证:能看到版本号就装好了
$ python3 -m django --version
6.0.4
💡 版本兼容 + 命令名
教材以 Django 3.0 写就,本章代码在 4.x、5.x、6.x 上都能跑——Django 基础 API 向后兼容性很好。
注意:阿里云服务器上 Python 3 命令叫 python3,pip 叫 pip3——别敲成 pythonpip,那可能指向 Python 2。
2 创建项目

建项目 blog

切换到放代码的目录,执行下面这几条:

SHELL
# 切到放项目的目录(咱们统一放在 /home/django 下)
$ cd /home/django

# 创建一个 Django 项目,名为 blog
$ django-admin startproject blog

# 进入项目目录(往后所有命令都在这里执行)
$ cd blog

启动开发服务器

这一步有个坑:直接 runserver 在某些机房/校园网/虚拟机环境下会报 DisallowedHost 或者外部访问不到。稳妥的写法是显式指定监听地址

SHELL
# 监听本机所有网卡的 8000 端口(推荐这样写)
$ python3 manage.py runserver 0.0.0.0:8000
⚠️ 配套要改 settings.py 的 ALLOWED_HOSTS
指定 0.0.0.0 之后,Django 出于安全要求必须知道"哪些域名/IP 允许访问"。打开 blog/settings.py,找到 ALLOWED_HOSTS = [],改成:
PYTHON blog/settings.py
# 课堂期间用 ['*'] 最省事,意思是"任何 host 都允许"
ALLOWED_HOSTS = ['*']
注意:上线产品里千万不要这样写,会有安全风险。课堂学习够用就行。

浏览器访问 http://127.0.0.1:8000/,看到绿色火箭🚀欢迎页,项目就跑起来了。

💡 服务器一直开着
后续修改代码不需要重启,Django 自动重载。彻底停掉按 Ctrl+C

项目结构(看一眼即可)

blog/ ├── manage.py # 管理工具,所有命令都通过它跑 └── blog/ ├── settings.py # ★ 总配置(刚才已经改过 ALLOWED_HOSTS) ├── urls.py # ★ 路由总入口(第6步要改) ├── asgi.py # 部署相关,先不管 └── wsgi.py # 部署相关,先不管
3 创建应用

建应用 article

💬 项目 vs 应用
项目是整个商场,应用是商场里的店铺。一个项目可以有多个应用。今天只建一个名为 article 的应用。

另开一个终端窗口(保持服务器一直跑),在 blog 目录下:

SHELL
$ python3 manage.py startapp article

项目里多出 article/ 文件夹。关键:把这个应用注册到项目。打开 blog/settings.py,找到 INSTALLED_APPS,把 'article' 加到列表最后:

PYTHON blog/settings.py
INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'article',   # ← 加这一行(前面 6 行别动)
]
⚠️ 别忘逗号
Python 列表元素之间用逗号分隔,漏写就会报 SyntaxError。
4 数据模型

写一个文章模型

模型就是用 Python 类描述一张数据库表。要存"文章",所以建一个 Article 类——每篇文章有标题、作者、正文、发布时间。

打开 article/models.py,写入:

PYTHON article/models.py
from django.db import models


# 一个 class 对应一张数据库表
class Article(models.Model):
    title = models.CharField('标题', max_length=200)
    author = models.CharField('作者', max_length=50)
    content = models.TextField('正文')
    created = models.DateTimeField('发布时间', auto_now_add=True)

    # 决定后台/打印时显示什么——这里显示标题
    def __str__(self):
        return self.title

把模型同步到数据库

模型只是 Python 代码,要让数据库里真的多出一张表,执行两条命令:

SHELL
# Django 看一眼模型,生成"建表说明书"
$ python3 manage.py makemigrations

# 按说明书在数据库里真的建表
$ python3 manage.py migrate
💡 数据库在哪
Django 默认用 SQLite——零配置的轻量数据库。执行完上面两条命令,项目根目录会多出一个 db.sqlite3 文件,那就是你的数据库。这节课不用关心 SQL 怎么写,Django 全替你搞定了。
5 后台 + 填数据

开个后台,添几篇文章

Django 最强的地方之一:不写一行前端代码,自动生成完整管理后台。三步:

第一步:建一个后台管理员账号

SHELL
$ python3 manage.py createsuperuser
Username: admin
Email address: admin@blog.com
Password: ******     # 输入密码(屏幕不显示是正常的)
Password (again): ******
Superuser created successfully.

第二步:把 Article 模型挂到后台

打开 article/admin.py

PYTHON article/admin.py
from django.contrib import admin
from .models import Article

admin.site.register(Article)

第三步:登录后台,添加 2~3 篇文章

浏览器打开 http://127.0.0.1:8000/admin/,用刚才的账号登录。点 ARTICLE → Articles → ADD ARTICLE,填几篇文章——这是后面前台要展示的内容。

💬 它怎么知道字段长什么样
因为你在模型里告诉 Django 了——"title 是 CharField"、"content 是 TextField",它就自动渲染出对应的输入框、文本域。声明一次,到处可用,这就是框架的威力。
6 跑出列表页

让浏览器看到你的文章

到目前为止,文章是存在数据库里、藏在后台里的。现在要让普通用户访问 http://127.0.0.1:8000/article/ 就能看到一个文章列表页。

这件事 Django 把它拆成三个零件,一气呵成做完——

💬 三个零件的关系
路由:浏览器访问 /article/ 时,交给哪个函数处理。
视图:那个函数负责取数据,把数据交给模板。
模板:HTML 骨架,把数据填进占位符,浏览器看到的就是它。

动手前先看:要碰这 4 个文件

blog/urls.py
总入口,加一行
新建
article/urls.py
应用路由,1 行有效代码
article/views.py
写一个函数
新建
article/templates/article/list.html
套两层文件夹

① 改 blog/urls.py —— 加一行

PYTHON blog/urls.py
from django.contrib import admin
from django.urls import path, include   # ← 多导入一个 include

urlpatterns = [
    path('admin/', admin.site.urls),
    path('article/', include('article.urls')),   # ← 加这一行
]

含义:所有 /article/ 开头的请求,转交给 article 应用自己的路由表。

② 新建 article/urls.py —— 这个文件本来不存在

PYTHON article/urls.py(手动新建)
from django.urls import path
from . import views

urlpatterns = [
    path('', views.article_list),   # /article/ → article_list 函数
]

③ 改 article/views.py —— 写一个函数

PYTHON article/views.py
from django.shortcuts import render
from .models import Article


def article_list(request):
    articles = Article.objects.all()
    return render(request, 'article/list.html', {'articles': articles})

render 三件事:找模板 → 把 articles 填进去 → 返回 HTML 给浏览器。

④ 新建 article/templates/article/list.html

在 article 文件夹里建两层目录 templates/article/,里面建 list.html

article/ ├── models.py ├── views.py ├── urls.py # 上面刚建的 └── templates/ └── article/ └── list.html # ← 这个就是模板
💬 为什么套两层 article/article/
外层 templates 是 Django 约定要找模板的地方,内层再加一层 article/ 是避免不同应用的模板重名冲突
HTML article/templates/article/list.html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <title>我的博客</title>
    <style>
        body { font-family: sans-serif; max-width: 720px;
               margin: 40px auto; padding: 0 20px; line-height: 1.7; }
        article { padding: 14px 0; border-bottom: 1px solid #eee; }
        .meta { color: #888; font-size: 0.9em; }
    </style>
</head>
<body>
    <h1>📝 我的博客</h1>

    <!-- 循环:把视图传来的 articles 一篇篇渲染 -->
    {% for article in articles %}
        <article>
            <h2>{{ article.title }}</h2>
            <div class="meta">
                作者:{{ article.author }} · {{ article.created|date:"Y-m-d H:i" }}
            </div>
            <p>{{ article.content }}</p>
        </article>
    {% endfor %}
</body>
</html>
💬 模板里的两种花括号
{{ ... }}填值——把变量内容替换进去;{% ... %}逻辑——循环、判断、过滤器。这是 Django 模板语言唯一要记的两件事。
⚠️ 访问之前,先重启一下服务器
改了 URL 配置之后,autoreloader 有时会因 ImportError 直接挂掉——表现是终端"卡住",没反应。访问页面之前先按 Ctrl+C 干净停掉服务器,再重新启动:
python3 manage.py runserver 0.0.0.0:8000
看到 Starting development server at http://0.0.0.0:8000/ 这一行才算真启动了。

🎉 浏览器里访问

列表页:http://127.0.0.1:8000/article/

应该能看到刚才在后台填的文章一篇篇显示出来。

⚠️ 跑不通?按这个顺序检查
  1. settings.py 里 'article' 加了吗?逗号呢?ALLOWED_HOSTS 改了吗?
  2. makemigrationsmigrate 都跑过了吗?
  3. 后台真的添加文章了吗?
  4. article 下面有 urls.py 吗(这个文件需要你自己建)?
  5. 模板路径是 article/templates/article/list.html 吗(套两层文件夹)?
  6. 服务器报红字了?看终端最后一行错误信息,往往就是答案。

📋 本节小结

四个零件的协作关系:

浏览器 ─请求 /article/──→ blog/urls.py # 总入口分发article/urls.py # 应用内路由匹配article/views.py # 函数取数据list.html # 模板渲染 ↓ 浏览器 ←──────────────── HTML 返回

记住这 4 个高频命令

  • django-admin startproject <名字> — 建项目
  • python3 manage.py startapp <名字> — 建应用
  • python3 manage.py makemigrations 然后 migrate — 同步模型到数据库
  • python3 manage.py runserver 0.0.0.0:8000 — 启动服务器

课堂练习

点选项即时判分。两组从浅到深:先 Linux 命令,再 Django 概念。

第一组

命令行基础(6 题)

这一章用了好几条终端命令——cdpwdmkdir 这些是基础中的基础。Windows、Mac、Linux 命令名略有差异,但思路一样。

Q1单选

想知道当前在哪个文件夹下,应该用哪个命令?

Acd
Bpwd
Cls
Dmkdir
解析:pwd = print working directory,打印当前工作目录。cd 是切换目录,ls 是列文件,mkdir 是建目录。
Q2单选

/home/django/blog 切换到上一级目录 /home/django,用哪个命令?

Acd(不带参数)
Bcd ..
Ccd /
Dback
解析:两个点 .. 表示"上一级目录"。cd / 是切到根目录,cd 不带参数在 Linux 下是回家目录。
Q3单选

想列出当前文件夹下所有内容,Linux/Mac 用 ls,Windows CMD 通常用什么?

Alist
Bll
Cdir
Dshow
解析:Windows CMD 用 dir,PowerShell 两个都能用(PowerShell 把 ls 设成了 dir 的别名)。
Q4单选

第 6 步要建模板目录,命令应该是?

Anew templates
Bmkdir templates
Ccd templates
Dtouch templates
解析:mkdir = make directory,建文件夹;touch 是建空文件;cd 是切换目录。
Q5判断

服务器跑起来后,按 Ctrl+C 可以彻底停掉服务器。

解析:Ctrl+C 是终端通用的"中断当前进程"信号,停服务器、停脚本都靠它。
Q6判断

在终端里输入命令时,$>提示符,是终端自动显示的,不是你要敲进去的内容。

解析:Linux/Mac 提示符通常是 $,Windows CMD 是 >,PowerShell 是 PS>。讲义里写 $ pip3 install,你只需敲 pip3 install
第二组

Django 概念加深(6 题)

不是考代码,是考你真的理解了 Django 在干什么。做完这一组,下次自己写新功能就能自如。

Q1单选

关于"项目(project)"和"应用(app)"的关系,下列哪个说法是对的?

A一个项目最多只能装一个应用
B应用必须叫和项目一样的名字
C一个项目可以装多个应用,由 INSTALLED_APPS 管理
D项目和应用是同一个概念,两个名字而已
解析:项目是商场,应用是店铺。一个商场可以有很多店铺。注册到 INSTALLED_APPS 之后 Django 才知道有这个应用。
Q2单选

本章 class Article(models.Model) 这个模型类,对应数据库里的什么?

A一行数据(一条记录)
B一张表
C一个字段(一列)
D整个数据库
解析:类 = 表,类的实例(一个 Article 对象)= 表里的一行,类的属性 = 表的字段。这套对应叫 ORM(对象关系映射)。
Q3单选

makemigrationsmigrate 的区别?

A没区别,写哪个都行
Bmakemigrations 把模型变化记成"说明书",migrate 才真的去数据库执行
Cmigrate 必须在 makemigrations 之前跑
Dmakemigrations 是更新数据,migrate 是更新模型
解析:两步分离的好处是——说明书(migrations 文件)会被 git 记录,团队协作时同事拉下来跑一次 migrate 就同步了。
Q4单选

为什么 Django 后台 /admin/自动给 Article 模型生成"添加文章"的表单?

ADjango 偷看了你的浏览器
B因为表单是 admin.py 里写好的
C因为模型类的字段类型(CharField/TextField/...)已经声明了,Django 反向推导出表单
D因为浏览器自带表单生成功能
解析:这就是"声明式编程"的威力——你只声明"这个字段是 CharField,最大 200 字",框架就能自动派生出表单、数据库列、校验逻辑、API 序列化等等。
Q5判断

SQLite 需要单独装一个数据库服务(像 MySQL 那样起一个 server),Django 才能用。

解析:SQLite 是嵌入式数据库,整个数据库就是一个文件(db.sqlite3),不需要起服务。Python 标准库已经自带,零安装。这就是它适合教学/小项目的原因。
Q6单选

如果 settings.py 里忘了把 'article' 加到 INSTALLED_APPS,会怎样?

A项目根本启动不了
B项目能启动,但 makemigrations 不会发现 Article 模型,后台也看不到这个应用
C没影响,Django 自动找
D启动会报数据库错误
解析:这是新手最常踩的坑——建了应用但忘了注册。表现是"代码看着没错,但数据库里就是不建表"。INSTALLED_APPS 是 Django 的"通讯录",没记上的应用它装作不认识。