Web开发-Flask从零开始的学习(二)

3 模板

业务逻辑表现逻辑进行分离,把表现逻辑移到模板中能提升应用的可维护性。

模板是包含响应文本的文件,其中包含用占位变量表示的动态部分,其具体值只在请求的上下文中才能知道。使用真实值替换变量,再返回最终得到的响应字符串,这一过程称为渲染。为了渲染模板,Flask 使用一个名为 Jinja2 的强大模板引擎。

Jinja2模板引擎

默认情况下,Flask 在应用目录中的 templates 子目录里寻找模板。

jinja2模板:

<!-- templates/index.html:jinja2模板 -->
<h1>Hello World!</h1>
<!-- templates/user.html:jinja2模板 -->
<h1>Hello, {{name}} ! </h1>

渲染模板:

from flask import  Flask
from flask import request,render_template

app = Flask(__name__)

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

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

Flask 提供的 render_template() 函数把 Jinja2 模板引擎集成到了应用中。

这个函数的第一个参数是模板的文件名,随后的参数都是键 – 值对,表示模板中变量对应的具体值。左边为模板中使用的占位符,右边为当前作用域的变量

变量

在模板中使用的 {{ name }} 结构表示一个变量,这是一种特殊的占位符,告诉模板引擎这个位置的值从渲染模板时使用的数据中获取。

  • jinja2能识别许多类型的变量:
<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>
  • 变量的值可以使用过滤器修改。过滤器添加在变量名之后,二者之间以竖线分隔,例如{{name|capitalize}} 首字母大写

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

默认情况下,出于安全考虑,Jinja2 会转义所有变量。

很多情况下需要显示变量中存储的HTML 代码,这时就可使用 safe 过滤器。

page41

控制结构

Jinja2 提供了多种控制结构,可用来改变模板的渲染流程。

  • 条件判断
{% if user %}
	Hello, {{ user }}!
{% else %}	
	Hello, Stranger!
{% endif %}
  • 循环结构
<ul>
	{% for comment in comments %}
		<li>{{ comment }}</li>
	{% endfor %}
</ul>
  • 支持宏(类似函数)
{% macro render_comment(comment) %}
	<li>{{ comment }}</li>
{% endmacro %}
<ul>
	{% for comment in comments %}
		{{ render_comment(comment) }}
	{% endfor %}
</ul>
  • 代码复用:

    • 重复使用宏:把宏保存在单独的文件中,然后在需要使用的模板中导入

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

      或者

      {% include 'common.html' %}
      
    • 模板继承,类似类继承

      基模板base.html

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

      基模板中定义的区块(block)可在衍生模板中覆盖。

      衍生模板:

      {% extends "base.html" %}
      {% block title %}Index{% endblock %}
      {% block head %}
      	{{ super() }}
      	<style>
      	</style>
      {% endblock %}
      {% block body %}	<h1>Hello, World!</h1>
      {% endblock %}
      

      如果基模板和衍生模板中的同名区块中都有内容,显示衍生模板

      在衍生模板的区块里可以调用 super(),引用基模板中同名区块里的内容

Flask-Bootstrap拓展集成Bootstrap

Bootstrap是一个开源Web前端框架。按照flask-bootstrap拓展

  • 在实例py文件中初始化
from flask_bootstrap import Bootstrap 
# ...
bootstrap = Bootstrap(app)
  • 导入后可以利用jinja2的模板继承机制来拓展基模板
{% 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 %}
{% block content %}
<div class="container">
 <div class="page-header">
 <h1>Hello, {{ name }}!</h1>
 </div>
</div>
{% endblock %}

extends 指令从 Flask-Bootstrap 中导入bootstrap/base.html,从而实现模板继承。Flask-Bootstrap 的基模板提供了一个网页骨架,引入了 Bootstrap 的所有 CSS 和 JavaScript文件。

很多区块都是 Flask-Bootstrap 自用的,如果直接覆盖可能会导致一些问题。

例如,Bootstrap 的 CSS 和 JavaScript 文件在 styles 和 scripts 区块中声明。如果应用需要向已经有内容的块中添加新内容,必须使用 Jinja2 提供的 super() 函数。

{% block scripts %}
{{ super() }}
<script type="text/javascript" src="my-script.js"></script>
{% endblock %}

自定义错误界面

Flask 允许应用使用模板自定义错误页面。最常见的错误代码有两个:

  • 404,客户端请求未知页面或路由时显示
  • 500,应用有未处理的异常时显示

使用 app.errorhandler 装饰器为这两个错误提供自定义的处理函数:

@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

与视图函数一样,错误处理函数也返回一个响应。此外,错误处理函数还要返回与错误对应的数字状态码。


同时这两个错误处理视图函数的模板也需要我们编写,而再去分别创建两个html文件是重复劳动。

利用jinja2的模板继承机制解决这一问题。Flask-Bootstrap 提供了一个具有页面基本布局的基模板;同样,应用也可以自定义一个具有统一页面布局的基模板,其中包含导航栏,而页面内容则留给衍生模板定义

建立我们自己定义的应用基模板templates/base.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="/">Flasky</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">
 {% block page_content %}{% endblock %}
</div>
{% endblock %}

自定义的基模板继承自bootstrap,后续应用的其他模板(404.html等)继承该自定义基模板

链接

任何具有多个路由的应用都需要可以连接不同页面的链接,例如导航栏

  • 直接编写简单路由的URL链接
  • 对于包含可变部分的动态路由

为了避免直接编写导致URL对路由产生不必要的依赖关系:

  • url_for():使用应用中URL映射中保存的信息生成URL(或者使用端点名)
url_for('index') # 结果:/
url_for('index',_external=True) # 返回绝对地址 http://localhost:5000/
# 动态URL生成
url_for('user',name='join',_external=True) # 返回http://localhost:5000/user/join
# 同时不限于动态路由中的参数,也包括非动态参数
url_for('user',name='john',page=2,version=1) #返回结果 /user/john?page=2&version=1

静态文件

Web 应用不是仅由 Python 代码和模板组成,多数应用还会使用静态文件:HTML 代码引用的图像、JavaScript 源码文件和 CSS。

审查 hello.py 应用的 URL 映射时,其中有一个 static 路由。
这是 Flask 为了支持静态文件而自动添加的,这个特殊路由的 URL 是 /static/。

例如调用 url_for('static', filename='css/styles.css', _external=True) 得到的结果是 http://localhost:5000/static/css/styles.css。

默认设置下,Flask 在应用根目录中名为 static 的子目录中寻找静态文件。如果需要,可在static 文件夹中使用子文件夹存放文件。

{% 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 %}

这个图标的声明插入 head 区块的末尾。注意,为了保留基模板中这个区块里的原始内容,我们调用了 super()。

Flask-Moment本地化日期和时间

服务器需要统一时间单位,这和用户所在的地理位置无关,所以一般使用协调世界时(UTC,coordinated universal time)

而用户往往需要当地时间和当地惯用的时间格式

Flask-Moment 是一个 Flask 扩展,能简化把 Moment.js 集成到 Jinja2 模板中的过程。

Moment.js是使用 JavaScript 开发的优秀客户端开源库,它可以在浏览器中渲染日期和时间。

  • hello.py:初始化 Flask-Moment
from flask_moment import Moment
moment = Moment(app)
  • 基模板的scripts块中引入Moment.js库
{% block scripts %}
{{ super() }}
{{ moment.include_moment() }}
{{ moment.locale('zh-cn') }} 
{% endblock %}

为了处理时间戳,Flask-Moment 向模板开放了 moment 对象。

  • hello.py:添加一个 datetime 变量
from datetime import datetime
@app.route('/')
def index():
	return render_template('index.html',current_time=datetime.utcnow())
  • templates/index.html:使用 Flask-Moment 渲染时间戳
<p>The local date and time is {{ moment(current_time).format('LLL') }}.</p>
<p>That was {{ moment(current_time).fromNow(refresh=True) }}</p>

一篇关于flask-moment的博文

format('LLL') 函数根据客户端计算机中的时区和区域设置渲染日期和时间。

第二行中的 fromNow() 渲染相对时间戳,而且会随着时间的推移自动刷新显示的时间。设定 refresh=True 参数后,其内容会随着
时间的推移而更新。

posted @ 2021-03-14 14:40  tlamm  阅读(117)  评论(0)    收藏  举报