Flask体系教程,使用指南

1.1.模板传参

(1)主程序

from flask import Flask,render_template

app = Flask(__name__)

@app.route('/')
def hello_world():

    context = {
        'username':'derek',
        'age':18,
        'gf':{
            'name':'xxx',
            'height':160
        }
    }
    return render_template('index.html',**context)    #加双下划綫,就可以直接获取key和value了

if __name__ == '__main__':

    app.run(debug=True)

(2)index.html

  <h2>模板中渲染数据</h2>
    <p>{{ username }}</p>
    <p>{{ age }}</p>
    <p>{{ gf.name }}</p>
    <p>{{ gf['height'] }}</p>

必发365游戏官方网址 1

 

模板:

如何使用

1.2.过滤器

 常用的过滤器

  • abs:绝对值
  • default:如果当前变量没有值,则会使用参数中的值来替代
  • escape:转义字符
  • first:返回一个序列的第一个元素
  • format:格式化字符串
  • last:返回一个序列的最后一个元素
  • length:返回一个序列的长度
  • join:拼接字符串
  • safe:关掉转义
  • int:转为int类型
  • float:转为浮点类型
  • lower:转换为小写
  • upper:转换为答谢
  • replace:替换
  • truncate:截取length长度的字符串
  • striptags:删除字符串中所有的html标签,如果出现多个空格,将替换成一个空格

 default过滤器的使用

主程序

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

@app.route('/')
def hello_world():

    context = {
       'position':-9,
       'signature':None   #个性签名
    }
    return render_template('index.html',**context)

if __name__ == '__main__':
    app.run(debug=True)

index.html

  <h2>过滤器</h2>
    <p>{{ position|abs }}</p>
    <p>个性签名:{{ signature|default('此人很懒,没有留下任何说明',boolean=True) }}</p>

也可以用or的方式

 <h2>过滤器</h2>
    <p>{{ position|abs }}</p>
{#    <p>个性签名:{{ signature|default('此人很懒,没有留下任何说明',boolean=True) }}</p>#}
    <p>个性签名:{{ signature or '此人很懒,没有留下任何说明' }}</p>

必发365游戏官方网址 2

在之前的章节中,视图函数只是直接返回文本,而在实际生产环境中其实很少这样用,因为实际的页面大多是带有样式和复杂逻辑的HTML代码,这可以让浏览器渲染出非常漂亮的页面。目前市面上有非常多的模板系统,其中最知名最好用的就是Jinja2和Mako,我们先来看一下这两个模板的特点和不同:

API

swig.init({
  allowErrors: false,   
  autoescape: true,
  cache: true,
  encoding: 'utf8',
  filters: {},
  root: '/',
  tags: {},
  extensions: {},
  tzOffset: 0
});

options:

allowErrors:
默认值为 false。将所有模板解析和编译错误直接输出到模板。如果为
true,则将引发错误,抛出到 Node.js 进程中,可能会使您的应用程序崩溃。

autoescape:
默认true,强烈建议保持。字符转换表请参阅转义过滤器。

true: HTML安全转义
false: 不转义,除非使用转义过滤器或者转义标签
‘js’: js安全转义

cache:
更改为 false 将重新编译每个请求的模板的文件。正式环境建议保持true。

encoding
模板文件编码

root
需要搜索模板的目录。如果模板传递给 swig.compileFile
绝对路径(以/开头),Swig不会在模板root中搜索。如果传递一个数组,使用第一个匹配成功的数组项。

tzOffset
设置默认时区偏移量。此设置会使转换日期过滤器会自动的修正相应时区偏移量。

filters 自定义过滤器或者重写默认过滤器,参见自定义过滤器指南。

tags
自定义标签或者重写默认标签,参见自定义标签指南。

extensions
添加第三方库,可以在编译模板时使用,参见参见自定义标签指南。

1.3.自定义过滤器

     
过滤器本质上就是一个函数,如果在模板中调用这个过滤器,那么就会将这个变量的值作为第一个参数传给过滤器这个函数,

然后函数的返回值会作为这个过滤器的返回值。需要使用一个装饰器:@app.template_filter(‘args’)

 

实例:自定义时间处理过滤器

 主程序

from flask import Flask,render_template
from datetime import datetime
app = Flask(__name__)

@app.route('/')
def hello_world():
    context = {
        'create_time':datetime(2018,5,25,17,52,10)
    }
    return render_template('index.html',**context)

@app.template_filter('handle_time')  #括号里面是自己给过滤器起的名字
def handle_time(time):
    '''
    1.如果时间间隔小与1分钟以内,就显示“刚刚”
    2.如果是1小时以内,显示“xx分钟”
    3.如果24h以内,显示“xx小时前”
    4.如果大于24小时小与30天,显示“xx天前”
    5.大于一个月,显示具体的时间
    :param time:
    :return:
    '''
    if isinstance(time,datetime):
        now = datetime.now()
        timestamp = (now-time).total_seconds()  #当前时间离创建时间的秒数
        if timestamp < 60:     #60s以内
            return "刚刚"
        elif timestamp >= 60 and timestamp < 60*60:
            minutes = timestamp / 60
            return "%s分钟前"%int(minutes)
        elif timestamp >= 60*60 and timestamp < 60*60*24:
            hours = timestamp / (60*60)
            return '%s小时前'%int(hours)
        elif timestamp >= 60*60*24 and timestamp < 60*60*24*30:
            days = timestamp / (60*60*24)
            return '%s天前'%int(days)
        else:
            return time.strftime('%Y/%m/%d %H:%M')
    else:
        return time

if __name__ == '__main__':
    app.run(debug=True)

index.html

 <h2>自定义时间过滤器</h2>
    {{ create_time|handle_time }}

必发365游戏官方网址 3

Jinja2:Jinja是日本寺庙的意思,并且寺庙的英文是temple和模板的英文template的发音类似。Jinja2是默认的仿Django模板的一个模板引擎,由Flask的作者开发。它速度快,被广泛使用,并且提供了可选的沙箱模板来保证执行环境的安全,它有以下优点:

nodejs

var tpl = swig.compileFile("path/to/template/file.html");
var renderedHtml = tpl.render({ vars: 'to be inserted in template' });

or

var tpl = swig.compile("Template string here");
var renderedHtml = tpl({ vars: 'to be inserted in template' });

1.4.if和for的使用

for中包含以下变量,可以用来获取当前的遍历状态

  • loop.index
  • loop.index0
  • loop.first
  • loop.last
  • loop.length

 

if和for简单用法

from flask import Flask,render_template

app = Flask(__name__)
app.config.update({
    'DEBUG':True,
    'TEMPLATES_AUTO_RELOAD':True
})

@app.route('/')
def hello_world():
    context = {
        'age':20,
        'users':['tom','jack','alice'],
        'person':{
            'name':'derek',
            'age':18
        }
    }
    return render_template('index.html',**context)

if __name__ == '__main__':
    app.run(debug=True)

index.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>

    {% if age >= 18 %}
        欢迎
    {% else %}
        无权限
    {% endif %}

    <ul>
    {% for user in users %}
        <li>{{ user }}</li>
    {% endfor %}
    </ul>

    <table>
        <thead>
            <tr>
                <th>用户名</th>
                <th>年龄</th>
            </tr>
        </thead>
        <tbody>
            <tr>
                {% for key,value in person.items() %}
                    <td>{{ value }}</td>
                {% endfor %}
            </tr>
        </tbody>
    </table>

</body>
</html>

必发365游戏官方网址 4

让前端开发者和后端开发者工作分离。

必发365游戏官方网址,结合Express

npm install express
npm install consolidate

然后

app.engine('.html', cons.swig);
app.set('view engine', 'html');

1.5.宏的使用和导入

模板的宏跟python中的函数类似,可以传递参数,但是不能有返回值,可以将一些经常用到的代码片段放到宏中,然后把一些

不固定的值抽取出来当成一个变量。

(1)简单使用实例

{#    定义一个宏,input是宏的名字,里面三个参数,可以指定默认参数值,也可以调用的传参#}
    {% macro input(name="",value="",type="text") %}
        <input name="{{ name }}" value="{{ value }}" type="{{ type }}">
    {% endmacro %}

    <form>
        <p>用户名:{{ input('username') }}</p>
        <p>密码:{{ input('password',type="password" )}}</p>
        <p> {{ input(value="提交",type="submit" )}}</p>

    </form>

必发365游戏官方网址 5

(2)宏的两种导入方式

新建macros.html

 {% macro input(name="",value="",type="text") %}
        <input name="{{ name }}" value="{{ value }}" type="{{ type }}">
    {% endmacro %}

index.html中导入使用宏

{#第一种#}
{# with context可以把后端传到当前模板的变量传到定义的宏里面#}
{% import "macros.html" as macro with context %}    
{#第二种#}
{% from "macros.html" import input as input_field %}


<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
{#    第一种#}
    <form>
        <p>用户名:{{ macro.input('username') }}</p>
        <p>密码:{{ macro.input('password',type="password" )}}</p>
        <p> {{ macro.input(value="提交",type="submit" )}}</p>
    </form>

{#    第二种#}
     <form>
        <p>用户名:{{ input_field('username') }}</p>
        <p>密码:{{ input_field('password',type="password" )}}</p>
        <p> {{ input_field(value="提交",type="submit" )}}</p>
    </form>

</body>
</html>

减少Flask项目代码的耦合性,页面逻辑放在模板中,业务逻辑放在视图函数中,将页面逻辑和业务逻辑解耦有利于代码的维护。

浏览器

Swig浏览器版本的api基本与nodejs版相同,不同点如下:

  1. 不能使用swig.compileFile,浏览器没有文件系统
  2. 你必须提前使用swig.compile编译好模板
  3. 按顺序使用extends, import, and
    include,同时在swig.compile里使用参数templateKey来查找模板

    var template = swig.compile('<p>{% block content %}{% endblock %}</p>', { filename: 'main' });
    var mypage = swig.compile('{% extends "main" %}{% block content %}Oh hey there!{% endblock %}', { filename: 'mypage' });
    

1.6.set、with在模板中自定义变量

 (1)set

在模板中可以使用set来定义变量,一旦定义了这个变量,在后面的代码中都可以使用,index.html

{% set usernmae='derek' %}

    <p>用户名:{{ usernmae }}</p>

必发365游戏官方网址 6

(2)with

with语句定义的变量,只能在with语句代码块(endwith)里面使用,超过代码块,就不能再使用了,set语句没有end,全局使用

{% with age=18  %}
        <p>年龄:{{ age }}</p>
    {% endwith %}

 

提供了控制语句、继承等高级功能,减少开发的复杂度。

基础

1.7.蓝图的使用

目录如下:

必发365游戏官方网址 7

(1)news.py

from flask import Blueprint


news_bp = Blueprint('new',__name__,url_prefix='/news')

@news_bp.route('/list/')
def news_list():
    return '新闻列表'

(2)user.py

from flask import Blueprint

# 1.定义一个蓝图,'user':蓝图的名字,url_prefix='/user':给url加一个前缀,注意后面不要加'/'
user_bp = Blueprint('user',__name__,url_prefix='/user')

@user_bp.route('/profile/')
def profile():
    return '个人中心'

(3)bluepoint_demo.py

from flask import Flask,url_for
# 2.导入
from blueprints.user import user_bp
from blueprints.news import news_bp

app = Flask(__name__)
# 3.注册蓝图
app.register_blueprint(user_bp)
app.register_blueprint(news_bp)

@app.route('/')
def hello_world():
    return 'Hello World!'

with app.test_request_context():
    print(url_for('new.news_list'))     # /news/list/   通过url_for反转url的时候要加蓝图的名字
    print(url_for('user.profile'))      # /user/profile/

if __name__ == '__main__':
    app.run(debug=True)

 必发365游戏官方网址 8

必发365游戏官方网址 9

 

Marko:Marko是另一个知名的模板。他从Django、Jinja2等模板借鉴了很多语法和API,他有以下优点:

变量

{{ foo.bar }}
{{ foo['bar'] }}

如果变量未定义,输出空字符。

变量可以通过过滤器来修改:

{{ name|title }} was born on {{ birthday|date('F jS, Y') }}
// Jane was born on July 6th, 1985

性能和Jinja2相近,在这里可以看到。

逻辑标签

参见标签部分。

有大型网站在使用,有成功的案例。Reddit和豆瓣都在使用。

注释

有知名的web框架支持。Pylons和Pyramid这两个web框架内置模板就是Mako。

空白

模板里的空白在最终输出时默认保留,如果需要去掉空白,可以在逻辑标签前后加上空白控制服-:

{% for item in seq -%}
    {{ item }}
{%- endfor %}

支持在模板中写几乎原生的Python语法的代码,对Python工程师比较友好,开发效率高。

模板继承

Swig 使用 extends 和 block 来实现模板继承

layout.html

<!doctype html>
<html>
<head>
    <meta charset="utf-8">
    <title>{% block title %}My Site{% endblock %}</title>

    {% block head %}
        <link rel="stylesheet" href="main.css">
    {% endblock %}
</head>
<body>
    {% block content %}{% endblock %}
</body>
</html>

index.html

{% extends 'layout.html' %}

{% block title %}My Page{% endblock %}

{% block head %}
{% parent %}
    <link rel="stylesheet" href="custom.css">
{% endblock %}

{% block content %}
    <p>This is just an awesome page.</p>
{% endblock %}

自带完整的缓存系统。当然也提供了非常好的扩展借口,很容易切换成其他的缓存系统。

变量过滤器

用于修改变量。变量名称后用 | 字符分隔添加过滤器。您可以添加多个过滤器。

Flask渲染Jinja模板:

例子

{{ name|title }} was born on {{ birthday|date('F jS, Y') }}
and has {{ bikes|length|default("zero") }} bikes.

也可以使用 filter 标签来为块内容添加过滤器

{% filter upper %}oh hi, paul{% endfilter %}

要渲染一个模板,通过render_template方法即可,以下将用一个简单的例子进行讲解:

内置过滤器

add(value)
使变量与value相加,可以转换为数值字符串会自动转换为数值。

addslashes
用 \ 转义字符串

capitalize
大写首字母

date(format[, tzOffset])
转换日期为指定格式

format: 格式
tzOffset: 时区

default(value)
默认值(如果变量为undefined,null,false)

e
同escape

escape([type])
转义字符

默认: &, <, >, “, ‘
js: &, <, >, “, ‘, =, -, ;

first
返回数组第一个值

join(glue)
同[].join

json_encode([indent])
类似JSON.stringify, indent为缩进空格数

last
返回数组最后一个值

length
返回变量的length,如果是object,返回key的数量

lower
同”.toLowerCase()

raw
指定输入不会被转义

replace(search, replace[, flags])
同”.replace

reverse
翻转数组

striptags
去除html/xml标签

title
大写首字母

uniq
数组去重

upper
同”.toUpperCase

url_encode
同encodeURIComponent

url_decode
同decodeURIComponemt

fromflaskimportFlask,render_templateapp =
Flask(__name__)@app.route(‘/about/’)defabout():returnrender_template(‘about.html’)

自定义过滤器

创建一个 myfilter.js 然后引入到 Swig 的初始化函数中

swig.init({ filters: require('myfilters') });

在 myfilter.js 里,每一个 filter 方法都是一个简单的 js
方法,下例是一个翻转字符串的 filter:

exports.myfilter = function (input) {
    return input.toString().split('').reverse().join('');
};

你的 filter 一旦被引入,你就可以向下面一样使用:

{{ name|myfilter }}

{% filter myfilter %}I shall be filtered{% endfilter %}

你也可以像下面一样给 filter 传参数:

exports.prefix = function(input, prefix) {
    return prefix.toString() + input.toString();
};

{{ name|prefix('my prefix') }}

{% filter prefix 'my prefix' %}I will be prefixed with "my prefix".{% endfilter %}
{% filter prefix foo %}I will be prefixed with the value stored to `foo`.{% endfilter %}

当访问/about/的时候,about()函数会在当前目录下的templates文件夹下寻找about.html模板文件。如果想更改模板文件地址,应该在创建app的时候,给Flask传递一个关键字参数template_folder,指定具体的路径,再看以下例子:

标签

fromflaskimportFlask,render_templateapp =
Flask(__name__,template_folder=r’C:\templates’)@app.route(‘/about/’)defabout():returnrender_template(‘about.html’)

内置标签

extends
使当前模板继承父模板,必须在文件最前

参数:
file:
父模板相对模板 root 的相对路径

block
定义一个块,使之可以被继承的模板重写,或者重写父模板的同名块

参数:
name:
块的名字,必须以字母数字下划线开头

parent
将父模板中同名块注入当前块中

include
包含一个模板到当前位置,这个模板将使用当前上下文

参数:
file:
包含模板相对模板 root 的相对路径
ignore missing:
包含模板不存在也不会报错
with x:
设置 x 至根上下文对象以传递给模板生成。必须是一个键值对
only:
限制模板上下文中用 with x 定义的参数

{% include template_path %}
{% include "path/to/template.js" %} 

你可以标记 ignore missing,这样如果模板不存在,也不会抛出错误

{% include "foobar.html" ignore missing %}

本地声明的上下文变量,默认情况不会传递给包含的模板。例如以下情况,inc.html
无法得到 foo 和 bar

{% set foo = "bar" %}
{% include "inc.html" %}

{% for bar in thing %}
    {% include "inc.html" %}
{% endfor %}

如果想把本地声明的变量引入到包含的模板种,可以使用 with
参数来把后面的对象创建到包含模板的上下文中

{% set foo = { bar: "baz" } %}
{% include "inc.html" with foo %}

{% for bar in thing %}
    {% include "inc.html" with bar %}
{% endfor %}    

如果当前上下文中 foo 和 bar 可用,下面的情况中,只有 foo 会被 inc.html
定义

{% include "inc.html" with foo only %}

only 必须作为最后一个参数,放在其他位置会被忽略

raw
停止解析标记中任何内容,所有内容都将输出

参数:
file:
父模板相对模板 root 的相对路径

for
遍历对象和数组

参数:
x:
当前循环迭代名
in:
语法标记
y:
可迭代对象。可以使用过滤器修改

{% for x in y %}
    {% if loop.first %}<ul>{% endif %}
    <li>{{ loop.index }} - {{ loop.key }}: {{ x }}</li>
    {% if loop.last %}</ul>{% endif %}
{% endfor %}

特殊循环变量
loop.index:当前循环的索引(1开始)
loop.index0:当前循环的索引(0开始)
loop.revindex:当前循环从结尾开始的索引(1开始)
loop.revindex0:当前循环从结尾开始的索引(0开始)
loop.key:如果迭代是对象,是当前循环的键,否则同 loop.index
loop.first:如果是第一个值返回 true
loop.last:如果是最后一个值返回 true
loop.cycle:一个帮助函数,以指定的参数作为周期

{% for item in items %}
    <li class="{{ loop.cycle('odd', 'even') }}">{{ item }}</li>
{% endfor %}

在 for 标签里使用 else

{% for person in people %}
    {{ person }}
{% else %}
    There are no people yet!
{% endfor %}

if
条件语句

参数:
…:
接受任何有效的 JavaScript 条件语句,以及一些其他人类可读语法

{% if x %}{% endif %}
{% if !x %}{% endif %}
{% if not x %}{% endif %}

{% if x and y %}{% endif %}
{% if x && y %}{% endif %}
{% if x or y %}{% endif %}
{% if x || y %}{% endif %}
{% if x || (y && z) %}{% endif %}

{% if x [operator] y %}
    Operators: ==, !=, <, <=, >, >=, ===, !==
{% endif %}

{% if x == 'five' %}
    The operands can be also be string or number literals
{% endif %}

{% if x|length === 3 %}
    You can use filters on any operand in the statement.
{% endif %}

{% if x in y %}
    If x is a value that is present in y, this will return true.
{% endif %}

else 和 else if

{% if foo %}
    Some content.
{% else if "foo" in bar %}
    Content if the array `bar` has "foo" in it.
{% else %}
    Fallback content.
{% endif %}

autoescape
改变当前变量的自动转义行为

参数:
on:
当前内容是否转义
type:
转义类型,js 或者 html,默认 html

假设

some_html_output = '<p>Hello "you" & \'them\'</p>';

然后

{% autoescape false %}
    {{ some_html_output }}
{% endautoescape %}

{% autoescape true %}
    {{ some_html_output }}
{% endautoescape %}

{% autoescape true "js" %}
    {{ some_html_output }}
{% endautoescape %}

将会输出

<p>Hello "you" & 'them'</p>

&lt;p&gt;Hello &quot;you&quot; &amp; &#39;them&#39; &lt;/p&gt;

\u003Cp\u003EHello \u0022you\u0022 & \u0027them\u0027\u003C\u005Cp\u003E

set
设置一个变量,在当前上下文中复用

参数:
name:
变量名
=:
语法标记
value:
变量值

{% set foo = [0, 1, 2, 3, 4, 5] %}
{% for num in foo %}
    <li>{{ num }}</li>
{% endfor %}

macro
创建自定义可服用的代码段

参数:
…:
用户定义

{% macro input type name id label value error %}
<label for="{{ name }}">{{ label }}</label>
<input type="{{ type }}" name="{{ name }}" id="{{ id }}" value="{{ value }}"{% if error %} class="error"{% endif %}>
{% endmacro %}

然后像下面使用

<div>{{ input("text", "fname", "fname", "First Name", fname.value, fname.errors) }}</div>
<div>{{ input("text", "lname", "lname", "Last Name", lname.value, lname.errors) }}</div>

输出如下

<div>
    <label for="fname">First Name</label>
    <input type="text" name="fname" id="fname" value="Paul">
</div>
<div>
    <label for="lname">Last Name</label>
    <input type="text" name="lname" id="lname" value="" class="error">
</div>

import
允许引入另一个模板的宏进入当前上下文

参数:
file:
引入模板相对模板 root 的相对路径
as:
语法标记 var: 分配给宏的可访问上下文对象

{% import 'formmacros.html' as form %}

{# this will run the input macro #}
{{ form.input("text", "name") }}

{# this, however, will NOT output anything because the macro is scoped to the "form" object: #}
{{ input("text", "name") }}

filter
对整个块应用过滤器

参数:
filter_name:
过滤器名字 … :
若干传给过滤器的参数 父模板相对模板 root 的相对路径

{% filter uppercase %}oh hi, {{ name }}{% endfilter %}
{% filter replace "." "!" "g" %}Hi. My name is Paul.{% endfilter %}

输出

OH HI, PAUL
Hi! My name is Paul!

spaceless
尝试移除html标签间的空格

{% spaceless %}
{% for num in foo %}
    <li>{{ loop.index }}</li>
{% endfor %}
{% endspaceless %}

输出

<li>1</li><li>2</li><li>3</li>

以上例子将会在C盘的templates文件夹中寻找模板文件。还有最后一点是,如果模板文件中有参数需要传递,应该怎么传呢,我们再来看一个例子:

fromflaskimportFlask,render_templateapp =
Flask(__name__)@app.route(‘/about/’)defabout():# return
render_template(‘about.html’,user=’xiaotuo’)returnrender_template(‘about.html’,**{‘user’:’xiaotuo})

以上例子介绍了两种传递参数的方式,因为render_template需要传递的是一个关键字参数,所以第一种方式是顺其自然的。但是当你的模板中要传递的参数过多的时候,把所有参数放在一个函数中显然不是一个好的选择,因此我们使用字典进行包装,并且加两个*号,来转换成关键字参数。

Jinja2:

Jinja2默认已经跟着Flask进行安装了,如果没有被安装到,可以通过pip install
Jinja2来进行安装。

概要:

Jinja模板是简单的一个纯文本文件(html/xml/csv…),不仅仅是用来产生html文件,后缀名也依照你自己的心情而定。当然,尽量命名为模板正确的文件格式,增加可读性。先看一个简单例子:

1.2.3.My Webpage4.5.6.7. {% for item in navigation %}8.

{{ item.caption }}

  1. {% endfor %}10.11.12. {{ a_variable }}13. {{ user.name }}14. {{
    user[‘name’] }}15.16. {# a comment #}17.18.

以上示例有需要进行解释:

第12~14行的{{ …
}}:用来装载一个变量,模板渲染的时候,会把这个变量代表的值替换掉。并且可以间接访问一个变量的属性或者一个字典的key。关于点.号访问和[]中括号访问,没有任何区别,都可以访问属性和字典的值。

第7~9行的{% …
%}:用来装载一个控制语句,以上装载的是for循环,以后只要是要用到控制语句的,就用{%
… %}。

第14行的{# … #}:用来装载一个注释,模板渲染的时候会忽视这中间的值。

属性访问规则:

比如在模板中有一个变量这样使用:foo.bar,那么在Jinja2中是这样进行访问的:

先去查找foo的bar这个属性,也即通过getattr(foo,’bar’)。

如果没有,就去通过foo.__getitem__(‘bar’)的方式进行查找。

如果以上两种方式都没有找到,返回一个undefined。

在模板中有一个变量这样使用:foo[‘bar’],那么在Jinja2中是这样进行访问:

通过foo.__getitem__(‘bar’)的方式进行查找。

如果没有,就通过getattr(foo,’bar’)的方式进行查找。

如果以上没有找到,则返回一个undefined。

过滤器:

过滤器是通过管道符号(|)进行使用的,例如:{{ name|length
}},将返回name的长度。过滤器相当于是一个函数,把当前的变量传入到过滤器中,然后过滤器根据自己的功能,再返回相应的值,之后再将结果渲染到页面中。Jinja2中内置了许多过滤器,在这里可以看到所有的过滤器,现对一些常用的过滤器进行讲解:

abs(value):返回一个数值的绝对值。示例:-1|abs

default(value,default_value,boolean=false):如果当前变量没有值,则会使用参数中的值来代替。示例:name|default(‘xiaotuo’)——如果name不存在,则会使用xiaotuo来替代。boolean=False默认是在只有这个变量为undefined的时候才会使用default中的值,如果想使用python的形式判断是否为false,则可以传递boolean=true。也可以使用or来替换。

escape(value)或e:转义字符,会将<、>等符号转义成HTML中的符号。示例:content|escape或content|e。

first(value):返回一个序列的第一个元素。示例:names|first

format(value,*arags,**kwargs):格式化字符串。比如:

{{“%s”-“%s”|format(‘Hello?’,”Foo!”) }}将输出:Helloo? – Foo!

last(value):返回一个序列的最后一个元素。示例:names|last。

length(value):返回一个序列或者字典的长度。示例:names|length。

join(value,d=u”):将一个序列用d这个参数的值拼接成字符串。

safe(value):如果开启了全局转义,那么safe过滤器会将变量关掉转义。示例:content_html|safe。

int(value):将值转换为int类型。

float(value):将值转换为float类型。

lower(value):将字符串转换为小写。

upper(value):将字符串转换为小写。

replace(value,old,new): 替换将old替换为new的字符串。

truncate(value,length=255,killwords=False):截取length长度的字符串。

striptags(value):删除字符串中所有的HTML标签,如果出现多个空格,将替换成一个空格。

trim:截取字符串前面和后面的空白字符。

string(value):将变量转换成字符串。

wordcount(s):计算一个长字符串中单词的个数。

控制语句:

所有的控制语句都是放在{% … %}中,并且有一个语句{% endxxx
%}来进行结束,Jinja中常用的控制语句有if/for..in..,现对他们进行讲解:

if:if语句和python中的类似,可以使用>,<,<=,>=,==,!=来进行判断,也可以通过and,or,not,()来进行逻辑合并操作,以下看例子:

{%ifkenny.sick %}Kennyissick.{%elifkenny.dead %}You killed Kenny! You
bastard!!!{%else%}Kenny looks okay — so far{% endif %}

for…in…:for循环可以遍历任何一个序列包括数组、字典、元组。并且可以进行反向遍历,以下将用几个例子进行解释:

普通的遍历:

  • {{ user.username|e }}

遍历字典:

{% for key, value in my_dict.iteritems() %}{{ key|e }}{{ value|e }}{%
endfor %}

如果序列中没有值的时候,进入else:

  • {{ user.username|e }}
  • no users found

并且Jinja中的for循环还包含以下变量,可以用来获取当前的遍历状态:

变量描述

loop.index当前迭代的索引(从1开始)

loop.index0当前迭代的索引(从0开始)

loop.first是否是第一次迭代,返回True/False

loop.last是否是最后一次迭代,返回True/False

loop.length序列的长度

另外,不可以使用continue和break表达式来控制循环的执行

测试器:

测试器主要用来判断一个值是否满足某种类型,并且这种类型一般通过普通的if判断是有很大的挑战的。语法是:if…is…,先来简单的看个例子:

{%ifvariableisescaped%}value of variable: {{ escaped
}}{%else%}variableisnotescaped{% endif %}

以上判断variable这个变量是否已经被转义了,Jinja中内置了许多的测试器,看以下列表:

callable(object):是否可调用。

defined(object):是否已经被定义了。

escaped(object):是否已经被转义了。

upper(object):是否全是大写。

lower(object):是否全是小写。

string(object):是否是一个字符串。

sequence(object):是否是一个序列。

number(object):是否是一个数字。

odd(object):是否是奇数。

even(object):是否是偶数。

宏:

模板中的宏跟python中的函数类似,可以传递参数,但是不能有返回值,可以将一些经常用到的代码片段放到宏中,然后把一些不固定的值抽取出来当成一个变量,以下将用一个例子来进行解释:

{% macro input(name, value=”,type=’text’) %}

value|e }}”>{% endmacro %}

以上例子可以抽取出了一个input标签,指定了一些默认参数。那么我们以后创建input标签的时候,可以通过他快速的创建:

{{ input(‘username’) }}

{{ input(‘password’,type=’password’) }}

import语句:

在真实的开发中,会将一些常用的宏单独放在一个文件中,在需要使用的时候,再从这个文件中进行导入。import语句的用法跟python中的import类似,可以直接import…as…,也可以from…import…或者from…import…as…,假设现在有一个文件,叫做forms.html,里面有两个宏分别为input和textarea,如下:

forms.html:{% macro input(name, value=”,type=’text’) %}{% endmacro
%}{% macro textarea(name, value=”, rows=10, cols=40) %}

}}”>{{ value|e }}{% endmacro %}

看以下导入宏的例子:

import…as…形式:

{% import ‘forms.html’ as forms %}Username{{ forms.input(‘username’)
}}Password{{ forms.input(‘password’, type=’password’) }}

{{ forms.textarea(‘comment’) }}

from…import…as…/from…import…形式:

{% from ‘forms.html’ import input as input_field, textarea %}Username{{
input_field(‘username’) }}Password{{ input_field(‘password’,
type=’password’) }}

{{ textarea(‘comment’) }}

另外需要注意的是,导入模板并不会把当前上下文中的变量添加到被导入的模板中,如果你想要导入一个需要访问当前上下文变量的宏,有两种可能的方法:

显式地传入请求或请求对象的属性作为宏的参数。

与上下文一起(with context)导入宏。

与上下文中一起(with context)导入的方式如下:

{%from’_helpers.html’importmy_macrowithcontext %}

include语句:

include语句可以把一个模板引入到另外一个模板中,类似于把一个模板的代码copy到另外一个模板的指定位置,看以下例子:

{%include’header.html’%}Body{%include’footer.html’%}

赋值(set)语句:

有时候我们想在在模板中添加变量,这时候赋值语句(set)就派上用场了,先看以下例子:

{%setname=’xiaotuo’%}

那么以后就可以使用name来代替xiaotuo这个值了,同时,也可以给他赋值为列表和元组:

{%setnavigation = [(‘index.html’,’Index’), (‘about.html’,’About’)] %}

赋值语句创建的变量在其之后都是有效的,如果不想让一个变量污染全局环境,可以使用with语句来创建一个内部的作用域,将set语句放在其中,这样创建的变量只在with代码块中才有效,看以下示例:

{%with%}{% set foo =42%}{{ foo }} foois42here{% endwith %}

也可以在with的后面直接添加变量,比如以上的写法可以修改成这样:

{%withfoo =42%}{{ foo }}{% endwith %}

这两种方式都是等价的,一旦超出with代码块,就不能再使用foo这个变量了。

模板继承:

Flask中的模板可以继承,通过继承可以把模板中许多重复出现的元素抽取出来,放在父模板中,并且父模板通过定义block给子模板开一个口,子模板根据需要,再实现这个block,假设现在有一个base.html这个父模板,代码如下:

{% block head %}{% block title %}{% endblock %} – My Webpage{% endblock
%}{% block content %}{% endblock %}{% block footer %}© Copyright 2008
byyou.{% endblock %}

以上父模板中,抽取了所有模板都需要用到的元素html、body等,并且对于一些所有模板都要用到的样式文件style.css也进行了抽取,同时对于一些子模板需要重写的地方,比如title、head、content都定义成了block,然后子模板可以根据自己的需要,再具体的实现。以下再来看子模板的代码:

{% extends “base.html” %}{% block title %}Index{% endblock %}{% block
head %}{{ super() }}.important{color:#336699; }{% endblock %}{% block
content %}

Index

Welcome to my awesome homepage.{% endblock %}

首先第一行就定义了子模板继承的父模板,并且可以看到子模板实现了title这个block,并填充了自己的内容,再看head这个block,里面调用了super()这个函数,这个函数的目的是执行父模板中的代码,把父模板中的内容添加到子模板中,如果没有这一句,则父模板中处在head这个block中的代码将会被子模板中的代码给覆盖掉。

另外,模板中不能出现重名的block,如果一个地方需要用到另外一个block中的内容,可以使用self.blockname的方式进行引用,比如以下示例:

{% block title %}{% endblock %}

{{ self.title() }}

{% block body %}{% endblock %}

以上示例中h1标签重用了title这个block中的内容,子模板实现了title这个block,h1标签也能拥有这个值。

另外,在子模板中,所有的文本标签和代码都要添加到从父模板中继承的block中。否则,这些文本和标签将不会被渲染。

转义:

转义的概念是,在模板渲染字符串的时候,字符串有可能包括一些非常危险的字符比如<、>等,这些字符会破坏掉原来HTML标签的结构,更严重的可能会发生XSS跨域脚本攻击,因此如果碰到<、>这些字符的时候,应该转义成HTML能正确表示这些字符的写法,比如>在HTML中应该用<来表示等。

但是Flask中默认没有开启全局自动转义,针对那些以.html、.htm、.xml和.xhtml结尾的文件,如果采用render_template函数进行渲染的,则会开启自动转义。并且当用render_template_string函数的时候,会将所有的字符串进行转义后再渲染。而对于Jinja2默认没有开启全局自动转义,作者有自己的原因:

渲染到模板中的字符串并不是所有都是危险的,大部分还是没有问题的,如果开启自动转义,那么将会带来大量的不必要的开销。

Jinja2很难获取当前的字符串是否已经被转义过了,因此如果开启自动转义,将对一些已经被转义过的字符串发生二次转义,在渲染后会破坏原来的字符串。

在没有开启自动转义的模式下(比如以.conf结尾的文件),对于一些不信任的字符串,可以通过{{
content_html|e }}或者是{{ content_html|escape
}}的方式进行转义。在开启了自动转义的模式下,如果想关闭自动转义,可以通过{{
content_html|safe }}的方式关闭自动转义。而{%autoescape
true/false%}…{%endautoescape%}可以将一段代码块放在中间,来关闭或开启自动转义,例如以下代码关闭了自动转义:

{% autoescape false %}

autoescaping is disabled here

{{ will_not_be_escaped }}{% endautoescape %}

数据类型:

Jinja支持许多数据类型,包括:字符串、整型、浮点型、列表、元组、字典、true/false。注意其中的布尔类型是true/false,都是小写,不是Python中的True/False。

运算符:

+号运算符:可以完成数字相加,字符串相加,列表相加。但是并不推荐使用+运算符来操作字符串,字符串相加应该使用~运算符。

-号运算符:只能针对两个数字相减。

/号运算符:对两个数进行相除。

%号运算符:取余运算。

*号运算符:乘号运算符,并且可以对字符进行相乘。

**号运算符:次幂运算符,比如2**3=8。

in操作符:跟python中的in一样使用,比如{{1 in [1,2,3]}}返回true。

~号运算符:拼接多个字符串,比如{{“Hello” ~ “World”}}将返回HelloWorld。

静态文件配置:

Web应用中会出现大量的静态文件来使得网页更加生动美观。静态文件主要包括有CSS样式文件、JavaScript脚本文件、图片文件、字体文件等静态资源。在Jinja中加载静态文件非常简单,只需要通过url_for全局函数就可以实现,看以下代码:

url_for函数默认会在项目根目录下的static文件夹中寻找about.css文件,如果找到了,会生成一个相对于项目根目录下的/static/about.css路径。当然我们也可以把静态文件不放在static文件夹中,此时就需要具体指定了,看以下代码:

app = Flask(__name__,static_folder=’/tmp’)

那么访问静态文件的时候,将会到/tmp这个文件夹下寻找

相关文章

发表评论

电子邮件地址不会被公开。 必填项已用*标注

*
*
Website