模板

1. jinja2简介

  • Jinja2:是 Python 下一个被普遍应用的模板引擎,是由Python实现的模板语言,他的设计思想来源于 Django 的模板引擎,并扩展了其语法和一系列强大的功能,其是Flask内置的模板语言。
  • 模板语言:是一种被设计来自动生成文档的简单文本格式,在模板语言中,通常都会把一些变量传给模板,替换模板的特定位置上预先定义好的占位变量名。
  • {{}} 来表示变量名,这种 {{}} 语法叫作变量代码块
  • 用 {%%} 定义的控制代码块,能够实现一些语言层次的功能,好比循环或者if语句
  • 使用 {# #} 进行注释,注释的内容不会在html中被渲染出来

2. 过滤器

  • 过滤器的使用方式为:变量名 | 过滤器。

{{variable | filter_name(*args)}}html

  • 若是没有任何参数传给过滤器,则能够把括号省略掉

{{variable | filter_name}}python

常见内建过滤器

字符串操做

safe:禁用转义sql

<p>{{ '<em>hello</em>' | safe }}</p>express

capitalize:把变量值的首字母转成大写,其他字母转小写flask

<p>{{ 'hello' | capitalize }}</p>api

lower:把值转成小写安全

<p>{{ 'HELLO' | lower }}</p>bash

upper:把值转成大写cookie

<p>{{ 'hello' | upper }}</p>session

title:把值中的每一个单词的首字母都转成大写

<p>{{ 'hello' | title }}</p>

reverse:字符串反转

<p>{{ 'olleh' | reverse }}</p>

format:格式化输出

<p>{{ '%s is %d' | format('name',17) }}</p>

striptags:渲染以前把值中全部的HTML标签都删掉

<p>{{ '<em>hello</em>' | striptags }}</p>

truncate: 字符串截断

<p>{{ 'hello every one' | truncate(9)}}</p>

列表操做

first:取第一个元素

<p>{{ [1,2,3,4,5,6] | first }}</p>

last:取最后一个元素

<p>{{ [1,2,3,4,5,6] | last }}</p>

length:获取列表长度

<p>{{ [1,2,3,4,5,6] | length }}</p>

sum:列表求和

<p>{{ [1,2,3,4,5,6] | sum }}</p>

sort:列表排序

<p>{{ [6,2,3,1,5,4] | sort }}</p>

3. 控制代码块

控制代码块主要包含两个:

  • if/else if /else / endif
  • for / endfor if语句

Jinja2 语法中的if语句跟 Python 中的 if 语句类似,后面的布尔值或返回布尔值的表达式将决定代码中的哪一个流程会被执行:

{%if user.is_logged_in() %}
    <a href='/logout'>Logout</a>
{% else %}
    <a href='/login'>Login</a>
{% endif %}

# 咱们能够在 Jinja2 中使用循环来迭代任何列表或者生成器函数
{% for post in posts %}
    <div>
        <h1>{{ post.title }}</h1>
        <p>{{ post.text | safe }}</p>
    </div>
{% endfor %}
复制代码
  • 在一个 for 循环块中你能够访问这些特殊的变量:

4. 模板代码复用

4.1 宏

  • 把它看做 Jinja2 中的一个函数,它会返回一个模板或者 HTML 字符串
  • 为了不反复地编写一样的模板代码,出现代码冗余,能够把他们写成函数以进行重用
  • 须要在多处重复使用的模板代码片断能够写入单独的文件,再包含在全部模板中,以免重复
# 定义宏
{% macro input(name,value='',type='text') %}
    <input type="{{type}}" name="{{name}}"
        value="{{value}}" class="form-control">
{% endmacro %}

# 调用宏
{{ input('name' value='zs')}}

# 输出
<input type="text" name="name"
    value="zs" class="form-control">
复制代码

4.2 继承

# 父模板
base.html
{% block top %}
  顶部菜单
{% endblock top %}

{% block content %}
{% endblock content %}

{% block bottom %}
  底部
{% endblock bottom %}

# 子模板
extends指令声明这个模板继承自哪
{% extends 'base.html' %}
{% block content %}
 须要填充的内容
{% endblock content %}
复制代码

模板继承使用时注意点:

  • 不支持多继承
  • 为了便于阅读,在子模板中使用extends时,尽可能写在模板的第一行。
  • 不能在一个模板文件中定义多个相同名字的block标签。
  • 当在页面中使用多个block标签时,建议给结束标签起个名字,当多个block嵌套时,阅读性更好。

4.3 包含

代码重用的功能,包含(Include)。它的功能是将另外一个模板整个加载到当前模板中,并直接渲染。

# include的使用
{% include 'hello.html' %}
包含在使用时,若是包含的模板文件不存在时,程序会抛出TemplateNotFound异常,能够加上 ignore missing 关键字。若是包含的模板文件不存在,会忽略这条include语句。

# include 的使用加上关键字ignore missing
{% include 'hello.html' ignore missing %}
复制代码

4.4 小结

  • 宏(Macro)、继承(Block)、包含(include)均能实现代码的复用。
  • 继承(Block)的本质是代码替换,通常用来实现多个页面中重复不变的区域。
  • 宏(Macro)的功能相似函数,能够传入参数,须要定义、调用。
  • 包含(include)是直接将目标模板文件整个渲染出来。

5. 特有函数和变量

  1. config
# 能够从模板中直接访问Flask当前的config对象:

{{config.SQLALCHEMY_DATABASE_URI}}
sqlite:///database.db
复制代码
  1. request
# 就是flask中表明当前请求的request对象:

{{request.url}}
http://127.0.0.1
复制代码
  1. session
为Flask的session对象

{{session.new}}
True
复制代码
  1. g变量
在视图函数中设置g变量的 name 属性的值,而后在模板中直接能够取出

{{ g.name }}
复制代码
  1. url_for()
url_for会根据传入的路由器函数名,返回该路由对应的URL,在模板中始终使用url_for()就能够安全的修改路由绑定的URL,则不比担忧模板中渲染出错的连接:

{{url_for('home')}}

# 若是咱们定义的路由URL是带有参数的,则能够把它们做为关键字参数传入url_for(),Flask会把他们填充进最终生成的URL中:

{{ url_for('post', post_id=1)}}
/post/1
复制代码
  1. get_flashed_messages()
这个函数会返回以前在flask中经过flask()传入的消息的列表,flash函数的做用很简单,能够把由Python字符串表示的消息加入一个消息队列中,再使用get_flashed_message()函数取出它们并消费掉:

{%for message in get_flashed_messages()%}
    {{message}}
{%endfor%}
复制代码

6. Flask-WTF表单

  • 使用 Flask-WTF 实现表单

  • 配置参数,关闭 CSRF 校验

app.config['WTF_CSRF_ENABLED'] = False

  • CSRF:跨站请求伪造

模板页面:

<form method="post">
    {{ form.username.label }} {{ form.username }}<br/>
    {{ form.password.label }} {{ form.password }}<br/>
    {{ form.password2.label }} {{ form.password2 }}<br/>
    {{ form.submit }}
</form>
复制代码

视图函数:

from flask import Flask,render_template, flash
#导入wtf扩展的表单类
from flask_wtf import FlaskForm
#导入自定义表单须要的字段
from wtforms import SubmitField,StringField,PasswordField
#导入wtf扩展提供的表单验证器
from wtforms.validators import DataRequired,EqualTo


app = Flask(__name__)
app.config['SECRET_KEY']='SECRET_KEY'

#自定义表单类,文本字段、密码字段、提交按钮
class RegisterForm(FlaskForm):
    username = StringField("用户名:", validators=[DataRequired("请输入用户名")], render_kw={"placeholder": "请输入用户名"})
    password = PasswordField("密码:", validators=[DataRequired("请输入密码")])
    password2 = PasswordField("确认密码:", validators=[DataRequired("请输入确认密码"), EqualTo("password", "两次密码不一致")])
    submit = SubmitField("注册")

#定义根路由视图函数,生成表单对象,获取表单数据,进行表单数据验证
@app.route('/demo2', methods=["get", "post"])
def demo2():
    register_form = RegisterForm()
    # 验证表单
    if register_form.validate_on_submit():
        # 若是代码能走到这个地方,那么就代码表单中全部的数据都能验证成功
        username = request.form.get("username")
        password = request.form.get("password")
        password2 = request.form.get("password2")
        # 伪装作注册操做
        print(username, password, password2)
        return "success"
    else:
        if request.method == "POST":
            flash("参数有误或者不完整")

    return render_template('temp_register.html', form=register_form)

if __name__ == '__main__':
    app.run(debug=True)
复制代码

7. CSRF

CSRF全拼为Cross Site Request Forgery,译为跨站请求伪造。

  • 即用户访问了一个存在漏洞的网站A,在未退出登陆的同时访问B攻击网站,B攻击网站会诱导用户作一些操做,提交这些操做的同时,B网站会携带cookie跳转访问A网站,A网站会认为用户访问,赞成B网站的请求,B网站攻击成功。

8. 蓝图

蓝图/Blueprint对象用起来和一个应用/Flask对象差很少,最大的区别在于一个蓝图对象没有办法独立运行,必须将它注册到一个应用对象上才能生效

使用蓝图能够分为三个步骤:

# 1.建立一个蓝图对象
admin=Blueprint('admin',__name__)
# 2.在这个蓝图对象上进行操做,注册路由,指定静态文件夹,注册模版过滤器
@admin.route('/')
def admin_home():
    return 'admin_home'
# 3.在应用对象上注册这个蓝图对象
app.register_blueprint(admin,url\_prefix='/admin')
当这个应用启动后,经过/admin/能够访问到蓝图中定义的视图函数
复制代码

蓝图的url前缀

  • 当咱们在应用对象上注册一个蓝图时,能够指定一个url_prefix关键字参数(这个参数默认是/)

  • 在应用最终的路由表url_map中,在蓝图上注册的路由URL自动被加上了这个前缀,这个能够保证在多个蓝图中使用相同的URL规则而不会最终引发冲突,只要在注册蓝图时将不一样的蓝图挂接到不一样的自路径便可

  • url_for

url_for('admin.index') # /admin/

9. 单元测试

测试从软件开发过程能够分为:

  • 单元测试: 对单独的代码块(例如函数)分别进行测试,以保证它们的正确性
  • 集成测试: 对大量的程序单元的协同工做状况作测试
  • 系统测试: 同时对整个系统的正确性进行检查,而不是针对独立的片断

断言语句相似于:

raise AssertionError
 AssertionError
复制代码

经常使用的断言方法:

assertEqual     若是两个值相等,则pass
assertNotEqual  若是两个值不相等,则pass
assertTrue      判断bool值为True,则pass
assertFalse     判断bool值为False,则pass
assertIsNone    不存在,则pass
assertIsNotNone 存在,则pass
复制代码
相关文章
相关标签/搜索