# 二、摸版

## 摸版

> render\_template 渲染摸版，会在程序文件夹中 templates 子文件夹中寻找模板

### Jinja2摸版

```bash
mkdir templates
echo '<h1>Hello, World!</h1>' > templates/index.html
echo '<h1>Hello, {{ name }}!</h1>' > templates/user.html
(myproject) [root@localhost myproject]# python app.py
```

```python
from flask import Flask, render_template
app = Flask(__name__)

app.debug = True

@app.route('/')
def index():
  return render_template('index.html')

@app.route('/user/<name>')
def user(name):
  return render_template('user.html', name=name)

if __name__ == '__main__':
  app.run(host='0.0.0.0',port=8080)
```

测试

```bash
[root@localhost ~]# curl 127.0.0.1:8080
<h1>Hello, World!

</h1>[root@localhost ~]# curl 127.0.0.1:8080/user/admin
<h1>Hello, admin!</h1>
```

### 变量过滤器

* Jinja2 能识别所有类型的变量，甚至是一些复杂的类型，例如列表、字典和对象。在模板 中使用变量的一些示例如下：

```html
<p>A value from a dictionary: {{ mydict['key'] }}.</p>
<p>A value from a list: {{ mylist[3] }}.</p>
<p>A value from a list, with a variable index: {{ mylist[myintvar] }}.</p>
<p>A value from an object's method: {{ myobj.somemethod() }}.</p>
```

| 过滤器名       | 说　　明                  |
| ---------- | --------------------- |
| safe       | 渲染值时不转义               |
| capitalize | 把值的首字母转换成大写，其他字母转换成小写 |
| lower      | 把值转换成小写形式             |
| upper      | 把值转换成大写形式             |
| title      | 把值中每个单词的首字母都转换成大写     |
| trim       | 把值的首尾空格去掉             |
| striptags  | 渲染之前把值中所有的 HTML 标签都删掉 |

### IF控制器

```html
<ul>
 {% for comment in comments %}
 <li>{{ comment }}</li>
 {% endfor %}
</ul>
```

### For控制器

```html
<ul>
 {% for comment in comments %}
 <li>{{ comment }}</li>
 {% endfor %}
</ul>
```

### 宏控制器

```bash
{% macro render_comment(comment) %}
 <li>{{ comment }}</li>
{% endmacro %}

<ul>
 {% for comment in comments %}
 {{ render_comment(comment) }}
 {% endfor %}
</ul>
```

为了重复使用宏，我们可以将其保存在单独的文件中，然后在需要使用的模板中导入：

```html
{% import 'macros.html' as macros %}
<ul>
 {% for comment in comments %}
 {{ macros.render_comment(comment) }}
 {% endfor %}
</ul>
```

需要在多处重复使用的模板代码片段可以写入单独的文件，再包含在所有模板中，以避免重复：

```html
{% include 'common.html' %}
```

### 摸版继承

创建基础摸版，可被其他摸版调用: base.html

```html
<html>
<head>
 {% block head %}
 <title>{% block title %}{% endblock %} - My Application</title>
 {% endblock %}
</head>

<body>
 {% block body %}
 {% endblock %}
</body>
</html>
```

基模板的衍生模板

> 注意新定义的 head 块，在基模板中其内容不是空的，所以使用 super() 获取原来的内容

````html
{% extends "base.html" %}

{% block title %}Index{% endblock %}

{% block head %}
 {{ super() }}
 <style>
 </style>
{% endblock %}

{% block body %}
<h1>Hello, World!</h1>
{% endblock %}
```
````

### 集成 Bootstrap

```bash
(myproject) [root@localhost myproject]# pipenv install flask-bootstrap
```

```python
# app.py
from flask_bootstrap import Bootstrap 
''''
bootstrap = Bootstrap(app)
```

### 使用 Flask-Bootstrap 的模板

> Bootstrap-Flask的出现是为了替代不够灵活且缺乏维护的Flask-Bootstrap。它的主要设计参考了Flask-Bootstrap，其中渲染表单和分页部件的宏基于Flask-Bootstrap中的相关代码修改实现。和Flask-Bootstrap相比 参考： <https://zhuanlan.zhihu.com/p/39799223> 参考：<https://bootstrap-flask.readthedocs.io/en/stable/basic.html#configurations> <https://bootstrap-flask.readthedocs.io/en/stable/advanced.html#bootswatch-theme>

* base.html 不变
* templates/user.html：使用 Flask-Bootstrap 的模板

```html
{% extends "bootstrap/base.html" %}
{% block title %}Flasky{% endblock %}
{% block navbar %}
<div class="navbar navbar-inverse" role="navigation">
 <div class="container">
 <div class="navbar-header">
 <button type="button" class="navbar-toggle"
 data-toggle="collapse" data-target=".navbar-collapse">
 <span class="sr-only">Toggle navigation</span>
 <span class="icon-bar"></span>
 <span class="icon-bar"></span>
 <span class="icon-bar"></span>
 </button>
 <a class="navbar-brand" href="/">Flask</a>
 </div>
 <div class="navbar-collapse collapse">
 <ul class="nav navbar-nav">
 <li><a href="/">Home</a></li>
 </ul>
 </div>
 </div>
</div>
{% endblock %}
{% block content %}
<div class="container">
 <div class="page-header">
 <h1>Hello, {{ name }}!</h1>
 </div>
</div>
{% endblock %}
```

![](/files/b7PT0aT03QV2nxyuNu3n)

Flask-Bootstrap 的 base.html 模板还定义了很多其他块，都可在衍生模板中使用。

Flask-Bootstrap基模板中定义的块

| 块　　名          | 说　　明                |
| ------------- | ------------------- |
| doc           | 整个 HTML 文档          |
| html\_attribs | 标签的属性               |
| html          | 标签中的内容              |
| head          | 标签中的内容              |
| title         | 标签中的内容              |
| metas 一组      | 标签                  |
| styles        | 层叠样式表定义             |
| body\_attribs | 标签的属性               |
| body          | 标签中的内容              |
| navbar        | 用户定义的导航条            |
| content       | 用户定义的页面内容           |
| scripts       | 文档底部的 JavaScript 声明 |

### 自定义错误页面

```python
# app.py add
@app.errorhandler(404)
def page_not_found(e):
 return render_template('404.html'), 404
 
@app.errorhandler(500)
def internal_server_error(e):
 return render_template('500.html'), 500
```

{% hint style="info" %}

方法一： 新建404.html, 500,然后把这两个文件中的页面头部元素改为相应的错误消息。但这种方法会带来很多重复劳动。

方法二： 继承自 **bootstrap/base.html** 的新模板

templates/base.html 其中定义了导航条。这个模板本身也可作为其他模板的基模板
{% endhint %}

```markup
<html>
 <head></head>
 <body>
  {% extends bootstrap/base.html %} 
  
  {% block title %}Flasky{% endblock %} 
  
  {% block navbar %} 
  <div class="navbar navbar-inverse" role="navigation"> 
   <div class="container"> 
    <div class="navbar-header"> 
     <button type="button" class="navbar-toggle" data-toggle="collapse" data-target=".navbar-collapse"> 
       <span class="sr-only">Toggle navigation</span> 
       <span class="icon-bar"></span> 
       <span class="icon-bar"></span> 
       <span class="icon-bar"></span> 
     </button> 
     <a class="navbar-brand" href="/">Flasky</a> 
    </div> 
    <div class="navbar-collapse collapse"> 
     <ul class="nav navbar-nav"> 
      <li><a href="/">Home</a></li> 
     </ul> 
    </div> 
   </div> 
  </div> 
  {% endblock %} 
     
     
  <!-- 这个模板的 content 块中只有一个 <div> 容器，其中包含了一个名为 page_content 的新的空块，块中的内容由衍生模板定义。 --> {% block content %} 
  <div class="container">
    {% block page_content %}{% endblock %} 
  </div> 
 </body>
</html>
```

templates/404.html：使用模板继承机制自定义 404 错误页面

```markup
{% extends "base.html" %}
{% block title %}Flasky - Page Not Found{% endblock %}
{% block page_content %}
<div class="page-header">
 <h1>Not Found</h1>
</div>
{% endblock %}
```

### 链接

* Flask 提供了 url\_for() 辅助函数，它可以使用程序 URL 映射中保存的信息生成 URL。
* url\_for() 函数最简单的用法是以视图函数名（或者 app.add\_url\_route() 定义路由时使用的端点名）作为参数，返回对应的 URL

```python
# 相对路径
url_for('index') 得到的结果是 /。

# 绝对路径
url_for('index', _external=True)  返回的则是绝对地址，在这个示例中是 http://localhost:5000/

# 生成动态地址
url_for('user', name='john', _external=True) 的返回结果是 http://localhost:5000/user/john。


# 关键字参数，用于查询字符串
url_for('index', page=2) 的返回结果是 /?page=2
```

### 静态文件

```python
# 默认设置下，Flask 在程序根目录中名为 static 的子目录中寻找静态文件
url_for('static', filename='css/styles.css', _external=True)
```

示例

```html
<!-- 图标的声明会插入 head 块的末尾。注意如何使用 super() 保留基模板中定义的块的原始内容。 -->

{% block head %}
{{ super() }}

<link rel="shortcut icon" href="{{ url_for('static', filename = 'favicon.ico') }}"
 type="image/x-icon">
<link rel="icon" href="{{ url_for('static', filename = 'favicon.ico') }}"
 type="image/x-icon">
 
{% endblock %}
```

### 使用Flask-Moment本地化日期和时间

> 一个优雅的解决方案是，把时间单位发送给 Web 浏览器，转换成当地时间，然后渲染。Web 浏览器可以更好地完成这一任务，因为它能获取用户电脑中的时区和区域设置。 Flask-Moment 是一个 Flask 程序扩展，能把moment.js 集成到 Jinja2 模板中。

```python
pipenv install flask-moment

# app.py
from flask.ext.moment import Moment
moment = Moment(app)
```

templates/base.html：引入 moment.js 库

```python
{% block scripts %}
{{ super() }}
{{ moment.include_moment() }}
{% endblock %}
```

如何使用

```python
pipenv install datetime

# app.py
from datetime import datetime
@app.route('/')
def index():
 return render_template('index.html',current_time=datetime.utcnow())


# templates/index.html
<p>The local date and time is {{ moment(current_time).format('LLL') }}.</p>

<!-- fromNow() 渲染相对时间戳，指定 refresh 参数后，其内容会随着时间的推移而更新 -->
<p>That was {{ moment(current_time).fromNow(refresh=True) }}</p>
```

##


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://close.gitbook.io/yun-wei-bi-ji/python/flask/er-mo-ban.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
