第 2 章:Hello, World 网站

第 2 章:Hello, World 网站

本章中,我们将回顾网站和 Web 框架的工作原理,剖析 Django 的架构,然后构建一个显示”Hello, World”的简单 Django 网站。我们还将深入了解 Django 的两个核心组成部分——URL 分发器和视图,并首次使用 Git 进行版本控制。本章及后续所有章节的完整源代码可以在本书的官方 GitHub 仓库 中找到。

互联网的工作原理

你可能每天都在使用互联网,但除非你是一名 Web 开发人员,否则你很可能不清楚在浏览器中输入 https://learndjango.com 这样的地址并按下回车后,背后究竟发生了什么。支撑这一用户体验的是一套由通信协议、Web 服务器和业务逻辑组成的复杂网络。

计算机科学专业的学生通常会选修一整门关于网络通信的课程,而网络工程师则终其职业生涯专注于这一领域。对于作为 Web 开发人员的我们来说,对这个过程有一个大致的理解就足够开始了。不过,如果你将来要处理高流量的网站,网络通信的细节和微妙之处就会变得越来越重要。

互联网的底层是一个由互联机器组成的网络,这些机器被称为服务器。它们是一些没有屏幕、鼠标或键盘的特殊计算机,通常位于数据中心,与其他服务器一起排列在一排排的机架中。在过去,Web 开发人员必须自己运行物理服务器;而如今,更常见的做法是从大型托管公司租用空间,也就是俗称的”云”。你可以通过海底电缆地图——一个交互式的地图,展示了连接各大洲和国家的海底电缆——来直观地感受这个互联机器网络。

海底电缆地图

让我们追踪一下当你尝试访问 https://learndjango.com 这个网站时发生的事情。我们可以将其简化为六个步骤:

  1. 你在浏览器中输入一个域名
  2. 浏览器通过 DNS(域名系统)查找该域名的 IP 地址
  3. 浏览器与 Web 服务器建立网络连接
  4. 浏览器发送一个 HTTP 请求,请求所需的资源(例如首页)
  5. 网站处理请求(下面会详细说明)并返回 HTTP 响应
  6. 浏览器开始渲染网页

HTTP(超文本传输协议)是计算机之间通过互联网进行通信以驱动网站运行的规则集合。它由万维网的发明者蒂姆·伯纳斯-李创建,他还创建了 HTML 标记语言和 URL 系统。一旦收到 HTTP 请求,Web 浏览器就会着手使用 HTML 渲染网页。页面所需的任何额外资源或其他页面都会经历同样的 HTTP 请求和 HTTP 响应循环,直到用户离开网站,网络连接被关闭。

当你请求访问像 learndjango.com 这样的网站时,你的 Web 浏览器首先会询问 DNS(域名系统),将域名翻译成 IP(互联网协议)地址——一个计算机用来在互联网上相互查找的唯一数字标识符。一旦 Web 浏览器知道了 IP 地址,它就会与该 IP 地址上的服务器建立网络连接,该服务器包含了所需网站的内容。浏览器发送一个针对所需资源(如首页)的 HTTP 请求,服务器则返回包含其内容的 HTTP 响应。

Web 框架的工作原理

网站分为两大类:静态网站动态网站。静态网站由独立的 HTML 文档组成——如果你的网站有十个页面,你就需要十个可以独立提供的 HTML 文档。这种方法只适用于非常小的网站。大多数网站是动态的,它们由数据库、HTML 模板和一个应用服务器组成,能够在将文件发送到浏览器之前动态生成文件。使用动态网站,相对较少的代码就能生成成百上千个网页。像 Django 这样的 Web 框架就是为动态网站设计的。

在万维网的早期,开发人员必须自己手工编码动态网站的所有组成部分,这种方式容易出错、经常不安全且性能低下。不久之后,像 Django 这样的 Web 框架应运而生,将这一过程标准化。Web 开发人员意识到许多任务是常规性的,由社区来执行和监控要比由个人来做更好。

从核心来说,像 Django 这样的 Web 框架有三个主要任务:

  1. 将 URL 映射到用于渲染页面的视图逻辑
  2. 提供与数据库交互的抽象层
  3. 通过模板系统显示类似 HTML 的代码

仅此而已!虽然 Web 框架的功能远不止这些,但从根本上说,它们都围绕这三个核心功能展开。

Django 架构

现在我们回顾了网站和 Web 框架的工作原理,接下来让我们看看 Django 的架构。其中有四个主要组成部分:URL、视图(View)、模型(Model)和模板(Template)。

从视觉上看,Django 的请求和响应周期如下图所示,其中实线表示必需的交互,虚线表示可选的交互。

Django 架构图

当一个 HTTP 请求从 Web 浏览器进入时,Django 中首先与之交互的是 URL 分发器urls.py 文件),它会在配置好的 URL 模式中进行搜索,并在匹配到第一个 视图views.py 文件)时停止。视图会组装请求的数据和样式,然后生成一个 HTTP 响应返回给 Web 浏览器。从技术上讲,这就是我们需要的全部。一个 Django 网站完全可以只靠 URL 分发器和视图来运行,就像本章后面将要演示的那样。

然而,更常见的情况是还有两个组件参与其中:模型模板。对于基于数据库的网站,视图会与模型models.py 文件)交互,模型定义数据库表、行为并支持对数据库的查询。这些数据随后被传回视图,在大多数情况下,视图会将其发送给模板进行渲染。模板主要是一个 HTML 文件,但也可以是任何基于文本的格式,包括 XML 和 JSON。一旦视图拥有了所有必要的信息,它就会向 Web 浏览器返回一个 HTTP 响应。

这个 Django 请求/响应循环会针对 Web 浏览器发出的每一个新的 HTTP 请求重复进行。

MVC 与 MVT

如果你以前构建过网站,你可能熟悉许多 Web 框架使用的模型-视图-控制器(MVC)模式,包括 Ruby on Rails、Spring(Java)、Laravel(PHP)和 ASP.NET(C#)。这是一种流行的方式,用于在内部将应用程序的数据、逻辑和显示分离成独立的组件,使开发人员更容易理解和维护。

在传统的 MVC 模式中,有三个主要组件:

  • 模型(Model):管理数据和核心业务逻辑
  • 视图(View):以特定格式呈现模型中的数据
  • 控制器(Controller):接收用户输入并执行特定于应用程序的逻辑

Django 的方法有时被称为模型-视图-模板(MVT),但更准确地说是一个包含 URL 配置的四部分模式,即 MVTU(模型-视图-模板-URL):

  • 模型(Model):管理数据和核心业务逻辑
  • 视图(View):描述哪些数据发送给用户,但不关心其呈现方式
  • 模板(Template):以 HTML 形式呈现数据,并可包含 CSS、JavaScript 和静态资源
  • URL 配置(URL Configuration):将正则表达式组件配置到视图

MVC 中的”视图”类似于 Django 中的”模板”,而 MVC 中的”控制器”在 Django 中被拆分为”视图”和”URL 分发器”。

如果你刚接触 Web 开发,MVC 和 MVT 之间的区别不会太重要:本书会展示 Django 的做事方式。不过,如果你是有 MVC 经验的 Web 开发人员,可能需要一些时间来转变思维,适应”Django 的方式”——相比于 MVC 方法,Django 的耦合更松散,修改也更容易。

初始设置

对于我们的第一个 Django 网站,我们将尽可能简单地构建一个”Hello, World”网站。虽然大多数 Django 网站都有 URL 分发器、视图、模型和模板,但从技术上讲,我们只需要 URL 分发器和视图。这就是我们在这里要做的,但在后续章节中,我们会逐步引入模板和模型。

首先,打开一个新的命令行 Shell,或使用 VSCode 内置的终端。对于后者,点击顶部的”Terminal”,然后选择”New Terminal”,它就会出现在 VSCode 界面的底部。

确保你不在任何现有的虚拟环境中——检查命令提示符前面是否没有用括号括起来的环境名。你甚至可以输入 deactivate 来彻底确认。然后,导航到桌面的 code 目录,创建一个 helloworld 目录,执行以下命令:

# Windows
$ cd onedrive\desktop\code
$ mkdir helloworld
$ cd helloworld

# macOS
$ cd ~/desktop/code
$ mkdir helloworld
$ cd helloworld

创建一个名为 .venv 的新虚拟环境,激活它,然后用 pip 安装 Django,就像我们在前一章中所做的那样。我们也可以顺便安装 Black。

# Windows
$ python -m venv .venv
$ .venv\Scripts\Activate.ps1
(.venv) $ python -m pip install django~=5.0.0
(.venv) $ python -m pip install black

# macOS
$ python3 -m venv .venv
$ source .venv/bin/activate
(.venv) $ python3 -m pip install django~=5.0.0
(.venv) $ python3 -m pip install black

现在,我们使用 Django 的 startproject 命令创建一个名为 django_project 的新项目。记得在命令末尾加上点号(.),以便将项目安装到当前目录中。

(.venv) $ django-admin startproject django_project .

让我们暂停一下,看看 Django 提供的默认项目结构。你可以用鼠标在桌面上打开新创建的目录来直观地查看。.venv 目录可能最初可见也可能不可见,因为它是一个”隐藏文件”——它确实存在,里面包含关于虚拟环境的信息。

├── django_project
│   ├── __init__.py
│   ├── asgi.py
│   ├── settings.py
│   ├── urls.py
│   └── wsgi.py
├── manage.py

Django 创建了一个 django_project 目录和一个 manage.py 文件。在 django_project 内部,有五个新文件:

  • __init__.py:表示该文件夹中的文件是一个 Python 包的一部分。没有这个文件,我们无法从其他目录导入文件——而在 Django 中,这是经常要做的事!
  • asgi.py:配置可选的 ASGI(异步服务器网关接口)应用程序
  • settings.py:控制我们 Django 项目的整体设置
  • urls.py:告诉 Django 响应浏览器或 URL 请求时构建哪些页面
  • wsgi.py:配置 WSGI(Web 服务器网关接口)应用程序——这是 Django 的默认设置

manage.py 文件不属于 django_project,它用于执行各种 Django 管理命令,例如运行本地 Web 服务器或创建新应用。

让我们来试试我们的新项目:执行 python manage.py runserver 启动 Django 的内置 Web 服务器。这个服务器适合开发使用,但不适合生产环境。我们会在本书后面的部署章节中更深入地探讨生产环境设置。

(.venv) $ python manage.py runserver
Watching for file changes with StatReloader
Performing system checks...

System check identified no issues (0 silenced).
You have 18 unapplied migration(s). Your project may not work properly until you
apply the migrations for app(s): admin, auth, contenttypes, sessions.
Run 'python manage.py migrate' to apply them.

June 28, 2024 - 16:43:31
Django version 5.0.6, using settings 'django_project.settings'
Starting development server at http://127.0.0.1:8000/
Quit the server with CTRL-BREAK.

如果你访问 http://127.0.0.1:8000/,应该会看到以下页面:

Django 欢迎页面

注意项目目录中已经创建了一个 db.sqlite3 文件,因为我们第一次尝试连接了 SQLite。目前它还是空的。

├── django_project
│   ├── __init__.py
│   ├── asgi.py
│   ├── settings.py
│   ├── urls.py
│   └── wsgi.py
├── db.sqlite3       # 新增
├── manage.py

迁移

在命令行中,我们仍然看到那个关于 18 个未应用迁移的警告信息。现在让我们来了解一下究竟发生了什么。迁移是 Django 自动创建的特殊脚本,用于追踪数据库的变更。随着项目的不断成长,Django 数据库模型(定义了数据库及其所有表的结构)经常会发生很多变化。Django 的迁移框架允许开发人员追踪这些随时间的变化,并使数据库与特定迁移文件中的配置保持同步。

当你使用 startproject 命令启动一个新项目时,Django 会包含几个内置的应用(稍后会详细说明什么是应用),它们会对数据库进行一些更改,包括 admin、auth、contenttypes 和 sessions。我们可以使用 migrate 管理命令将这些更改应用到本地数据库。先输入 Control + c 停止本地服务器。

$ python manage.py migrate
Operations to perform:
  Apply all migrations: admin, auth, contenttypes, sessions
Running migrations:
  Applying contenttypes.0001_initial... OK
  Applying auth.0001_initial... OK
  Applying admin.0001_initial... OK
  Applying admin.0002_logentry_remove_auto_add... OK
  Applying admin.0003_logentry_add_action_flag_choices... OK
  Applying contenttypes.0002_remove_content_type_name... OK
  Applying auth.0002_alter_permission_name_max_length... OK
  Applying auth.0003_alter_user_email_max_length... OK
  Applying auth.0004_alter_user_username_opts... OK
  Applying auth.0005_alter_user_last_login_null... OK
  Applying auth.0006_require_contenttypes_0002... OK
  Applying auth.0007_alter_validators_add_error_messages... OK
  Applying auth.0008_alter_user_username_max_length... OK
  Applying auth.0009_alter_user_last_name_max_length... OK
  Applying auth.0010_alter_group_name_max_length... OK
  Applying auth.0011_update_proxy_permissions... OK
  Applying auth.0012_alter_user_first_name_max_length... OK
  Applying sessions.0001_initial... OK

migrate 命令会应用所有可用的迁移并列出它们:Apply all migrations: admin, auth, contenttypes, sessions。输出结果包含每个应用的完整名称及其迁移脚本。例如,Applying contenttypes.0001_initial... OK 表示 contenttypes 应用中的 0001_initial 迁移脚本已成功运行。

重新启动开发服务器,警告信息就会消失。

(.venv) $ python manage.py runserver
Watching for file changes with StatReloader
Performing system checks...

System check identified no issues (0 silenced).

June 28, 2024 - 16:43:31
Django version 5.0.6, using settings 'django_project.settings'
Starting development server at http://127.0.0.1:8000/
Quit the server with CTRL-BREAK.

现在 db.sqlite3 文件中已经填充了 Django 内置的表和数据。如果你安装免费的 SQLite Viewer 扩展,就可以直观地查看它们。

我们会在本书后面更深入地介绍数据库。在这个阶段,重要的是理解 Django 使用迁移文件来控制数据库的变更,而 migrate 管理命令则是用来应用这些变更的。

创建应用

一个 Django 项目可以包含许多”应用”——这是一种保持代码整洁和可读性的组织技术。每个应用应控制一个独立的功能模块。如果你看一下 django_project/settings.py 文件,会发现 Django 已经为我们提供了六个内置应用。它们位于 django.contrib 目录下,控制着 admin、auth、contenttypes、sessions、messages 和 staticfiles 的功能。目前你不需要了解每个应用的具体作用。

# django_project/settings.py
INSTALLED_APPS = [
    "django.contrib.admin",
    "django.contrib.auth",
    "django.contrib.contenttypes",
    "django.contrib.sessions",
    "django.contrib.messages",
    "django.contrib.staticfiles",
]

没有任何强制性的规定要求你使用应用这种约定——如果你愿意,完全可以把所有代码写在一个文件里——但这种将逻辑分离的约定使得 Django 项目的结构化和理解变得更加容易。当我们想要向 Django 项目添加功能时,就会创建新的应用。例如,一个电商网站可能有一个用于用户认证的应用,另一个用于支付的应用,还有一个用于管理商品列表的应用。如何以及何时将功能拆分成多个应用是非常主观的,但一个好的经验法则是:当你感觉一个应用承担了太多功能时,就应该将功能拆分成多个独立的、各司其职的应用。

要创建一个新应用,在命令行中先用 Control + c 停止正在运行的服务器。然后使用 startapp 命令,后面跟上应用名称。在这个例子中,我们将使用 pages 这个名称。Django 中的一个最佳实践是:应用名称使用复数形式——pagespayments 等——除非这样命名不合适,比如 blog 应用。

(.venv) $ python manage.py startapp pages

如果你查看 django_project 目录,会发现 Django 在其中创建了一个新的 pages 目录,包含以下应用文件:

├── pages
│   ├── __init__.py
│   ├── admin.py
│   ├── apps.py
│   ├── migrations
│   │   └── __init__.py
│   ├── models.py
│   ├── tests.py
│   └── views.py

让我们来了解一下每个新 pages 应用文件的作用:

  • admin.py:用于配置内置的 Django Admin 应用
  • apps.py:应用的配置文件
  • migrations/:追踪 models.py 文件的任何更改,使其与数据库保持同步
  • models.py:定义数据库模型的地方,Django 会自动将它们翻译成数据库表
  • tests.py:用于编写应用特定的测试
  • views.py:处理 Web 应用的请求/响应逻辑的地方

尽管我们的新应用已经存在于 Django 项目中,但在我们显式地将其添加到 django_project/settings.py 文件之前,Django 并不知道它的存在。在你的文本编辑器中打开该文件,滚动到 INSTALLED_APPS 部分,你会看到六个内置的 Django 应用。在底部添加 pages

# django_project/settings.py
INSTALLED_APPS = [
    "django.contrib.admin",
    "django.contrib.auth",
    "django.contrib.contenttypes",
    "django.contrib.sessions",
    "django.contrib.messages",
    "django.contrib.staticfiles",
    "pages",  # 新增
]

你的第一个视图

我们将为第一个网站创建一个静态页面,输出”Hello, World!“文本。这个页面不涉及数据库,甚至不需要模板文件。相反,它是了解视图和 URL 在 Django 中如何工作的绝佳入门。

视图是一个 Python 函数,它接受一个 Web 请求并返回一个 Web 响应。响应可以是网页的 HTML 内容、重定向、404 错误、图片,或者几乎任何东西。当一个网页被请求时,Django 会自动创建一个包含请求元数据的 HttpRequest 对象。然后 Django 加载相应的视图,将 HttpRequest 作为第一个参数传递给视图函数。视图最终负责返回一个 HttpResponse 对象。

在我们的 pages 应用中,已经有一个名为 views.py 的文件,其默认内容如下:

# pages/views.py
from django.shortcuts import render

# Create your views here.

我们会在下一章中详细了解 render,但现在先将 pages/views.py 文件更新为以下代码:

# pages/views.py
from django.http import HttpResponse

def home_page_view(request):
    return HttpResponse("Hello, World!")

让我们逐行看一下:

  • django.http 模块中导入 HttpResponse
  • 定义一个名为 home_page_view 的函数。在 Python 中,函数名和变量名通常使用蛇形命名法(snake_case)——所有单词小写,用下划线分隔
  • 传递给视图的第一个参数是 HttpRequest 对象。为了可读性,习惯上将其命名为 request,但重要的是参数的顺序而非名称——从技术上讲,你可以把它叫做 req 或任何其他名字,它仍然能正常工作
  • 该视图返回一个 HttpResponse 对象,包含文本字符串”Hello, World!”

所有视图的工作方式都是这样的:首先为视图定义一个名称,命名 HttpRequest 对象(这里叫 request),然后返回一些内容。虽然可以向视图添加更多的逻辑和参数,但总体模式是相同的。

URL 分发器

有了视图之后,现在来配置对应的 URL。在你的文本编辑器中,在 pages 应用中创建一个名为 urls.py 的新文件,并写入以下代码:

# pages/urls.py
from django.urls import path
from .views import home_page_view

urlpatterns = [
    path("", home_page_view),
]

在第一行,我们从 Django 中导入 path 来驱动 URL 模式。通过使用 .views 来引用 views.py 文件,我们告诉 Django 在当前目录中查找 views.py 文件,并导入名为 home_page_view 的视图。注意 URL 模式中没有前导斜杠 /。Django 会自动为我们添加前导斜杠。

这里的 URL 模式包含两个部分:

  • 路由本身,这里由空字符串 "" 定义
  • 对视图的引用,即 home_page_view

换句话说,如果用户请求首页(由空字符串 "" 表示),Django 应该使用名为 home_page_view 的视图。

到这里我们就快完成了。最后一步是更新 django_project/urls.py 文件——这是通向其他 URL 模式的门户,与每个应用的 URL 模式是分开的。随着我们在本书后面构建越来越复杂的 Web 应用程序,这种架构模式会变得更加清晰。

Django 自动导入并设置了内置 admin 的路由。要添加额外的 URL 路径,我们从 django.urls 模块中导入 include 函数,然后设置对应的路径。这里,我们再次使用空字符串 "",并包含 pages 应用中的所有 URL。更多细节可以参考 Django 文档

更新后的代码如下所示:

# django_project/urls.py
from django.contrib import admin
from django.urls import path, include  # 新增

urlpatterns = [
    path("admin/", admin.site.urls),
    path("", include("pages.urls")),   # 新增
]

现在,每当用户访问首页(由这里的空字符串 "" 表示)时,Django 都会在 pages 应用中查找匹配的 URL 路由。

我们已经拥有了全部代码。要确认一切按预期工作,重新启动 Django 服务器:

(.venv) $ python manage.py runserver

如果你刷新浏览器中的 http://127.0.0.1:8000/,现在会显示”Hello, World!“这个文本。

Hello World 首页

就是这样!我们从头创建了一个 Django 项目,并且初次体验了 Django 的架构和典型模式:

  • 创建一个项目,然后在其中创建一个应用
  • 编写一个视图
  • 将视图连接到 URL 分发器

你完全可以用更少的代码、甚至在一个文件中编写一个”Hello, World”的 Django 应用——如果你对此好奇,可以查看 django-microframework 这个仓库。但在这个阶段,更重要的是开始内化 Django 的典型项目结构和模式。

Git

在前一章中,我们安装了版本控制系统 Git。现在让我们来使用它。第一步是初始化(即添加)Git 到我们的仓库。确保你已经用 Control + c 停止了本地服务器,然后运行 git init 命令。

(.venv) $ git init

如果你输入 git status,会看到自上次 Git 提交以来的更改列表。由于这是我们的第一次提交,这个列表包含整个目录的内容。

(.venv) $ git status
On branch main

No commits yet

Untracked files:
  (use "git add <file>..." to include in what will be committed)
    .venv
    django_project/
    db.sqlite3
    manage.py
    pages/

nothing added to commit but untracked files present (use "git add" to track)

注意虚拟环境 .venv 也被包含在内。最佳实践是不将虚拟环境纳入 Git 版本控制,因为它可能包含 API 密钥等敏感信息。

解决方案是在项目根目录下创建一个名为 .gitignore 的新文件,告诉 Git 应该忽略哪些内容。文件名开头的点号表示这是一个”隐藏”文件——文件本身仍然存在,但这种命名方式向开发者传达了一种信息:它的内容很可能与配置有关,不应纳入版本控制。

现在你的项目结构应该是这样的:

├── django_project
│   ├── __init__.py
│   ├── asgi.py
│   ├── settings.py
│   ├── urls.py
│   └── wsgi.py
├── pages
│   ├── migrations
│   │   └── __init__.py
│   ├── __init__.py
│   ├── admin.py
│   ├── apps.py
│   ├── models.py
│   ├── tests.py
│   ├── urls.py
│   └── views.py
├── .gitignore        # 新增
├── db.sqlite3
├── manage.py

在这个新的 .gitignore 文件中,添加一行内容:.venv/

# .gitignore
.venv/

如果你再次运行 git status,会发现 .venv 已经不再出现了。它被 Git”忽略”了。在专业项目中,.gitignore 文件通常相当长。出于效率和安全性考虑,通常会有不少目录和文件应该从版本控制中排除。不过对于本书中的学习项目来说,优化并不是重点。

与此同时,我们确实需要记录虚拟环境中安装的包。当前的最佳实践是创建一个 requirements.txt 文件来保存这些信息。pip freeze 命令会输出当前虚拟环境的内容,配合 > 操作符,我们可以一步完成所有操作:将输出内容写入一个名为 requirements.txt 的新文件中。如果你的服务器仍在运行,先输入 Ctrl + c 和回车退出,然后再输入这个命令。

(.venv) $ pip freeze > requirements.txt

一个新的 requirements.txt 文件会出现,里面包含所有已安装的包及其依赖。如果你查看这个文件,会发现有九个包,尽管我们只安装了 Django 和 Black 这两个。这是因为 Django 和 Black 还依赖于其他包。在安装一个 Python 包时,通常会同时安装多个依赖包。由于手动跟踪所有包很困难,所以 requirements.txt 文件至关重要。

# requirements.txt
asgiref==3.8.1
black==24.4.2
click==8.1.7
Django==5.0.6
mypy-extensions==1.0.0
packaging==24.1
pathspec==0.12.1
platformdirs==4.2.2
sqlparse==0.5.0

接下来,我们要执行第一次 Git 提交,以保存所有最近的更改。Git 有大量的选项和标志。例如,要添加所有最近的更改,可以使用 git add -A。然后,要提交更改,我们使用 -m 标志(“message”)来描述发生了什么变化。每次提交时都添加描述性信息非常重要,因为大多数项目很容易就有成百上千个提交。每次添加描述性的消息有助于后续的调试工作,因为你可以通过搜索提交历史来定位问题。

(.venv) $ git add -A
(.venv) $ git commit -m "initial commit"

结论

恭喜!本章涵盖了大量的内容,从互联网和 Web 框架的工作原理开始,到 Django 的架构,然后我们构建了第一个 Django 网站,同时学习了应用、视图、URL 和 Django 内置的 Web 服务器。我们还使用了 Git 来追踪更改、创建了 .gitignore 文件,并生成了 requirements.txt 文件。

如果你遇到问题,可以对照官方仓库中的代码进行比对。

请继续进入下一章,我们将构建一个更复杂的 Django 应用程序,使用模板和更高级的基于函数的视图,同时开始引入测试。