美文网首页
Jinja2模板

Jinja2模板

作者: stoneyang94 | 来源:发表于2018-06-30 13:06 被阅读0次

博主在使用Flask过程,预在项目中使用中Jinja2模板。本文为博主Jinja2的学习笔记,主要为了形成对Jinja2模板的相对系统的认识和为日后复习备忘。

为什么使用模板

本节部分参考转载自廖雪峰大大的相关教学

主要从web实质出发,从开发者角度,一步步向高层抽象,说明为什么要使用模板。

Web与Web静态服务器

一个Web应用的本质就是:

  1. 浏览器发送一个HTTP请求
  2. 服务器收到请求,生成一个HTML文档
  3. 服务器把HTML文档作为HTTP响应的Body发送给浏览器
  4. 浏览器收到HTTP响应,从HTTP Body取出HTML文档并显示

所以,最简单的Web应用就是:

  1. 先把HTML用文件保存好
  2. 用一个现成的HTTP服务器软件,接收用户请求,从文件中读取HTML
  3. 返回

Apache、Nginx、Lighttpd等这些常见的静态服务器就是干这件事情的。

动态HTML

WSGI

如果要动态生成HTML,就需要把上述步骤自己来实现。不过,接受HTTP请求、解析HTTP请求、发送HTTP响应都是苦力活,如果我们自己来写这些底层代码,还没开始写动态HTML呢,就得花个把月去读HTTP规范。

正确的做法是底层代码由专门的服务器软件实现,我们用Python专注于生成HTML文档。因为我们不希望接触到TCP连接、HTTP原始请求和响应格式,所以,需要一个统一的接口,让我们专心用Python编写Web业务。

这个接口就是WSGI:Web Server Gateway Interface。

WSGI接口定义

WSGI接口定义非常简单,它只要求Web开发者实现一个函数,就可以响应HTTP请求。我们来看一个最简单的Web版本的“Hello, web!”:

def application(environ, start_response):
    start_response('200 OK', [('Content-Type', 'text/html')])
    return '<h1>Hello, web!</h1>'

上面的application()函数就是符合WSGI标准的一个HTTP处理函数,它接收两个参数:

  • environ:一个包含所有HTTP请求信息的dict对象;
  • start_response:一个发送HTTP响应的函数。

WSGI接口调用

application()函数中,调用:

start_response('200 OK', [('Content-Type', 'text/html')])

就发送了HTTP响应的Header,注意Header只能发送一次,也就是只能调用一次start_response()函数。start_response()函数接收两个参数,一个是HTTP响应码,一个是一组list表示的HTTP Header,每个Header用一个包含两个strtuple表示。

通常情况下,都应该把Content-Type头发送给浏览器。其他很多常用的HTTP Header也应该发送。

然后,函数的返回值'<h1>Hello, web!</h1>'将作为HTTP响应的Body发送给浏览器。

有了WSGI,我们关心的就是如何从environ这个dict对象拿到HTTP请求信息,然后构造HTML,通过start_response()发送Header,最后返回Body。

WSGI过程分析

整个application()函数本身没有涉及到任何解析HTTP的部分,也就是说,底层代码不需要我们自己编写,我们只负责在更高层次上考虑如何响应请求就可以了。
所以application()函数必须由WSGI服务器来调用。

框架

了解了WSGI框架,我们发现:其实一个Web App,就是写一个WSGI的处理函数,针对每个HTTP请求进行响应。

但是如何处理HTTP请求不是问题,问题是如何处理100个不同的URL。

每一个URL可以对应GET和POST请求,当然还有PUT、DELETE等请求,但是我们通常只考虑最常见的GET和POST请求。

一个最简单的想法是从environ变量里取出HTTP请求的信息,然后逐个判断:

def application(environ, start_response):
    method = environ['REQUEST_METHOD']
    path = environ['PATH_INFO']
    if method=='GET' and path=='/':
        return handle_home(environ, start_response)
    if method=='POST' and path='/signin':
        return handle_signin(environ, start_response)
    ...

只是这么写下去代码是肯定没法维护了。

代码这么写没法维护的原因是因为WSGI提供的接口虽然比HTTP接口高级了不少,但和Web App的处理逻辑比,还是比较低级,我们需要在WSGI接口之上能进一步抽象

Web框架:让我们专注于用一个函数处理一个URL,至于URL到函数的映射,就交给Web框架来做。

模板

Web框架的不足

Web框架把我们从WSGI中拯救出来了。现在,我们只需要不断地编写函数,带上URL,就可以继续Web App的开发了。

但是,Web App不仅仅是处理逻辑,展示给用户的页面也非常重要。在函数中返回一个包含HTML的字符串,简单的页面还可以,但是,多行的HTML极难做到。

生成HTML页面的难度很大。在Python代码里拼字符串是不现实的,所以,模板技术出现了。

使用模板,我们需要预先准备一个HTML文档,这个HTML文档不是普通的HTML,而是嵌入了一些变量和指令,然后,根据我们传入的数据,替换后,得到最终的HTML,发送给用户:

mvc-seq

MVC

这就是MVC:Model-View-Controller,中文名“模型-视图-控制器”。

Controller

  • Controller负责业务逻辑,比如检查用户名是否存在,取出用户信息等等
  • Python处理URL的函数就是Controller

View

  • View负责显示逻辑,通过简单地替换一些变量,View最终输出的就是用户看到的HTML
  • 包含变量{{ name }}的模板就是View

Model

  • Model是用来传给View的,这样View在替换变量的时候,就可以从Model中取出相应的数据。

上面的例子中,Model就是一个dict

{ 'name': 'Michael' }

只是因为Python支持关键字参数,很多Web框架允许传入关键字参数,然后,在框架内部组装出一个dict作为Model。

MVC优点

通过MVC,我们在Python代码中处理Model和Controller,而View是通过模板处理的,这样我们就成功地把Python代码和HTML代码最大限度地分离了。

使用模板的另一大好处是,模板改起来很方便,而且,改完保存后,刷新浏览器就能看到最新的效果,这对于调试HTML、CSS和JavaScript的前端工程师来说实在是太重要了。

在Jinja2模板中,我们用{{ name }}表示一个需要替换的变量。很多时候,还需要循环、条件判断等指令语句,在Jinja2中,用{% ... %}表示指令。

除了Jinja2,常见的模板还有:

  • Mako:用<% ... %>${xxx}的一个模板;
  • Cheetah:也是用<% ... %>${xxx}的一个模板;
  • Django:Django是一站式框架,内置一个用{% ... %}{{ xxx }}的模板。

小结

有了MVC,我们就分离了Python代码和HTML代码。HTML代码全部放到模板里,写起来更有效率

Jinja2模板简介

  • 组成
    模板语言本质上包括一些变量和编程逻辑,并在被解释(或渲染成HTML)时被真实值替换
  • 扩展名
    模板仅仅是文本文件。它可以生成任何基于文本的格式(HTML、XML、CSV、LaTex 等等)。 它并没有特定的扩展名, .html 或 .xml 都是可以的。
  • 位置
    一般放在Flask项目的/templates目录里

一. 语句与表达式

控制语句--{%控制语句%}

  • 控制语句都是包含在分隔符{% %}
  • 模板中无法像代码中一样靠缩进来判断代码块的结束,故:
    注意if(for同理)控制语句要用{% endif %}来结束
{% if name and name == 'admin'  %}
    <h1>This is admin console</h1>
{% elif name %}
    <h1>Welcome {{ name }}!</h1>
{% else %}
    <h1>Please login</h1>
{% endif %}

例子:

Flask Python代码:

from flask import Flask,render_template
 
app = Flask(__name__)
 
@app.route('/hello')
@app.route('/hello/<name>')
def hello(name=None):
    return render_template('hello.html', name=name)
 
if __name__ == '__main__':
    app.run(host='0.0.0.0', debug=True)

模板代码:

<!doctype html>
<title>Hello Sample</title>
{% if name %}
  <h1>Hello {{ name }}!</h1>
{% else %}
  <h1>Hello World!</h1>
{% endif %}

表达式--{{表达式}}

模板的表达式都是包含在分隔符{{ }}内的

表达式类型 例子
变量 由Flask渲染模板时传过来
任意一种Python基础类型 直接显示基础类型没意义,一般配合其他表达式用
运算 包括算数运算,如{{ 2 + 3 }};比较运算,如{{ 2 > 1 }};逻辑运算,如{{ False and True }}
过滤器“|”和测试器“is” 这个在后面会介绍
函数调用 如{{ current_time() }};数组下标操作,如{{ arr[1] }}
“in”操作符 如{{ 1 in [1,2,3] }}
字符串连接符”~” 作用同Python中的”+”一样,如{{ “Hello ” ~ name ~ “!” }}
“if”关键字,如{{ ‘Hi, %s’ % name if name }} 这里的”if”不是条件控制语句。

例子:

<a class="navbar-brand" href={{ url_for('index') }}>Flask小例子 </a>

注释块--{{# 注释 #}}

要把模板中一行的部分注释掉,默认使用 {# ... #} 注释语法。

行语句--#这是行语句

如果应用启用了行语句,就可以把一个行标记为一个语句。例如如果行语句前缀配置为 #,下面的两个例子是等价的:

<ul>
# for item in seq
    <li>{{ item }}</li>
# endfor
</ul>
<ul>
{% for item in seq %}
    <li>{{ item }}</li>
{% endfor %}
</ul>

继承--block

Jinja 中最强大的部分就是模板继承。

定义

模板继承允许你构建一个包含你站点共同元素的基本模板“骨架”,并定义子模板可以覆盖的
可以将整个布局设置为父模板,让其子模板继承。这样可以非常方便的控制整个网站的外观。
对比一下面向对象编程的继承概念,容易理解。

做法

子模板用内容填充空的块。

例子

父模板

这个 base.html模板,定义了一个简单的 HTML 骨架文档,你可能使用一个简单的两栏页面。用内容填充空的块是子模板的工作:

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN">
<html lang="en">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
    {% block head %}
    <link rel="stylesheet" href="style.css" />
    <title>{% block title %}{% endblock %} - My Webpage</title>
    {% endblock %}
</head>
<body>
    <div id="content">{% block content %}{% endblock %}</div>
    <div id="footer">
        {% block footer %}
        &copy; Copyright 2008 by <a href="http://domain.invalid/">you</a>.
        {% endblock %}
    </div>
</body>

在本例中, {% block %}标签定义了四个字幕版可以填充的块。
所有的 block 标签 告诉模板引擎子模板可以覆盖模板中的这些部分。

子模块--{% extends "父模板" %}

{% extends "base.html" %}
{% block title %}Index{% endblock %}
{% block head %}
    {{ super() }}
    <style type="text/css">
        .important { color: #336699; }
    </style>
{% endblock %}
{% block content %}
    <h1>Index</h1>
    <p class="important">
      Welcome on my awesome homepage.
    </p>
{% endblock %}
  • 语法
    {% extends %} 标签是这里的关键。它告诉模板引擎这个模板“继承”另一个模板。 当模板系统对这个模板求值时,首先定位父模板。
  • 位置
    extends 标签应该是模板中的第一个 标签。它前面的所有东西都会按照普通情况打印出来

参考文章
Flask 快速入门
Jinja官方文档手册
廖雪峰大大的相关教学
Flask中Jinja2模板引擎详解

相关文章

网友评论

      本文标题:Jinja2模板

      本文链接:https://www.haomeiwen.com/subject/nxgiyftx.html