11.美化
本章我们将讨论如何用基于 Bootstrap
框架来美化我们的网页。
本章将与之前的章节略有不同,因为我不会像平常解说 Python 那样,事无巨细,也不会把源码大量粘贴出来,因为那都是大同小异的东西。创建漂亮的网页是一个很广泛的话题,而与 Python Web 的后端开发很大程度上无关,因此我将讨论一些基本的指导方针和想法,你可以通过重新设计应用的外观来研究和学习它。
CSS框架
如果我们只是一个想创建出规范网页的开发人员,没有时间或兴趣去学习底层机制并通过编写原生 HTML 和 CSS 来实现它,那么唯一可行的解决方案是使用 CSS 框架来简化任务。
通过这种方式,你会失去一些创造性的自由,但另一方面,无需通过太多的功夫就可以让网页在所有浏览器中看起来都不错。 CSS 框架为普通类型的用户界面元素提供了高级 CSS 类的集合,其中包含预定义样式。 大多数这样的框架还提供 JavaScript 插件,以实现不能纯粹使用 HTML 和 CSS 来完成的功能。
Bootstrap 简介
最受欢迎的 CSS 框架之一是由 Twitter 推出的 Bootstrap。
这些是使用 Bootstrap 来设置网页风格的一些好处:
- 在所有主流网页浏览器中都有相似的外观
- 自动处理 PC 桌面,平板电脑和手机屏幕尺寸
- 可定制的布局
- 精心设计的导航栏,表单,按钮,警示,弹出窗口等
使用 Bootstrap 最直接的方法是简单地在你的基本模板中导入 bootstrap.min.css
文件。可以下载此文件并将其添加到你的项目静态文件中,或直接从 CDN 导入。然后,就可以根据其文档开始使用它提供的通用 CSS 类。
我们可能还需要导入包含框架J avaScript 代码的 bootstrap.min.js
文件,以便使用最先进的功能。幸运的是,有一个名为 Flask-Bootstrap
的 Flask 插件,它提供了一个已准备好的基础模板,该模板预先引入了 Bootstrap 框架。 让我们来安装这个扩展:
(venv) $ pip install flask-bootstrap
使用 Flask-Bootstrap
Flask-Bootstrap
需要像大多数其他 Flask 插件一样被初始化:
编写 app/__init__.py
:
# app/__init__.py
from flask_bootstrap import Bootstrap
bootstrap = Bootstrap()
def create_app(config):
app = Flask(__name__)
app.config.from_object(config)
bootstrap.init_app(app)
# ...
return app
在初始化插件之后,bootstrap/base.html
模板就会变为可用状态,你可以使用 extends
子句从应用 HTML 模板中引用。
但是,回顾一下,我已经使用了 extends
子句来继承我的基础模板 base.html
,这样我们可以将页面的公共部分放在一个地方。所以我们在 base.html
模板里定义了导航栏,消息显示模块,并且还导出了一个 content
块。 应用中的所有其他模板都从基础模板继承,并为内容块提供页面的主要内容。
那么我怎样才能适配 Bootstrap 基础模板呢?解决方案是从使用两个层级模板到使用三个层级模板。下面举个例子:
bootstrap/base.html => base.html => index.html
bootstrap/base.html
模板提供页面的基本结构,其中引入了 Bootstrap 框架文件。这个模板为派生的模板定义了一些块,例如 title
,navbar
和 content
(更多请参见:官方文档)。
base.html
模板会从从 bootstrap/base.html
派生,并提供 title
,navbar
和 content
块的具体实现。而第三级模板的内容将通过 app_content
块来展现。
重新设计后的基础模板 app/templates/base.html
:
# app\templates\base.html
{% extends 'bootstrap/base.html' %}
{% block title %}
{% if title %}
{{ title }} - Microblog
{% else %}
Welcome to Microblog
{% endif %}
{% endblock %}
{% block navbar %}
<nav class="navbar navbar-default">
... navigation bar here (see complete code on GitHub) ...
</nav >
{% endblock %}
{% block content %}
<div class="container">
{% with messages = get_flashed_messages() %}
{% if messages %}
{% for message in messages %}
<div class="alert alert-info" role="alert">
{{ message }}
</div>
{% endfor %}
{% endif %}
{% endwith %}
{# application content needs to be provided in the app_content block #}
{% block app_content %}{% endblock %}
</div>
{% endblock %}
从中你可以看到我如何从 bootstrap/base.html
派生此模板,接下来分别实现了页面标题、导航栏和页面内容的这三个模块。
title
块不需要使用 <title>
标签来定义用于页面标题的文本。对于这个块我简单地挪用了原模板中 <title>
标签内部的逻辑。
navbar
块是一个可选块,用于定义导航栏。对于此块,我调整了 Bootstrap 导航栏文档中的示例,以便它在左侧展示网站品牌,跟着是 Home
和 Explore
的链接。 然后我添加了个人主页和登录或注销链接并使其与页面的右边界对齐。 正如我上面提到的,我在上面的例子中省略大量代码,请从 github 中产看完整的 base.html
模板。
最后,在 content
块中,我定义了一个顶级容器 <div class="container">
,并在其中设定了呈现闪现消息的逻辑,这些消息现在将显示为 Bootstrap 警示的样式。 接下来是一个新的 app_content
块,这个块用于从第三级别模板来定义他们自己的内容。
所有页面模板的原来的版本在名为 content
的块中定义了它们的内容,现在所有的模板都必须使用 app_content
作为它们的内容块。
例如,这是 404.html
模板的修改后版本的展示:
# app\templates\errors\404.html
{% extends "base.html" %}
{% block app_content %}
<h1>File Not Found</h1>
<p><a href="{{ url_for('main.index') }}">Back</a></p>
{% endblock %}
渲染 Bootstrap 表单
Flask-Bootstrap
在渲染表单这方面做得非常出色。Flask-Bootstrap
不需要逐个设置表单字段,而是使用一个接受 Flask-WTF
表单对象作为参数,并以 Bootstrap
样式渲染出完整的表单。
下面你可以看到重新设计后的 register.html
模板:
# app\templates\auth\register.html
{% extends "base.html" %}
{% import 'bootstrap/wtf.html' as wtf %}
{% block app_content %}
<h1>Register</h1>
<div class="row">
<div class="col-md-4">
{{ wtf.quick_form(form) }}
</div>
</div>
{% endblock %}
顶端附近的 import
语句与 Python 导入类似。 这增加了一个 wtf.quick_form()
宏,它只需要接收一个 Flask-WTF
表单对象作为参数,就会渲染完整的表单,包括对显示验证错误的支持,并且适配 Bootstrap 框架的所有样式。
渲染用户动态
单条用户动态的渲染逻辑被提取到名为 _post.html
的子模板中。只需要在这个模板上做一些很小的调整,就可以使其在 Bootstrap 下看起来很棒了。
重新设计后的用户动态子模板 app/templates/_post.html
:
# app\templates\_post.html
<table class="table table-hover">
<tr>
<td width="70px">
<img src="{{ post.author.avatar(36) }}">
</td>
<td>
<a href="{{ url_for('auth.user', username=post.author.username) }}">
{{ post.author.username }}
</a>
says:<br>{{ post.body }}
</td>
</tr>
</table>
渲染分页链接
分页链接是 Bootstrap 提供直接支持的另一个方面。为此,我再一次访问 Bootstrap 文档,并修改了其中的一个示例。 以下是在 index.html
页面中的分页链接的代码:
重新设计后的分页链接 app/templates/index.html
:
<nav aria-label="...">
<ul class="pager">
<li class="previous{% if not prev_url %} disabled{% endif %}">
<a href="{{ prev_url or '#' }}">
<span aria-hidden="true">←</span> Newer posts
</a>
</li>
<li class="next{% if not next_url %} disabled{% endif %}">
<a href="{{ next_url or '#' }}">
Older posts <span aria-hidden="true">→</span>
</a>
</li>
</ul>
</nav>
请注意,在分页链接的实现中,当某个方向没有更多内容时,不是隐藏该链接,而是使用禁用状态(disabled
),这会使该链接显示为灰色。
类似的更改需要应用于 user.html
,但我不打算展示在此处,具体请到 github 查看源码。
本文源码:https://github.com/SingleDiego/Flask-Tutorial-Source-Code/tree/SingleDiego-patch-11