1. 程式人生 > >Flask Template ( 模板學習)

Flask Template ( 模板學習)

學習目標

基本使用
過濾器&自定義過濾器
控制程式碼塊
巨集、繼承、包含
Flask 的模板中特有變數和方法
CSRF

Jinja2模板引擎簡介(template)

模板

檢視函式的主要作用是生成請求的響應,這是最簡單的請求。實際上,檢視函式有兩個作用:處理業務邏輯和返回響應內容。在大型應用中,把業務邏輯和表現內容放在一起,會增加程式碼的複雜度和維護成本。本節學到的模板,它的作用即是承擔檢視函式的另一個作用,即返回響應內容。

模板其實是一個包含響應文字的檔案,其中用佔位符(變數)表示動態部分,告訴模板引擎其具體的值需要從使用的資料中獲取
使用真實值替換變數,再返回最終得到的字串,這個過程稱為“渲染”
Flask是使用 Jinja2 這個模板引擎來渲染模板

使用模板的好處:

檢視函式只負責業務邏輯和資料處理(業務邏輯方面)
而模板則取到檢視函式的資料結果進行展示(檢視展示方面)
程式碼結構清晰,耦合度低

Jinja2
兩個概念:

Jinja2:是 Python 下一個被廣泛應用的模板引擎,是由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 }} #}

模板使用

#.html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <h1>{{title | reverse | upper}}</h1>
    <br>
    {{list2 | listreverse}}
    <br>
    <ul>
        {% for item in my_list %}
        <li>{{item.id}}----{{item.value}}</li>
        {% endfor %}
    </ul>

    {% for item in my_list %}{% if loop.index==1 %}
            <li style="background-color: red;">{{ loop.index }}--{{ item.get('value') }}</li>
        {% elif loop.index==2 %}
            <li style="background-color: blue;">{{ loop.index }}--{{ item.get('value') }}</li>
        {% elif loop.index==3 %}
            <li style="background-color: green;">{{ loop.index }}--{{ item.get('value') }}</li>
        {% else %}
            <li style="background-color: yellow;">{{ loop.index }}--{{ item.get('value') }}</li>
        {% endif %}{% endfor %}
</body>
</html>
# .py
from flask import Flask
from flask import render_template

app = Flask(__name__)
@app.route('/')
def index():
    list1 = list(range(10))
    my_list = [{"id": 1, "value": "我愛工作"},
               {"id": 2, "value": "工作使人快樂"},
               {"id": 3, "value": "沉迷於工作無法自拔"},
               {"id": 4, "value": "日漸消瘦"},
               {"id": 5, "value": "以夢為馬,越騎越傻"}]
    return render_template(
        # 渲染模板語言
        'index.html',
        title='hello world',
        list2=list1,
        my_list=my_list
        )


# step1 定義過濾器
def do_listreverse(li):
    temp_li = list(li)
    temp_li.reverse()
    return temp_li

# step2 新增自定義過濾器
app.add_template_filter(do_listreverse, 'listreverse')



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

過濾器

過濾器的本質就是函式。有時候我們不僅僅只是需要輸出變數的值,我們還需要修改變數的顯示,甚至格式化、運算等等,而在模板中是不能直接呼叫 Python 中的某些方法,那麼這就用到了過濾器。

鏈式呼叫

{{ "hello world" | reverse | upper }}

常見內建過濾器

字串操作

safe:禁用轉義

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

capitalize:把變數值的首字母轉成大寫,其餘字母轉小寫

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

lower:把值轉成小寫

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

upper:把值轉成大寫

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

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>

語句塊操作

{% filter upper %}
    #一大堆文字#
{% endfilter %}

自定義過濾器

from flask import Flask
from flask import render_template

app = Flask(__name__)
@app.route('/')
def index():
    list1 = list(range(10))
    my_list = [{"id": 1, "value": "我愛工作"},
               {"id": 2, "value": "工作使人快樂"},
               {"id": 3, "value": "沉迷於工作無法自拔"},
               {"id": 4, "value": "日漸消瘦"},
               {"id": 5, "value": "以夢為馬,越騎越傻"}]
    return render_template(
        # 渲染模板語言
        'index.html',
        title='hello world',
        list2=list1,
        my_list=my_list
        )


# step1 定義過濾器
def do_listreverse(li):
    temp_li = list(li)
    temp_li.reverse()
    return temp_li

# step2 新增自定義過濾器
app.add_template_filter(do_listreverse, 'listreverse')



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

控制程式碼塊

控制程式碼塊主要包含兩個:

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

條件語句

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

迴圈語句

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

結合使用

模擬continue功能

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

程式碼複用

巨集 —- 類似python中的函式

建立:
        {% macro 標籤名(key=value)%} {% end macro %}
# d8_macro1.html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<!-- step1 巨集匯入 import filename as xx-->
{% import 'd8_macro2.html' as fun %}
<!-- step2 巨集呼叫 類似於python函式呼叫 -->
{{ fun.input('button','zhuce') }}
</body>
</html>
# d8_macro2.html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<!-- step1 巨集匯入 import filename as xx-->
{% import 'd8_macro2.html' as fun %}
<!-- step2 巨集呼叫 類似於python函式呼叫 -->
{{ fun.input('button','zhuce') }}
</body>
</html>

繼承

關鍵字: block extends

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<h1>top123 {{ title }}</h1><br>

<!-- 預留空間 給繼承的html -->
{% block body %}{% endblock %}

<br><h1>bottom</h1>
</body>
</html>
{% extends 'd1_base.html' %}{% block body %}
<h2>detail</h2>
{% endblock %}

繼承:常常用於上下部分不做修改的網址

包含

包含: 將一個模板載入到另一個模板裡面種

<h1>這是一個多頁面共同的內容</h1>
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<!-- 特殊的自定義 函式 -->
<h1>{{ request.url }}</h1>
<h2>{{ g.title }}</h2>
index
<br>
<!-- include 呼叫html檔案 -->
{% include 'd2_include.html' %}
<hr>
<!-- 已經內建的特殊變數 -->
{{ url_for('detail') }}<br>
<a href="{{ url_for('detail') }}">詳細頁面</a>
</body>
</html>

總結:

巨集(Macro)、繼承(Block)、包含(include)均能實現程式碼的複用。
繼承(Block)的本質是程式碼替換,一般用來實現多個頁面中重複不變的區域。
巨集(Macro)的功能類似函式,可以傳入引數,需要定義、呼叫。
包含(include)是直接將目標模板檔案整個渲染出來。

crsf 跨域請求偽造

這裡寫圖片描述

1.能夠描述出這是一種什麼樣的攻擊方式

 a. 訪問A時進行了登入
 b. A進行了狀態保持
 c. 訪問網站B
 d. 返回攻擊程式碼
 e. 向網站A發起請求 在使用者未意識到的情況下

2.如何防止這種攻擊?

1.修改操作 由get操作 改為 post方式
資料修改 get ==》 post
跨網站 借用使用者資訊 訪問目標網站
2.口令驗證 隨機產生 
隨機產生一個口令 拿這個口令做對比
安裝包 pip install flask-wtf

3.作為開發人員如何防止這樣的攻擊

1.設定加密欄位
app.secret_key('fad')
2.引入類
from flask_wtf.csrf import CRSFProtect
3.建立物件
CRSFProtect(app)
4.在模板的form中生成一個隨機的口令值
<input type="hidden" name="crsf_token" value="{{csrf_token()}}">

4.知道實現程式碼 背後做了什麼

註冊了請求勾子before_request 這個函式會在檢視函式前執行 進行口令驗證
如何驗證? 接收表單傳遞的口令 與session的口令對比

CRSFProtect === init === init_app ==== crsf_protect === protect === saft_str_cmp(session[filed_name], token)
session中的值是什麼時候寫  在呼叫csrf_token()函式時
from flask import Flask, render_template


app = Flask(__name__)
# 1 設定加密字串
app.secret_key = 'python'
# 2. 引入類
from flask_wtf.csrf import CSRFProtect
# 3. 建立物件
CSRFProtect(app)

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


@app.route("/detail", methods=['POST'])
def detail():
    return 'ok'


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