flask的基本使用3,jinja2模板

 

jinja2模板引擎简介
1,模板
视图函数的主要作用是生成请求的响应,这是最简单的请求。实际上,视图函数有

两个作用:
处理业务逻辑和返回响应内容。在大型应用中,把业务逻辑和表现内容放在一起,会增加
代码的复杂度和维护成本。模板的作用即是承担视图函数的另一个作用,即返回响应内容。
模板其实是多个包含响应脚本的文件,其中用占位符(变量)表示动态部分,告诉模板引擎其具体的
值需要从使用的数据中获取
使用真实值替换变量,再返回最终得到的字符串,这个过程称为“渲染”
Flask是使用 Jinja2 这个模板引擎来渲染模板

使用模板的好处:
视图函数只负责业务逻辑和数据处理(业务逻辑展示),模板则取到视图函数的数据结果进行展示(视图展示展示)代码结构清晰,耦合度低

Jinja2的两个概念:
Jinja2:是 Python flask下个被广泛应使用的模板引擎,是由Python实现的模板语言,他的设计思想来
源于 Django 的模板引擎,并扩展了其语法和本系列强大的功能,其是Flask内置的模板语言。
模板语言:是一种被设计来自动生成文档的简单文本格式,在模板语言中,一般都会把某些变量传给模板,替换模板的特定位置上预先定义好的占位变量名。
渲染模版函数
Flask提供的 render_template 函数封装了该模板引擎
render_template 函数的第一个参数是模板的文件名,后面的参数都是键值对,表示模板中变量
对应的真实值。

使用
{{}} 来表示变量名,这种 {{}} 语法叫做变量代码块

<h1>{{ post.title }}</h1>

Jinja2 模版中的变量代码块可以是任意 Python 类型或者对象,只要它能够被 Python 的 str() 用法转换为单个字符串就可以,比如,可以通过面的方式显示单个字典或者列表中的某个元素:  

{{your_dict['key']}}
{{your_list[0]}}

用 {%%} 定义的控制代码块,可以实现某些语言层次的功能,比如循环或者if语句  

{% if user %}
{{ user }}
{% else %}
hello!
<ul>
{% for index in indexs %}
<li> {{ index }} </li>
{% endfor %}
</ul>

注释

使⽤用{# #} 进行注释,注释的内容不会在html中被渲染出来

{# {{ name }} #}

2,模板变量

2.1 模板变量的基本使用

模板渲染的三步操作

from flask import Flask, render_template

app = Flask(__name__)
# 模板渲染的三步操作
# 1,将模板文件放入模板文件夹中,并设置模板语言
# 2,使用render_template进行模板渲染,将要替换的内容设置为模板变量
# 3,在模板文件中使用模板变量替换内容(找到要替换内容设置为{{模板变量名}})


@app.route('/')
def index():
    city_name = '北京'
    htm_str = render_template('baidu.html',city=city_name)
    return htm_str


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

2.2模板替换

py代码
from flask import Flask, render_template

app = Flask(__name__)
class User():
    type='vip'
    def is_login(self):
        return True

@app.route('/')
def index():
    name = 'zs'
    dict = {
        'name': 'ls',
        'age': 18
    }
    list = [1, 2, 3, 4]
    htm_str = render_template('render.html',name=name,dict=dict,list=list,user=User())
    return htm_str


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

html代码

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<label>{{ name }}</label><br/>
<label>{{ dict }}</label><br/>
<label>{{ dict['name'] }}</label><br/>
<label>{{ dict.name }}</label><br/> 
<label>{{ list }}</label><br/>
<label>{{ list[0] }}</label><br/>
<label>{{ user.type }}</label><br/>
<label>{{ user.is_login }}</label><br/>

</body>
</html>

2.3过滤器

py代码 

from flask import Flask, render_template

app = Flask(__name__)


@app.route('/')
def index():
    name = 'zs'
    list = [1, 6, 3, 9]
    h1_tag='<h1>我是标题<h1>'
    htm_str = render_template('filtering.html',name=name,list=list,h1_tag=h1_tag)
    return htm_str


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

html代码

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<label>{{ name|lower }}</label><br/>
<label>{{ list|first }}</label><br/>
<label>{{ list|sum }}</label><br/>
<label>{{ h1_tag|safe }}</label><br/>

{#过滤块代码#}
{% filter upper %}
    hello<br/>
    world
{% endfilter %}

</body>
</html>

2.4自定义过滤器

1.定义过滤器函数>1定义形参接收模板变量的值,>2将转换后的结果返回
2.应用添加过滤器 add_template_filter(过滤器函数引用,过滤器名称)  

py代码

from flask import Flask, render_template

app = Flask(__name__)


@app.route('/')
def index():
    list = [1,4,9,8,2]
    htm_str = render_template('filtering1.html',list=list )
    return htm_str

# 1.定义过滤器函数>1定义形参接收模板变量的值,>2将转换后的结果返回
def li_reverse(var):
    return var[::-1]

# 2.应用添加过滤器 add_template_filter(过滤器函数引用,过滤器名称)
app.add_template_filter(li_reverse,'Li_reverse')
if __name__ == '__main__':
    app.run(host='0.0.0.0', port=8080, debug=True)

html代码

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

<label>{{ list|Li_reverse }}</label><br/>


</body>
</html>

3控制代码块

控制代码块主要包含两个  

- if / else if / else / endif
- for / endfor

3.1 if语句

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

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

过滤器可以被用在 if 语句中:  

{% if comments | length > 0 %}
There are {{ comments | length }} comments
{% else %}
There are no comments
{% endif %}

3.2循环

我们可以在 Jinja2 中使用循环来迭代任何列表或者生成器函数  

{% for post in posts %}
<div>
<h1>{{ post.title }}</h1>
<p>{{ post.text | safe }}</p>
</div>
{% endfor %}

循环和if语句可以组合使用,以模拟 Python 循环中的 continue 功能,下面这个循环将只会渲染post.text不为None的那些post:  

{% for post in posts if post.text %}
<div>
<h1>{{ post.title }}</h1>
<p>{{ post.text | safe }}</p>
</div>
{% endfor %}

3.3实例

py代码

from flask import Flask, render_template

app = Flask(__name__)


@app.route('/')
def index():
    my_list = [
        {
            "id": 1,
            "value": "我爱工作"
        },
        {
            "id": 2,
            "value": "工作使我快乐"
        },
        {
            "id": 3,
            "value": "沉迷于工作无法自拔"
        },
        {
            "id": 4,
            "value": "日渐消瘦"
        },
        {
            "id": 5,
            "value": "以梦为马,越骑越傻"
        }
    ]
    return render_template('if_for.html',my_list=my_list)


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

html代码

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<ul>
    {% for item in my_list if item.id !=5 %}
        {% if loop.index == 1 %}
            <li style="background-color: orange">{{ item.value }}</li>

        {% elif loop.index == 2 %}
            <li style="background-color: green">{{ item.value }}</li>

        {% elif loop.index == 3 %}
            <li style="background-color: red">{{ item.value }}</li>

        {% else %}
            <li style="background-color: indigo">{{ item.value }}</li>

        {% endif %}

    {% endfor %}

</ul>

</body>
</html>

4前端渲染VS后端渲染
后端渲染:使用正则表达式在服务端直接对html进行替换
优点:有利于SEO(搜素引擎优化)
缺点:前后端不分离,开发效率很低
前端渲染:前端使用js对html内容进行更改,一般是发起ajax异步请求,后端以json形式返回数据,再进行拼接html
优点:前后端分离,开发效率高,局部动态刷新
缺点:不利于SEO

5模板继承

模板继承是为了重用模板中的公共内容。一般Web开发中,继承主要使用在网站的顶部菜单、底部。

这些内容可以定义在父模板中,子模板直接继承,且不需要重复书写。标签定义的内容

{% block top %} {% endblock %}

 

  

相当于在父模板中挖个坑,当子模板继承父模板时,可以进行填充。

子模板使用extends 指令声明这个模板继承自哪个模板父模板中定义的块在子模板中被重新定义,在子模板中调用父模板的内容可以使用super()

父模板

base.html
{% block top %}
顶部菜单 
{% endblock top %}
{% block content %}
{% endblock content %}
{% block bottom %}
底部  
{% endblock bottom %}

 

子模板

extends指令声明这个模板继承自哪  

{% extends 'base.html' %}
{% block content %}
需要填充的内容 
{% endblock content %}

模板继承使用时注意点:

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

实例

py代码
from flask import Flask, render_template

app = Flask(__name__)


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


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

父类模板(content_base.html)

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<h1>顶部内容</h1>
{% block content_block %}
我是父类
{% endblock %}
<h1>底部内容</h1>
</body>
</html>

子类模板(content.html)

{% extends 'content_base.html' %}
{% block content_block %}
  
    
    <li>人民日报:部分城市楼市放松限制引关注,楼市调控不会“拉抽屉”</li>
{% endblock %}

6模板中可以直接使用的变量和函数

 

你可以在前端的模板中访问某些 Flask 默认内置的函数和对象

 

config

 

你可以从模板中直接访问Flask当前的config对象:

 

{{config.SQLALCHEMY_DATABASE_URI}}
sqlite:///database.db

 

request

就是flask中代表当前请求的request对象:

{{request.url}}
http://127.0.0.1

session

为Flask的session对象

{{session.new}}
True

g变量

在视图函数中设置g变量的 name 属性的值,然后在模板中直接可以取出

{{ g.name }}

url_for()

url_for会根据传递的路由器函数名,返回该路由对应的URL,在模板中始终使用url_for()就可以安全的修
改路由绑定的URL,则不用担心模板中渲染出错的链接:

{{ url_for('post', post_id=1)}}
/post/1

get_flashed_messages()

这个函数会返回之前在flask中通过flask()传递的消息的列表,flash函数的作用很简单,可以把由Python
字符串表示的消息加到单个消息队列中,再使用get_flashed_message()函数取出它们并消费掉:  

{%for message in get_flashed_messages()%}
{{message}}
{%endfor%}

实例

py代码
from flask import Flask, session, g, render_template, flash

app = Flask(__name__)
app.secret_key = 'test'

@app.route('/')
def index():
    session['name']='ls'
    g.age=18

    flash('登陆开始')
    flash('登陆结束')
    return render_template('demo5.html')
@app.route('/home')
def home():
    flash('回家')
    return 'home'

if __name__ == '__main__':
    app.run(host='0.0.0.0', port=8080, debug=True)
html代码
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<label>{{ request.url }}</label><br/>
<label>{{ session.name }}</label><br/>
<label>{{ g.age }}</label><br/>

<label>{{ url_for('index') }}</label><br/>

{% for message in get_flashed_messages() %}
    <script>alert("{{ message }}")</script>
{% endfor %}
</body>
</html>

7CSRF
CSRF 全拼为 Cross Site Request Forgery ,译为跨站请求伪造。
CSRF 指攻击者盗用了你的身份,以你的名义发送恶意请求。
包括:以你名义发送邮件,发消息,盗取你的账号,甚至于购买商品,虚拟货币转账…
造成的问题:个人隐私泄露以及财产安全。

 

 

 

防止 CSRF 攻击

 

步骤在客户端向后端请求界面数据的时候,后端会往响应中的 cookie 中设置 csrf_token 的值

在 Form 表单中添加单个隐藏的的字段,值也是 csrf_token
在用户点击提交的时候,会带上这两个值向后台发起请求
后端接受到请求,以会以下事件:
从 cookie中取出 csrf_token
从 表单数据中取出来隐藏的 csrf_token 的值进行对比
如果比较之后两值同样,那么代表是正常的请求,如果没取到或者值较不一样,代表不是正常的请求,不执行下一步操作

 

 

 

实例

A网站
py代码
import base64
import os

from flask import Flask, request, render_template, url_for, redirect
from flask_wtf import CSRFProtect
app = Flask(__name__)

# 设置随机密钥
s_key = base64.b64encode(os.urandom(48)).decode()
app.secret_key = s_key

# 设置csrf防护
CSRFProtect(app)

@app.route('/',methods=['GET','POST'])
def index():
    if request.method == 'GET':
        return render_template('login.html')
    username = request.form.get('username')
    password = request.form.get('password')
    print(username,password)
    if username == 'ww' and password == '123':
        return redirect(url_for('transfer'))
    else:
        return '用户名或密码错误'
@app.route('/transfer',methods=['GET','POST'])
def transfer():
    if request.method == 'GET':
        return render_template('demo2.html')
    to_account = request.form.get('to_account')
    money = request.form.get('money')
    return '成功转了%s给%s' % (money,to_account)
if __name__ == '__main__':
    app.run(host='0.0.0.0', port=8080, debug=True)

html登陆代码  

 

<body>
<h1>欢迎来到A网站</h1>
<form action="" method="post">
    <div>
    <label>用户名:</label>
    <input type="text" name="username" placeholder="请输入用户名">
    <label>密码:</label>
    <input type="password" name="password" placeholder="请输入密码">
    </div>
    <input type="submit" value="登陆"><br/>
    <input type="hidden" name="csrf_token" value="{{ csrf_token() }}">
</form>

</body>
</html>

 

html转账代码  

 

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<h1>欢迎来到A网站</h1>
<form action="" method="post">
    <div>
    <label>对方账户名:</label>
    <input type="text" name="to_account" placeholder="请输入对方账户">
    <label>金额:</label>
    <input type="text" name="money" placeholder="请输入金额">
    </div>
    <input type="submit" value="转帐"><br/>
    <input type="hidden" name="csrf_token" value="{{ csrf_token() }}">
</form>

</body>
</html>

 

B网站

py代码
from flask import Flask, render_template

app = Flask(__name__)


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


if __name__ == '__main__':
    app.run(host='0.0.0.0', port=8000, debug=True)
html优惠卷代码
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<h1>欢迎来到B网站</h1>
<form action="http://127.0.0.1:8080/transfer" method="post">
    <input type="hidden" name="to_account" value="haha">
    <input type="hidden" name="money" value="8888888">
    <input type="submit" value="领取优惠卷">
</form>

</body>
</html>

  

  

  



 

  

  

  

  

  

  

  

  

  

  

  

  

 

  

  

  

  



 

posted @ 2021-10-08 12:33  和风的夏天  阅读(278)  评论(0)    收藏  举报