第 4 章:公司网站

在本章中,我们将构建第三个项目——一个公司网站,同时进一步学习模板知识,引入基于类的视图(CBV),并编写更高级的测试。这是我们在接触数据库和 Django 模型之前的最后一个项目,因此这是巩固之前所学知识、探索 Django 另外三个组成部分——视图、URL 和模板——更多功能的好机会。

初始设置

我们的初始设置现在应该开始变得熟悉了,包含以下步骤:

  • 为代码创建一个名为 company 的新目录并进入该目录
  • 创建一个名为 .venv 的新虚拟环境并激活它
  • 安装 Django 和 Black
  • 创建一个名为 django_project 的新 Django 项目
  • 创建一个名为 pages 的新应用

在命令行中,确保你不在现有的虚拟环境中工作。如果命令行提示符(Windows 上是 >,macOS 上是 %)之前有文本,说明你在!请务必输入 deactivate 退出。

在新的命令行终端中,导航到桌面上的 code 文件夹,创建一个名为 company 的新文件夹,进入该目录,并激活一个名为 .venv 的新 Python 虚拟环境。

# Windows
$ cd onedrive\desktop\code
$ mkdir company
$ cd company
$ python -m venv .venv
$ .venv\Scripts\Activate.ps1
(.venv) $

# macOS
$ cd ~/desktop/code
$ mkdir company
$ cd company
$ python3 -m venv .venv
$ source .venv/bin/activate
(.venv) $

接下来,安装 Django 和 Black,创建一个名为 django_project 的新项目,并创建一个名为 pages 的新应用。我们一直将所有应用称为”pages”,因为它们一直用于相对静态的页面。在未来的项目中,我们将从数据库中填充页面,应用名称将反映这种新的动态。

(.venv) $ python -m pip install django~=5.0.0
(.venv) $ python -m pip install black
(.venv) $ django-admin startproject django_project .
(.venv) $ python manage.py startapp pages

请记住,即使我们添加了新应用,Django 也不会自动识别它,直到在 django_project/settings.pyINSTALLED_APPS 设置中明确添加。现在打开文本编辑器,将其添加到底部:

# 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",  # 新增
]

使用 migrate 初始化数据库,然后用 runserver 启动本地 Web 服务器。

(.venv) $ python manage.py migrate
(.venv) $ python manage.py runserver

然后导航到 http://127.0.0.1:8000/ 查看 Django 欢迎页面。

项目级别的模板

之前我们已经看到,Django 期望模板文件位于应用内的 templates 目录中,并且最佳实践是通过再次添加目录名来进一步命名空间化。换句话说,pages 应用的模板文件应该位于 pages/templates/pages/ 目录中。

然而,许多 Django 开发者青睐另一种方法:创建一个单一的项目级别 templates 目录,并将所有模板放置在其中。这样更容易在同一个位置查找和更新所有模板。通过调整 django_project/settings.py 文件,我们可以告诉 Django 在这个目录中查找模板。

首先,按 Control+c 停止正在运行的服务器。然后,创建一个名为 templates 的目录。

(.venv) $ mkdir templates

接下来,我们需要更新 django_project/settings.py,告诉 Django 新的 templates 目录在哪里。这需要在 TEMPLATES 下对 "DIRS" 配置做一行修改。

# django_project/settings.py
TEMPLATES = [
    {
        ...
        "DIRS": [BASE_DIR / "templates"],  # 新增
        ...
    },
]

我们将在本书的其余部分使用这种组织模板的方式。在 templates 目录中创建一个名为 home.html 的新文件。你可以在文本编辑器中完成:在 Visual Studio Code 中,转到屏幕左上角,点击”File”,然后点击”New File”。确保在正确的位置命名并保存文件。

目前,home.html 文件将包含一个简单的标题。

<!-- templates/home.html -->
<h1>Company Homepage</h1>

模板完成了!下一步是配置 URL 和视图文件。

基于函数的视图和 URL

先写视图还是先写 URL,完全由开发者决定。最终,我们需要两者来显示网页,因此随着时间的推移,决定执行顺序变成了个人偏好。这里,我们从 pages 应用中的视图开始。

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

def home_page_view(request):  # 新增
    return render(request, "home.html")

这段代码看起来应该很熟悉,和上一章一样。我们使用了顶部导入的 render() 快捷函数。然后创建了视图 home_page_view,并将其第一个参数(HttpRequest 对象)命名为 request。我们返回 request 对象并指定了正确的模板文件 home.html

接下来是 URL 配置,包括作为网站入口点的项目级 urls.py 文件和应用级 urls.py 文件(包含首页的具体路由和视图)。

django_project/urls.py 文件是所有 URL 请求进入我们项目的初始入口点。我们必须在顶部导入 include 函数,然后使用它来包含 pages 应用中的 URL 路由,这些路由将设置为空字符串 ""

# 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")),  # 新增
]

应用级别的 pages/urls.py 文件导入视图 home_page_view,并将其设置为空字符串 "" 的 URL 路径。

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

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

使用 runserver 命令启动开发 Web 服务器。

(.venv) $ python manage.py runserver

如果你导航到 http://127.0.0.1:8000/,首页现在可见。

!基于函数的视图:公司首页

很简单,对吧?到目前为止,唯一引入的新概念是项目级别的模板目录。

模板上下文、标签和过滤器

让我们为首页视图添加一个模板上下文,然后试用一些 Django 的内置标签和过滤器。标签执行更复杂的操作,如循环、条件判断和模板继承。而过滤器则用于执行更简单的转换,修改变量的显示方式,例如格式化日期、截断文本或将字符串转换为大写。标签和过滤器太多了,不可能全部记住;你只需要知道,对于几乎任何内容显示需求,都有大量原生解决方案可用。

模板上下文具有键值对的字典结构。为了演示,我们可以添加两个:一个包含三个小部件的 inventory_list 和一个故意混合大小写字母的问候文字字符串 greeting

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

def home_page_view(request):
    context = {  # 新增
        "inventory_list": ["Widget 1", "Widget 2", "Widget 3"],
        "greeting": "THAnk you FOR visitING.",
    }
    return render(request, "home.html", context)

用以下代码更新 home.html 模板文件。[now](https://docs.djangoproject.com/en/5.0/ref/templates/builtins/#now) 标签使用 [DATE_FORMAT](https://docs.djangoproject.com/en/5.0/ref/settings/#std-setting-DATE_FORMAT)(几种显示选项之一)显示当前日期和/或时间。接下来,使用 [length](https://docs.djangoproject.com/en/5.0/ref/templates/builtins/#length) 过滤器显示 inventory_list 中的项目数量,该过滤器既适用于字符串也适用于列表。

然后,在 [for](https://docs.djangoproject.com/en/5.0/ref/templates/builtins/#for) 标签中遍历每个项目。通用语法是 {% for item in item_list %},其中 item 是代表循环中当前项的变量名,item_list 是我们要循环的序列。同样关键的是,必须包含 {% endfor %} 标签来结束任何 for 循环。这里,序列名为 inventory_list。我们可以任意命名变量,但像 item 这样的描述性名称是常见选择,能使代码更容易理解。最后,我们使用 [title](https://docs.djangoproject.com/en/5.0/ref/templates/builtins/#title) 过滤器将字符串转换为标题大小写——每个单词以大写字母开头,后面跟小写字母。

<!-- templates/home.html -->
<h1>Company Homepage</h1>
<p>The current date and time is: {% now "DATETIME_FORMAT" %}</p>
<p>There are {{ inventory_list|length }} items of inventory.
<ul>
{% for item in inventory_list %}
    <li>{{ item }}</li>
{% endfor %}
</ul>
<p>{{ greeting|title }}</p>
{% comment %}Add more content here!{% endcomment %}

本地 Web 服务器应该仍然在后台使用 runserver 命令运行,所以你只需刷新网页即可看到更改。

!带上下文的公司首页

这里的目的是不要让你被许多需要记忆的新 Django 功能所淹没。需要记住的标签和过滤器太多了。相反,要强调的是,对于你想到的几乎任何 Web 开发任务,Django 很可能都有一个内置的解决方案,这就是为什么官方文档不可或缺,也是专业 Django 开发者日常开发的一部分。

基于类的视图和通用基于类的视图

Django 社区中最接近宗教式辩论的问题是关于基于函数的视图(我们一直在使用的)和基于类的视图。早期版本的 Django 只提供了基于函数的视图,可以说它们比基于类的视图更容易理解,因为它们模拟了 HTTP 请求/响应周期。本书一开始只使用基于函数的视图正是出于这个原因。

基于函数的视图确实有缺点。它们缺乏简便的继承方式,这意味着开发者必须在每个视图中重复相同的代码片段。这违反了 Django 通用的 DRY(Don’t Repeat Yourself,不要重复自己)原则。但即使不重复相同的代码,基于函数的视图在实际项目中通常也会变得冗长,因此难以理解。看到包含十行、二十行甚至更多逻辑的视图是很常见的,这变得难以梳理。

Django 早期开发中引入了通用的基于函数的视图,以抽象常见模式并避免代码重复。例子包括:

  • 编写显示单个模板的视图(就像我们刚才做的)
  • 编写列出数据库模型中所有对象的视图
  • 编写仅显示模型中单个详细项目的视图
  • 编写创建、更新或删除对象的视图

通用的基于函数的视图的问题在于,没有简便的方法来扩展或自定义它们。随着项目规模的增长,这变得越来越成问题。

Django 添加了基于类的视图和通用基于类的视图来帮助实现代码可重用性,同时保留了基于函数的视图。类是 Python 中面向对象编程(OOP)和继承的基础部分,因此一个类可以从另一个类继承属性和方法。这意味着我们不需要在一个地方包含视图的所有逻辑,而是可以抽象出常见模式。

如果你需要 Python 中类的介绍或复习,我建议查看官方 Python 文档,其中有一个关于类及其用法的优秀教程。

一旦你使用通用基于类的视图一段时间,它们就会成为编写代码的优雅而高效的方式。你通常只需修改一个方法就能实现自定义行为,而不必从头重写所有内容,这使得理解他人的代码更加容易。然而,这是以复杂性为代价的,并且需要一种信念上的飞跃,因为需要很长时间才能理解它们在底层是如何工作的。有一个名为 Classy Class-Based Views 的整个网站致力于帮助 Django 开发者解读通用基于类的视图。

Django 代码库本身也已转变为主要使用基于类的视图和通用基于类的视图。通用的基于函数的视图在 Django 1.3 中被弃用,并在版本 1.5 中被完全移除。

这些年来 Django 的这些变化导致现在有三种不同的方式在 Django 中编写视图:基于函数的视图、基于类的视图和通用基于类的视图。这对初学者来说无疑是令人困惑的。

本书的早期版本完全专注于通用基于类的视图,但这个版本两者都包含。一个 Django 开发者需要理解每种方法的工作原理,即使随着时间的推移他们无疑会形成个人偏好。

TemplateView)

让我们为公司网站创建第二个网页,这次使用 [TemplateView](https://docs.djangoproject.com/en/5.0/ref/class-based-views/base/#django.views.generic.base.TemplateView))——一个通用的基于类的视图。这将是一个关于页面,同时利用模板上下文和 Django 模板语言。

views.py 文件的顶部,从 django.views.generic 模块中导入 [TemplateView](https://docs.djangoproject.com/en/5.0/ref/class-based-views/base/#django.views.generic.base.TemplateView)。然后创建一个类 AboutPageView,它继承 TemplateView 并指定一个模板 about.html。在 Python 中,类的命名惯例是使用”驼峰命名法(CamelCase)“,即每个单词的首字母大写,单词之间没有下划线。

# pages/views.py
from django.shortcuts import render
from django.views.generic import TemplateView  # 新增

def home_page_view(request):
    context = {
        "inventory_list": ["Widget 1", "Widget 2", "Widget 3"],
        "greeting": "THAnk you FOR visitING.",
    }
    return render(request, "home.html", context)

class AboutPageView(TemplateView):  # 新增
    template_name = "about.html"

接下来,更新 pages/urls.py 文件以显示新视图。我们导入 AboutPageView 并将路由设置为 about/,同时指定 AboutPageView 作为视图。

# pages/urls.py
from django.urls import path
from .views import home_page_view, AboutPageView  # 新增

urlpatterns = [
    path("about/", AboutPageView.as_view()),  # 新增
    path("", home_page_view),
]

注意添加了 as_view() 方法,它返回一个可调用的视图。配置基于类的视图与基于函数的视图的 URL 之间的唯一本质区别就是必须添加这个方法。

最后一步是创建模板文件 about.html。在你的文本编辑器中,在现有的 templates 目录中添加这个新文件,代码如下:

<!-- templates/about.html -->
<h1>Company About Page</h1>

确保本地服务器正在运行,然后在浏览器中导航到 127.0.0.1:8000/about/

!公司关于页面

很简单,对吧?

get_context_data()

Django 中最强大、最有用、最常用的方法之一是 get_context_data()。这是在通用基于类的视图中更新模板上下文的推荐方法。现在让我们使用它在关于页面中添加上下文数据。

# pages/views.py
...
class AboutPageView(TemplateView):
    template_name = "about.html"

    def get_context_data(self, **kwargs):  # 新增
        context = super().get_context_data(**kwargs)
        context["contact_address"] = "123 Main Street"
        context["phone_number"] = "555-555-5555"
        return context
...

首先,我们重写了现有的 get_context_data() 方法。第一个参数是 self,第二个是 **kwargs,允许我们传入关键字参数。这就是我们如何向上下文中添加键/值对的方法。

下一步是设置一个名为 context 的变量,其中包含上下文的现有值。怎么做呢?在 get_context_data 上调用 super() 并包含任何关键字参数。然后我们添加两个键 contact_addressphone_number 及其对应的值。最后一步始终是显式返回更新后的上下文。

要在模板中渲染上下文变量,我们使用双大括号 { }

<!-- templates/about.html -->
<h1>Company About Page</h1>
<p>The company address is {{ contact_address }} and the phone number is
{{ phone_number }}.</p>

刷新浏览器中的关于页面以查看显示的信息。

!带上下文的公司关于页面

目前,使用通用基于类的视图可能看起来没必要,但它们的真正威力将在下一章我们开始使用数据库时显现出来。

模板继承

这一章全是关于模板和视图的。我们已经涵盖了很多信息:模板上下文、模板标签和过滤器,以及基于类的视图。然而,模板还有一个强大的功能——它们可以被扩展。

如果你思考大多数网站,相同的内容会出现在每个页面上(页眉、页脚等)。作为开发者,如果我们能有一个统一的地方放置页眉代码,而所有其他模板都能继承它,那该多好?没错,我们可以!

templates 目录中,创建一个 base.html 文件,其中包含一个指向首页和关于页面的链接的页眉。这是父模板,所有其他子模板都将继承自它。要定义哪些区域可以被覆盖,我们将使用 [block](https://docs.djangoproject.com/en/5.0/ref/templates/builtins/#block) 标签,语法为 {% block content %}{% endblock %}block 标签内的任何内容都可以在子模板中被覆盖。

<!-- templates/base.html -->
<header>
    <a href="/">Home</a> |
    <a href="/about">About</a>
</header>
{% block content %}{% endblock %}

[extends](https://docs.djangoproject.com/en/5.0/ref/templates/builtins/#extends) 标签允许我们通过指定父模板来建立模板之间的父子关系。将其添加到 home.htmlabout.html 模板的顶部。然后使用 {% [block](https://docs.djangoproject.com/en/5.0/ref/templates/builtins/#block) content %}{% endblock %} 标签定义子模板内容。

<!-- templates/home.html -->
{% extends "base.html" %}
{% block content %}
<h1>Company Homepage</h1>
<p>The current date and time is: {% now "DATETIME_FORMAT" %}</p>
<p>There are {{ inventory_list|length }} items of inventory.
<ul>
{% for item in inventory_list %}
    <li>{{ item }}</li>
{% endfor %}
</ul>
<p>{{ greeting|title }}</p>
{% comment %}Add more content here!{% endcomment %}
{% endblock %}
<!-- templates/about.html -->
{% extends "base.html" %}
{% block content %}
<h1>Company About Page</h1>
<p>The company address is {{ contact_address }} and the phone number is
{{ phone_number }}.</p>
{% endblock %}

在浏览器中刷新每个网页以查看结果:

!带基础模板的公司首页

!带基础模板的公司关于页面

每个页面现在都包含 base.html 中的页眉,上面有首页和关于页面的导航链接。

命名 URL

经验丰富的 Web 开发者可能已经注意到我们当前页面链接方式的一个问题。我们在 views.pyurls.py 文件中硬编码了 URL 路径。在每个地方,我们为首页指定了 /,为关于页面指定了 about/。如果我们在一个地方更改了 URL 路径而没有在其他地方更改,会发生什么?我们会得到 404 页面未找到的错误。

Django 非常重视在单一位置定义逻辑的理念。在这种情况下,我们希望只在一个地方引用 URL 及其关联视图。为此,我们可以为 URL 添加一个 names/#naming-[url](https://docs.djangoproject.com/en/5.0/ref/templates/builtins/#url)-patterns)[path()](https://docs.djangoproject.com/en/5.0/ref/urls/#path) 函数接受以下参数:path(route, [view](https://docs.djangoproject.com/en/5.0/ref/class-based-views/base/#django.views.generic.base.View.as_view), kwargs=None, [name](https://docs.djangoproject.com/en/5.0/topics/http/urls/#naming-url-patterns)=None)。默认情况下,kwargsname 设置为 None,但我们可以在这里更新 name

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

urlpatterns = [
    path("about/", AboutPageView.as_view(), name="about"),  # 新增
    path("", home_page_view, name="home"),  # 新增
]

现在,每当我们想要引用特定的 URL 路径时,都可以通过内置的 [url](https://docs.djangoproject.com/en/5.0/ref/templates/builtins/#url) 模板标签,在模板中使用命名 URL。用下面的代码更新 base.html 文件。

<!-- templates/base.html -->
<header>
    <a href="{% url 'home' %}">Home</a> |
    <a href="{% url 'about' %}">About</a>
</header>
{% block content %}{% endblock %}

如果你在浏览器中刷新网站,两个页面及其链接和以前一样工作。URL 路径现在只在一个位置设置——在 urls.py 文件中。命名 URL 使你的项目更容易维护和修改,因为 URL 模式的更改不需要在代码的多个地方进行修改。这是一个应该在所有 Django 项目中采用的最佳实践。

测试

在上一章中,我们为每个网页编写了一个单元测试,检查它是否返回 HTTP 200 状态码。让我们先快速回顾一下,然后为网站添加更稳健的测试。用下面的代码更新 pages/tests.py 文件:

# pages/tests.py
from django.test import SimpleTestCase

class HomepageTests(SimpleTestCase):
    def test_url_exists_at_correct_location(self):
        response = self.client.get("/")
        self.assertEqual(response.status_code, 200)

class AboutpageTests(SimpleTestCase):
    def test_url_exists_at_correct_location(self):
        response = self.client.get("/about/")
        self.assertEqual(response.status_code, 200)

然后,按 Control+c 退出本地 Web 服务器,在命令行中输入 python manage.py test 来运行测试。

(.venv) $ python manage.py test
Found 2 test(s).
System check identified no issues (0 silenced).
..
----------------------------------------------------------------------
Ran 2 tests in 0.003s
OK

到目前为止一切顺利。与上一章的个人网站相比,公司网站有什么变化?现在我们每个 URL 路由都有 URL 名称,所以我们应该检查它们是否按预期工作。我们可以使用方便的 Django 工具函数 reverseresolvers/#reverse)。它不会去访问 URL 路径,而是查找 URL 名称。通常,硬编码 URL 是一个坏主意,尤其是在模板中。我们可以通过使用 reverse 来避免这种情况。

打开文本编辑器中现有的 pages/tests.py 文件,添加以下代码:

# pages/tests.py
from django.test import SimpleTestCase
from django.urls import reverse  # 新增

class HomepageTests(SimpleTestCase):
    def test_url_exists_at_correct_location(self):
        response = self.client.get("/")
        self.assertEqual(response.status_code, 200)

    def test_url_available_by_name(self):  # 新增
        response = self.client.get(reverse("home"))
        self.assertEqual(response.status_code, 200)

class AboutpageTests(SimpleTestCase):
    def test_url_exists_at_correct_location(self):
        response = self.client.get("/about/")
        self.assertEqual(response.status_code, 200)

    def test_url_available_by_name(self):  # 新增
        response = self.client.get(reverse("about"))
        self.assertEqual(response.status_code, 200)

顶部我们导入了 SimpleTestCase(因为我们没有使用数据库),然后导入了 reverseresolvers/#reverse) 函数。有两个测试类分别对应每个网页。HomepageTests 检查首页在 / 处返回 200 状态码,然后检查对命名 URL "home" 调用 reverse 是否也做同样的事情。AboutpageTests 类中对关于页面的两个测试模式重复了同样的操作。

运行测试以确认它们正确工作。

(.venv) $ python manage.py test
Found 4 test(s).
System check identified no issues (0 silenced).
..
----------------------------------------------------------------------
Ran 4 tests in 0.005s
OK

我们已经测试了 URL 位置和名称,但没有测试模板。让我们确保每个页面使用了正确的模板 home.htmlabout.html,并且它们分别包含预期的文本 <h1>Company Homepage</h1><h1>Company About Page</h1>。我们可以使用 [assertTemplateUsed](https://docs.djangoproject.com/en/5.0/topics/testing/tools/#django.test.SimpleTestCase.assertTemplateUsed)[assertContains](https://docs.djangoproject.com/en/5.0/topics/testing/tools/#django.test.SimpleTestCase.assertContains) 来实现这一点。

# pages/tests.py
from django.test import SimpleTestCase
from django.urls import reverse

class HomepageTests(SimpleTestCase):
    def test_url_exists_at_correct_location(self):
        response = self.client.get("/")
        self.assertEqual(response.status_code, 200)

    def test_url_available_by_name(self):
        response = self.client.get(reverse("home"))
        self.assertEqual(response.status_code, 200)

    def test_template_name_correct(self):  # 新增
        response = self.client.get(reverse("home"))
        self.assertTemplateUsed(response, "home.html")

    def test_template_content(self):  # 新增
        response = self.client.get(reverse("home"))
        self.assertContains(response, "<h1>Company Homepage</h1>")

class AboutpageTests(SimpleTestCase):
    def test_url_exists_at_correct_location(self):
        response = self.client.get("/about/")
        self.assertEqual(response.status_code, 200)

    def test_url_available_by_name(self):
        response = self.client.get(reverse("about"))
        self.assertEqual(response.status_code, 200)

    def test_template_name_correct(self):  # 新增
        response = self.client.get(reverse("about"))
        self.assertTemplateUsed(response, "about.html")

    def test_template_content(self):  # 新增
        response = self.client.get(reverse("about"))
        self.assertContains(response, "<h1>Company About Page</h1>")

最后一次运行测试以检查我们的新工作。所有测试都应该通过。

(.venv) $ python manage.py test
Found 8 test(s).
System check identified no issues (0 silenced).
..
----------------------------------------------------------------------
Ran 8 tests in 0.006s
OK

有经验的程序员可能会注意到我们的测试代码重复性很高。例如,我们为全部八个测试都设置了 response。一般来说,遵循 DRY(Don’t Repeat Yourself)编码风格是个好主意,但单元测试在独立且高度详细的情况下效果最好。随着测试套件的扩展,出于性能原因,将多个断言合并到较少数量的测试中可能更有意义。

我们将来会做更多的测试,特别是当我们开始使用数据库时。目前,重要的是要看到每次向 Django 项目添加新功能时添加测试是多么容易和重要。

Git 和 GitHub

是时候用 Git 跟踪我们的更改并将它们推送到 GitHub 了。我们首先初始化目录并检查更改的状态。

(.venv) $ git init
(.venv) $ git status

然后,创建一个 .gitignore 文件,指示 Git 不要跟踪哪些内容。我们将关注三个方面:包含虚拟环境的 .venv 目录、包含编译字节码的 __pycache__ 目录以及数据库文件 db.sqlite3

.venv/
__pycache__/
db.sqlite3

下一步是创建一个 requirements.txt 文件,列出虚拟环境中的内容。

(.venv) $ pip freeze > requirements.txt
(.venv) $ git status

最后一步是再次运行 git status 确认 requirements.txt 被包含在内,而 .gitignore 文件中的三个项目被忽略。然后,添加所有预期的文件和目录,并附上一个初始提交信息。

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

在 GitHub 上创建一个名为 company-website 的新仓库,确保选择”Private”单选按钮。然后点击”Create repository”按钮。

在下一页上,滚动到”…or push an existing repository from the command line”处。将那里的两条命令复制粘贴到你的终端中。

命令看起来应该像下面这样,只是用户名 wsvincent 会被替换为你的 GitHub 用户名。

(.venv) $ git remote add origin https://github.com/wsvincent/company-website.git
(.venv) $ git branch -M main
(.venv) $ git push -u origin main

结论

恭喜你构建并部署了第三个 Django 项目!这一次,我们同时使用了基于函数的视图和通用基于类的视图来构建网站。我们引入了模板继承、命名 URL,并添加了更高级的测试。本章的完整源代码可在 GitHub 上找到,供你参考。在下一章中,我们将转向第一个基于数据库的项目——一个留言板网站,并见证 Django 真正大放异彩的地方。