样式学习日志
在此之前,我们有意忽略了样式设计,而是首先关注学习日志的功能。这是一种很好的开发方法,因为只有当应用程序能正常运行时,它才是有用的。一旦应用程序可以运行,它的外观就至关重要,这样人们才会愿意使用它。
在本节中,我们将安装 django-bootstrap5 应用程序并将其添加到项目中。然后,我们将使用它为项目中的各个页面设计样式,这样所有页面就会有一致的外观和感觉。
django-bootstrap5 应用程序
我们将使用 django-bootstrap5 将 Bootstrap 集成到我们的项目中。该应用程序会下载所需的 Bootstrap 文件,将它们放到项目中的适当位置,并在项目模板中提供样式指令。
要安装 django-bootstrap5,请在活动的虚拟环境中发出以下命令:
(ll_env)learning_log$ pip install django-bootstrap5
--snip--
Successfully installed beautifulsoup4-4.11.1 django-bootstrap5-21.3
soupsieve-2.3.2.post1
接下来,我们需要将 django-bootstrap5 添加到 settings.py 中的 INSTALLED_APPS 中:
--snip--
INSTALLED_APPS = [
# My apps.
'learning_logs',
'accounts',
# Third party apps.
'django_bootstrap5',
# Default django apps.
'django.contrib.admin',
--snip--
为其他开发者创建的应用程序新建一个名为 "第三方应用程序 "的部分,并在该部分中添加 "django_bootstrap5"。确保将该部分放在 "我的应用程序 "之后、包含 Django 默认应用程序的部分之前。
使用 Bootstrap 设计学习日志
Bootstrap 集合了大量造型工具。它还有许多模板,您可以将其应用到您的项目中以创建整体风格。使用这些模板要比使用单个样式工具简单得多。要查看 Bootstrap 提供的模板,请访问 https://getbootstrap.com 并单击 Examples。我们将使用导航栏静态模板,它提供了一个简单的顶部导航栏和页面内容的容器。
图 20-1 显示了将 Bootstrap 模板应用到 base.xhtml 并对 index.xhtml 稍作修改后主页的样子。

修改base.html
我们需要使用 Bootstrap 模板重写 base.xhtml。我们将分段开发新的 base.xhtml。该文件较大,您可能需要从 https://ehmatthes.github.io/pcc_3e 上的在线资源复制该文件。如果您复制了该文件,仍应通读以下部分,以了解所做的更改。
定义 HTML 标头
我们要对 base.xhtml 进行的第一项修改是定义文件中的 HTML 头文件。我们还将添加一些在模板中使用 Bootstrap 的要求,并给页面加上标题。删除 base.xhtml 中的所有内容,代之以以下代码:
❶ <!doctype html>
❷ <html lang="en">
❸ <head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initialscale=1">
❹ <title>Learning Log</title>
❺ {% load django_bootstrap5 %}
{% bootstrap_css %}
{% bootstrap_javascript %}
</head>
首先,我们将该文件声明为用英语❷ 编写的 HTML 文档。HTML 文件分为两个主要部分:头部和主体。文件头部以开头的 <head> 标记❸ 开始。HTML 文件的头部并不包含页面的任何内容;它只是告诉浏览器正确显示页面所需的信息。我们为页面加入一个 <title> 元素,只要打开学习日志❹,它就会显示在浏览器的标题栏中。
在关闭 head 部分之前,我们将加载 django-bootstrap5 ❺中可用的模板标签集合。模板标签 {% bootstrap_css %}
是 django-bootstrap5 的自定义标签;它加载了实现 Bootstrap 风格所需的所有 CSS 文件。接下来的标签会启用页面上可能用到的所有交互行为,例如可折叠导航栏。结尾的 </head> 标签出现在最后一行。
现在,所有 Bootstrap 风格选项都可以在继承自 base.xhtml 的任何模板中使用。如果要使用来自 django-bootstrap5 的自定义模板标记,每个模板都需要包含 {% load django_bootstrap5 %}
标记。
定义导航条
定义页面顶部导航栏的代码相当长,因为它必须在狭窄的手机屏幕和宽大的台式机显示器上都能正常工作。我们将对导航栏进行分段处理。
下面是导航栏的第一部分:
--snip--
</head>
<body>
❶ <nav class="navbar navbar-expand-md navbar-light bg-light
mb-4 border">
<div class="container-fluid">
❷ <a class="navbar-brand" href="{% url 'learning_logs:in
dex' %}">
Learning Log</a>
❸ <button class="navbar-toggler" type="button" data-bs-t
oggle="collapse"
data-bs-target="#navbarCollapse" aria-controls="navba
rCollapse"
aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
❹ <div class="collapse navbar-collapse" id="navbarCollap
se">
❺ <ul class="navbar-nav me-auto mb-2 mb-md-0">
❻ <li class="nav-item">
❼ <a class="nav-link" href="{% url 'learning_logs:
topics' %}">
Topics</a></li>
</ul> <!-- End of links on left side of navbar -->
</div> <!-- Closes collapsible parts of navbar -->
</div> <!-- Closes navbar's container -->
</nav> <!-- End of navbar -->
❽ {% block content %}{% endblock content %}
</body>
</html>
第一个新元素是开始 <body> 标记。 HTML 文件的正文包含用户将在页面上看到的内容。 接下来我们有一个 <nav> 元素,它打开页面顶部导航栏的代码❶。 此元素中包含的所有内容均根据选择器 navbar、navbarexpand-md 以及您在此处看到的其余部分定义的 Bootstrap 样式规则进行样式设置。 选择器确定特定样式规则适用于页面上的哪些元素。 navbar-light 和 bg-light 选择器使用浅色主题背景设计导航栏。 mb-4中的 mb 是 margin-bottom 的缩写;该选择器确保导航栏和页面其余部分之间出现一点空间。 边框选择器在浅色背景周围提供了一个细边框,以使其与页面的其余部分稍微分开。
下一行的 <div> 标记将打开一个可调整大小的容器,该容器将容纳整个导航栏。 术语 div 是除法的缩写; 您可以通过将网页分为几个部分并定义适用于该部分的样式和行为规则来构建网页。开始 <div> 标记中定义的任何样式或行为规则都会影响您看到的所有内容,直到其相应的结束标记(写为 </div>)为止。
接下来,我们将项目的名称 “学习日志” 设置为显示在导航栏上的第一个元素❷。这也将作为主页的链接,就像我们在前两章中构建的项目的最小样式版本中所做的那样。 导航栏品牌选择器设置此链接的样式,使其从其他链接中脱颖而出,并有助于向网站添加一些品牌。
然后,Bootstrap 模板定义一个按钮,如果浏览器窗口太窄而无法水平显示整个导航栏,则会出现该按钮 ❸。 当用户单击该按钮时,导航元素将显示在下拉列表中。 当用户缩小浏览器窗口或在小屏幕设备上显示站点时,折叠引用会导致导航栏折叠。
接下来,我们打开导航栏的新部分(<div>)❹。 这是导航栏的一部分,可以根据浏览器窗口的大小折叠。
Bootstrap 将导航元素定义为无序列表中的项目❺,其样式规则使其看起来一点也不像列表。 栏上所需的每个链接或元素都可以作为无序列表中的项目包含❻。 在这里,列表中的唯一项目是我们到主题页面的链接❼。 请注意链接末尾的结束 </li> 标记; 每个开始标签都需要一个相应的结束标签。
此处显示的其余行关闭了所有已打开的标签。 在 HTML 中,注释是这样写的:
<!-- This is an HTML comment. -->
结束标记通常不带注释,但如果你是 HTML 新手,标注一些结束标记会很有帮助。缺少一个标签或多出一个标签都会影响整个页面的布局。我们还包括内容块 ❽,以及结尾的 </body> 和 </html> 标签。
我们还没有完成导航栏的制作,但现在已经有了一个完整的 HTML 文档。如果运行服务器当前处于活动状态,请停止当前服务器并重新启动。访问项目主页,你会看到一个导航栏,其中包含图 20-1 所示的一些元素。现在,让我们为导航栏添加其余元素。
添加用户账户链接
我们还需要添加与用户账户相关的链接。首先,我们将添加除注销表单以外的所有与账户相关的链接。
对 base.xhtml 进行以下修改:
--snip--
</ul> <!-- End of links on left side of navbar -->
<!-- Account-related links -->
❶ <ul class="navbar-nav ms-auto mb-2 mb-md-0">
❷ {% if user.is_authenticated %}
<li class="nav-item">
❸ <span class="navbar-text me-2">Hello, {{ user.
username }}.
</span></li>
❹ {% else %}
<li class="nav-item">
<a class="nav-link" href="{% url 'accounts:regi
ster' %}">
Register</a></li>
<li class="nav-item">
<a class="nav-link" href="{% url 'accounts:logi
n' %}">
Log in</a></li>
{% endif %}
</ul> <!-- End of account-related links -->
</div> <!-- Closes collapsible parts of navbar -->
--snip--
我们使用另一个开始 <ul> 标签开始一组新的链接❶。 您可以在一个页面上拥有任意数量的链接组。 选择器 ms-auto 是 margin-startautomatic 的缩写:该选择器检查导航栏中的其他元素,并计算出左(开始)边距,将这组链接推到浏览器窗口的右侧。
if 块与我们之前使用的条件块相同,用于根据用户是否登录来向用户显示适当的消息❷。 该块现在有点长,因为条件标签内有一些样式规则。 经过身份验证的用户的问候语包含在 <span> 元素中❸。 span 元素对作为较长行一部分的文本片段或页面元素进行样式设置。 div 元素在页面中创建自己的分区,而 span 元素在较大的部分中是连续的。 一开始这可能会令人困惑,因为许多页面都有深度嵌套的 div 元素。 在这里,我们使用 span 元素来设置导航栏上信息文本的样式:在本例中为登录用户的名称。
在为未经身份验证的用户运行的 else 块中,我们包含用于注册新帐户和登录的链接❹。 这些应该看起来就像主题页面的链接。
如果您想向导航栏添加更多链接,您可以使用样式指令(如您在此处看到的那样)将另一个 <li> 项添加到我们定义的 <ul> 组之一。
现在让我们将注销表单添加到导航栏。
将注销表单添加到导航栏
第一次编写注销表单时,我们将其添加到了 base.xhtml 的底部。现在,让我们把它放在一个更好的位置,即导航栏中:
--snip--
</ul> <!-- End of account-related links -->
{% if user.is_authenticated %}
<form action="{% url 'accounts:logout' %}" method
='post'>
{% csrf_token %}
❶ <button name='submit' class='btn btn-outline-sec
ondary btn-sm'>
Log out</button>
</form>
{% endif %}
</div> <!-- Closes collapsible parts of navbar -->
--snip--
注销表单应放置在帐户相关链接集之后,但位于导航栏的可折叠部分内。 表单中唯一的变化是在 <button> 元素中添加了许多 Bootstrap 样式类,这些类将 Bootstrap 样式元素应用于注销按钮❶。
重新加载主页,您应该能够使用您创建的任何帐户登录和注销。
我们还需要添加一些内容到 base.xhtml 中。我们需要定义两个块,各个页面可以使用它们来放置特定于这些页面的内容。
定义页面的主要部分
base.xhtml 的其余部分包含页面的主要部分:
--snip--
</nav> <!-- End of navbar -->
❶ <main class="container">
❷ <div class="pb-2 mb-2 border-bottom">
{% block page_header %}{% endblock page_header %}
</div>
❸ <div>
{% block content %}{% endblock content %}
</div>
</main>
</body>
</html>
我们首先打开一个 <main> 标签❶。主元素用于页面正文中最重要的部分。在这里,我们指定了 bootstrap 选择器容器,这是一种简单的页面元素分组方式。我们将在此容器中放置两个 div 元素。
第一个 div 元素包含一个 page_header 块❷。我们将用这个块来为大多数页面加上标题。为了使该部分从页面的其他部分中脱颖而出,我们将在页眉下方放置一些填充。填充指的是元素内容与其边框之间的空间。选择器 pb-2 是一个 bootstrap 指令,可在样式元素的底部提供适量的填充。边距是指元素边框与页面上其他元素之间的空间。选择器 mb-2 在此 div 的底部提供了适量的边距。我们希望在该块的底部设置边框,因此我们使用了选择器 border-bottom,它在 page_header 块的底部提供了一个细边框。
然后,我们再定义一个包含块内容 ❸的 div 元素。我们不会对该区块应用任何特定样式,因此我们可以根据自己的喜好为任何页面的内容设计样式。在 base.xhtml 文件的末尾,为 main、body 和 html 元素加上了结束标记。
当你在浏览器中加载学习日志的主页时,应该会看到一个与图 20-1 一致的专业导航栏。尝试调整窗口大小,使其变得非常窄;一个按钮将取代导航栏。单击该按钮,所有链接都会出现在下拉列表中。
使用 Jumbotron 设计主页样式
为了更新主页,我们将使用一种名为 "jumbotron" 的 Bootstrap 元素。通常,它被用于主页上,用于放置对整个项目的简要描述,以及邀请浏览者参与的行动号召。
这是修改后的 index.xhtml 文件:
{% extends "learning_logs/base.xhtml" %}
❶ {% block page_header %}
❷ <div class="p-3 mb-4 bg-light border rounded-3">
<div class="container-fluid py-4">
❸ <h1 class="display-3">Track your learning.</h1>
❹ <p class="lead">Make your own Learning Log, and keep a
list of the
topics you're learning about. Whenever you learn someth
ing new
about a topic, make an entry summarizing what you've le
arned.</p>
❺ <a class="btn btn-primary btn-lg mt-1"
href="{% url 'accounts:register' %}">Register »
</a>
</div>
</div>
{% endblock page_header %}
我们首先要告诉 Django,我们要定义 page_header 块❶ 中的内容。跳线是以一对 div 元素的形式实现的,并在其中应用了一组样式指令❷。外层 div 具有 padding 和 margin 设置、浅背景色和圆角。内层 div 是一个随窗口大小变化的容器,也有一些填充。py-4 选择器为 div 元素的顶部和底部添加了填充。请随意调整这些设置中的数字,看看主页是如何变化的。
广告牌内有三个元素。第一个是简短的信息 "跟踪你的学习",让新访客了解学习日志的作用❸。<h1> 元素是一级标题,display-3 选择器为这一特定标题添加了更薄更高的外观。我们还包含一条较长的信息,为用户提供更多关于学习日志❹ 的信息。该信息的格式为前导段,旨在从普通段落中脱颖而出。
我们没有使用文本链接,而是创建了一个按钮,邀请用户注册学习日志❺账户。这个链接与页眉中的链接相同,但按钮在页面中非常醒目,向浏览者展示了开始使用项目所需的操作。您在此处看到的选择器将其样式化为一个大按钮,代表一个行动号召。代码 »
是一个 HTML 实体,看起来像两个直角括号的组合 (>>
)。最后,我们提供了结尾 div 标记,并关闭了 page_header
块。由于该文件中只有两个 div 元素,因此标注收尾 div 标签并没有什么特别的帮助。我们不会在此页面中添加任何其他内容,因此不需要在此模板中定义内容块。
主页现在看起来如图 20-1 所示。这比项目的无样式版本有了很大改进!
设计登录页面的样式
我们已经完善了登录页面的整体外观,但登录表单本身还没有任何样式。让我们通过修改 login.xhtml 使表单与页面的其他部分保持一致:
{% extends 'learning_logs/base.xhtml' %}
❶ {% load django_bootstrap5 %}
❷ {% block page_header %}
<h2>Log in to your account.</h2>
{% endblock page_header %}
{% block content %}
<form action="{% url 'accounts:login' %}" method='post'>
{% csrf_token %}
❸ {% bootstrap_form form %}
❹ {% bootstrap_button button_type="submit" content="Log i
n" %}
</form>
{% endblock content %}
我们首先将 bootstrap5 模板标签加载到该模板中 ❶。然后,我们定义 page_header 块,告诉用户该页面的内容❷。请注意,我们删除了模板中的 {% if form.errors %}
块;djangobootstrap5 会自动管理表单错误。
要显示表单,我们使用模板标记 {% bootstrap_form %}
❸;它取代了我们在第 19 章中使用的 {{ form.as_div }}
元素。当表单呈现时,{% booststrap_form %}
模板标签会在表单的各个元素中插入 Bootstrap 样式规则。为了生成提交按钮,我们使用了 {% bootstrap_button %}
标签,并将其参数设置为提交按钮,并赋予它登录❹ 标签。
图 20-2 显示了现在的登录表单。页面更加整洁,样式一致,目的明确。试着用错误的用户名或密码登录,你会发现即使是错误信息也采用了一致的样式,并与整个网站融为一体。

设置主题页面的样式
让我们从主题页面开始,确保用于查看信息的页面也有合适的样式:
{% extends 'learning_logs/base.xhtml' %}
{% block page_header %}
❶ <h1>Topics</h1>
{% endblock page_header %}
{% block content %}
❷ <ul class="list-group border-bottom pb-2 mb-4">
{% for topic in topics %}
❸ <li class="list-group-item border-0">
<a href="{% url 'learning_logs:topic' topic.id %}">
{{ topic.text }}</a>
</li>
{% empty %}
❹ <li class="list-group-item border-0">No topics have be
en added yet.</li>
{% endfor %}
</ul>
<a href="{% url 'learning_logs:new_topic' %}">Add a new top
ic</a>
{% endblock content %}
我们不需要 {% load bootstrap5 %}
标签,因为我们在此文件中没有使用任何自定义 bootstrap5 模板标签。 我们将标题 Topics 移至 page_header 块中,并将其设为 <h1> 元素,而不是简单的段落❶。
该页面的主要内容是主题列表,因此我们使用 Bootstrap 的列表组组件来渲染页面。 这会将一组简单的样式指令应用于整个列表以及列表中的每个项目。 当我们打开 <ul> 标签时,我们首先包含 list-group 类以将默认样式指令应用于列表❷。 我们通过在列表底部放置一个边框、在列表下方放置一点内边距 (pb-2) 以及在底部边框下方放置一个边距 (mb-4) 来进一步自定义列表。
列表中的每个项目都需要 list-group-item 类,我们通过删除各个项目周围的边框来自定义默认样式❸。 列表为空时显示的消息需要这些相同的类❹。
当您现在访问主题页面时,您应该会看到一个与主页样式相匹配的页面。
设置主题页面上条目的样式
在主题页面中,我们将使用 Bootstrap 的卡片组件来突出每个条目。卡片是一组可嵌套的 div,具有灵活的预定义样式,非常适合显示主题的条目:
{% extends 'learning_logs/base.xhtml' %}
❶ {% block page_header %}
<h1>{{ topic.text }}</h1>
{% endblock page_header %}
{% block content %}
<p>
<a href="{% url 'learning_logs:new_entry' topic.id %}">Ad
d new entry</a>
</p>
{% for entry in entries %}
❷ <div class="card mb-3">
<!-- Card header with timestamp and edit link -->
❸ <h4 class="card-header">
{{ entry.date_added|date:'M d, Y H:i' }}
❹ <small><a href="{% url 'learning_logs:edit_entry' en
try.id %}">
edit entry</a></small>
</h4>
<!-- Card body with entry text -->
❺ <div class="card-body">{{ entry.text|linebreaks }}</di
v>
</div>
{% empty %}
❻ <p>There are no entries for this topic yet.</p>
{% endfor %}
{% endblock content %}
我们首先将主题放在 page_header 块中❶。 然后我们删除此模板中先前使用的无序列表结构。 我们没有将每个条目都设置为列表项,而是使用选择器卡❷打开一个 div 元素。 该卡有两个嵌套元素:一个用于保存时间戳和编辑条目的链接,另一个用于保存条目的正文。 卡片选择器负责处理这个 div 所需的大部分样式; 我们通过在每张卡片的底部添加一个小边距 (mb-3) 来定制卡片。
卡片中的第一个元素是标题,它是带有选择器卡片标题❸的 <h4> 元素。 此标题包含条目的创建日期以及用于编辑条目的链接。 edit_entry 链接周围的 <small> 标记使其看起来比时间戳❹小一点。 第二个元素是带有选择器卡体❺的 div,它将条目的文本放置在卡上的一个简单框中。 请注意,用于在页面上包含信息的 Django 代码没有更改; 只有影响页面外观的元素才有。 由于我们不再有无序列表,因此我们用简单的段落标签替换了空列表消息周围的列表项标签❻。
图20-3显示了新外观的主题页面。 学习日志的功能没有改变,但它看起来更加专业并且对用户有吸引力。
如果您想为项目使用不同的 Bootstrap 模板,请遵循与我们在本章到目前为止所做的类似的过程。 将要使用的模板复制到 base.xhtml 中,并修改包含实际内容的元素,以便模板显示项目的信息。 然后使用 Bootstrap 的单独样式工具对每个页面上的内容进行样式设置。
Bootstrap 项目拥有出色的文档。请访问主页 https://getbootstrap.com 并单击文档,了解有关 Bootstrap 的更多信息。 |
