1. 程式人生 > 實用技巧 >Flask-模板

Flask-模板

一、cookie

  • 解釋: 用來保持伺服器和瀏覽器互動的狀態的, 由伺服器設定,儲存在瀏覽器
  • 作用: 用來做廣告推送
  • cookie的設定和獲取
    • 設定cookie: response.set_cookie(key,value,max_age)
      • max_age: 表示cookie在瀏覽器的儲存時間,單位是秒
    • 獲取cookie: request.cookies.get("key")
from flask import Flask, make_response, request

app = Flask(__name__)

#設定cookie
@app.route('/set_cookie')
def set_cookie():

    #呼叫make_response方法獲取響應體物件
    response = make_response("set cookie")

    #設定cookie
    response.set_cookie("computer","lenovo")
    response.set_cookie("age","13",10)

    return response


#獲取cookie
@app.route('/get_cookie')
def get_cookie():

    #獲取cookie
    name = request.cookies.get("computer")
    age = request.cookies.get("age")

    #返回
    return "name is %s, age is %s"%(name,age)

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

二、session

  • 解釋: 伺服器和使用者來做狀態保持的,裡面儲存的是敏感資訊(比如身份證,登陸資訊),由伺服器設定,並存儲在伺服器
  • 作用: 用來做使用者的登陸狀態保持
  • session的設定和獲取
    • 設定session: sessioin[key] = value
    • 獲取session: value = session.get(key)
  • 注意點:
    • 1.session的儲存依賴於cookie
    • 2.儲存在cookie中的sessionID需要加密,需要祕鑰(SECRET_KEY)
from flask import Flask, session

app = Flask(__name__)

#設定SECRET_KEY,此時就是隨意的字串
app.config["SECRET_KEY"] = "sdofinsidofnsfdas"

#設定session
@app.route('/set_session/<path:name>')
def set_session(name):

    session["name"] = name

    return "set session!"


#獲取session
@app.route('/get_session')
def get_session():

    value = session.get("name")

    return "set session, name is %s"%value


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

三、上下文

  • 解釋: 就是一個容器
  • 請求上下文
    • request: 封裝的是請求相關的資料
    • session: 封裝的是和使用者相關的敏感資訊
  • 應用上下文(在專案中具體應用)
    • current_app: 是app的一個代理物件,可以通過他獲取app身上設定的各種屬性,主要用在模組化開發中
    • g: 一個區域性的全域性變數,主要用在裝飾器中
from flask import Flask, current_app

app = Flask(__name__)

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

    print(app.config.get("DEBUG"))
    print(current_app.config.get("DEBUG"))

    return "helloworld"

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

四、Flask_script

  • 解釋: 屬於flaks的擴充套件
  • 作用: 用來動態執行程式,配合flask_migrate做資料庫遷移
  • 使用格式:
    • 1.安裝
      • pip install flask_script
    • 2.匯入Manager類
    • 3.建立物件manager,管理app
    • 4.使用manager啟動程式
      • 啟動命令: python xxx.py runserver -h(host是IP地址) -p(埠號) -d(除錯模式)
from flask import Flask
from flask_script import Manager

app = Flask(__name__)
app.config["DEBUG"] = True

#3.建立物件manager,管理app
manager = Manager(app)

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

    return "helloworld"

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

五、render_template

  • 解釋: 屬於jinja2的模板函式
  • 好處:
    • 1.以後的檢視函式,只負責業務邏輯的處理,比如: 資料庫的增刪改查
    • 2.以後資料的展示,全部都有jinja2的模板負責
  • 使用格式:
    • response = render_template('模板檔案',key=value)

模板檔案寫在自動生成的templates目錄下

如果是自己建的目錄需要這樣

from flask import Flask,render_template

app = Flask(__name__)

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

    response = render_template('template.html')

    return response

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

template.html檔案

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <style>
        .box{
            width: 300px;
            height: 300px;
            background: red;
        }

    </style>
</head>
<body>

    <div class="box">

    </div>

</body>
</html>

六、模板語法,獲取變數

  • 解釋: 在模板中獲取檢視函式的變數
  • 格式:
    • {{ 變數 }}
from flask import Flask,render_template

app = Flask(__name__)

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

    #1.定義各種型別的變數
    number = 10
    str = "老王"
    tuple = (1,2,3,4,5)
    list = [6,7,8,9,10]
    dict = {
        "name":"班長",
        "age":29
    }

    #2.攜帶變數到模板中展示
    return render_template("variable.html",number=number,str=str,tuple=tuple,list=list,dict=dict)

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

variable.html檔案

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

    <h1>1.獲取各種變數的值</h1>
    <h2>整數: {{number}}</h2>
    <h2>字串: {{str}}</h2>
    <h2>元祖: {{tuple}},分開獲取: {{tuple[0]}}, {{ tuple.1 }}</h2>
    <h2>列表: {{list}},分開獲取: {{ list.0 }}, {{ list.1 }}</h2>
    {# 如果字典使用方括號,獲取,需要寫成字串,如果不是字串,那麼則會被當成變數對待   #}
    <h2>字典: {{dict}}, 分開獲取: {{ dict.name }}, {{ dict["age"] }}</h2>

</body>
</html>

七、模板語法,分支迴圈判斷

  • 模板語法的種類

    • 分支格式:

      {% if 條件 %}
      	語句1
      {% else%}
      	語句2
      {% endif %}
      
    • 迴圈語句格式:

      {% for 變數 in 容器 %}
      
      {% endfor %}
      
    • 註釋:

      {# 這裡是註釋的內容 #}
      
from flask import Flask,render_template

app = Flask(__name__)

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

    #1.定義各種型別的變數
    number = 10
    str = "老王"
    tuple = (1,2,3,4,5)
    list = [6,7,8,9,10]
    dict = {
        "name":"班長",
        "age":29
    }

    #2.攜帶變數到模板中展示
    return render_template("programer.html",number=number,str=str,tuple=tuple,list=list,dict=dict)

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

對應的html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <style>
        h1{
            color:red;
        }

    </style>
</head>
<body>

    <h1>1.遍歷元祖中的偶數</h1>
    {% for item in tuple %}
        {% if item %2 == 0 %}
            <h3>{{ item }}</h3>
        {% endif %}
    {% endfor %}


    <h1>2.遍歷字典</h1>
    {% for key in dict %}
        {# dict.key那麼這個key會當成字典中的一個鍵,  dict[key],那麼這個key當成一個變數 #}
        <h3>{{ key }} = {{ dict[key] }}</h3>
    {% endfor %}


</body>
</html>

八、系統字串過濾器

  • 解釋: 過濾器,用來過濾想要的資料
  • 格式: {{ 字串 | 字串過濾器 }}
  • 常見的字串過濾器有:
    • title: 將每個單詞的首字母都大寫
    • lower: 將每個單詞都小寫
    • upper: 將每個單詞都大寫
    • reverse: 反轉
    • ....
from flask import Flask,render_template

app = Flask(__name__)

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

    return render_template("stringfilter.html")

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

對應的html

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

    {#    使用格式:{{ 字串 | 字串過濾器 }}#}
    1.safe:禁用轉義,讓標籤生效
    <p>{{ '<em>hello</em>' | safe }}</p>

    2.capitalize:把變數值的首字母轉成大寫,其餘字母轉小寫
    <p>{{ 'hello PYTHON' | capitalize }}</p>

    3.lower:把值轉成小寫
    <p>{{ 'HELLO PYthON' | lower }}</p>

    4.upper:把值轉成大寫,中文沒有大小寫
    <p>{{ 'hello python 你好' | upper }}</p>

    5.title:把值中的每個單詞的首字母都轉成大寫
    <p>{{ 'hello world python java' | title }}</p>

    6.reverse:字串反轉
    <p>{{ 'olleh' | reverse }}</p>
    <p>{{ '我愛你' | reverse }}</p>


    7.format:格式化輸出
    <p>{{ '%s is %d' | format('age',17) }}</p>

    8.striptags:渲染之前把值中所有的HTML標籤都刪掉
    <p>{{ '<em>hello</em>' | striptags }}</p>

</body>
</html>

九、系統列表過濾器

  • 解釋: 過濾器,用來過濾想要的資料
  • 格式: {{ 列表 | 列表過濾器 }}
  • 常見的列表過濾器有:
    • first: 獲取列表第一個元素
    • last: 最後一個元素
    • sum: 列表和
    • length: 列表長度
    • ....
from flask import Flask,render_template

app = Flask(__name__)

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

    return render_template("list_fliter.html")

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

對應的html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    {#    * 使用格式:{{ 列表 | 列表過濾器 }}#}

    1.first:取第一個元素
    <p>{{ [1,2,3,4,5,6] | first }}</p>

    2. last:取最後一個元素
    <p>{{ [1,2,3,4,5,6] | last }}</p>

    3. length:獲取列表長度
    <p>{{ [1,2,3,4,5,6] | length }}</p>

    4.sum:列表求和
    <p>{{ [1,2,3] | sum }}</p>

    5.sort:列表排序,預設升序
    <p>{{ [6,2,3,1,5,4] | sort }}</p>


    6.過濾器的鏈式呼叫
    {# 過濾器的鏈式呼叫 #}
    {{ "hello" | upper | reverse }}

</body>
</html>

十、自定義過濾器

  • 解釋: 當系統提供的過濾器滿足不了需求的時候,需要自定義

  • 自定義過濾器有兩種格式:

    • 1.先定義好函式,再將函式新增到系統預設的過濾器列表中

      • def 函式名: pass
      • app.add_template_filter(函式名,'過濾器名字')
    • 2.定義函式的時候,直接使用系統過濾器進行裝飾

      @app.template_filter('過濾器名字')
      def 函式名():
          pass
      
    • 案例:

    • 1.獲取列表偶數和

    • 2.反轉列表

from flask import Flask,render_template

app = Flask(__name__)

# 1.先定義好函式,再將函式新增到系統預設的過濾器列表中
def get_oushu(list):
    print(list)
    sum = 0
    for i in list:
        if i %2 == 0:
            sum += i

    return sum
#引數1: 關聯的函式名稱,  引數2: 在模板中使用的過濾器名字
app.add_template_filter(get_oushu,"oushu")


# 2.定義函式的時候,直接使用系統過濾器進行裝飾
@app.template_filter("reverse")
def listreverse(list):
    list.reverse()
    return list



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

    return render_template("custom_filter.html")

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

html

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

    <h2>原列表: {{ [1,2,3,4,5,6] }}</h2>
    <h2>偶數列表: {{ [1,2,3,4,5,6] | oushu }}</h2>
    <h2>反轉列表: {{ [1,2,3,4,5,6] | reverse }}</h2>
    <h2>降序列表: {{ [1,2,3,4,5,6,10,9,7] | sort | reverse }}</h2>

</body>
</html>

十一、程式碼複用之巨集

  • 解釋: 相當於python中的函式,定義好一段功能,在需要的時候進行呼叫即可

  • 定義格式:

    {% macro 巨集名(引數) %}
    
    {% endmacro %}
    
  • 使用格式:

    // 使用當前檔案定義好的巨集
    {{ 巨集名(引數) }}
    
    //使用其他檔案定義好的巨集
    {% import '檔案' as 別名%}
    {{ 別名.巨集名(引數) }}
    
from flask import Flask,render_template

app = Flask(__name__)

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

    return render_template("file07macro.html")

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

使用

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

    {# 定義巨集 #}
    {% macro my_macro(name,password) %}
        使用者名稱: <input type="text" value="{{ name }}"><br>
        密碼: <input type="password" value="{{ password }}"><br>
    {% endmacro %}

    {# 呼叫當前檔案巨集 #}
    {{ my_macro("zhangsan","111111") }}


    {# 使用其他檔案的巨集 #}
    {% import 'othermacro.html' as other %}
    {{ other.my_input() }}
    {{ other.my_div() }}


</body>
</html>

othermacro.html

{% macro my_macro(name,password) %}
    使用者名稱: <input type="text" value="{{ name }}"><br>
    密碼: <input type="password" value="{{ password }}"><br>
{% endmacro %}


{% macro my_input() %}

    <h1>這是一個其他檔案的巨集</h1>

{% endmacro %}


{% macro my_div() %}

    <div style="color: red;">我是一個孤獨的div</div>

{% endmacro %}

十二、程式碼複用之繼承

  • 解釋: 一個子模板繼承自父模板

  • 作用: 共性抽取,程式碼複用

  • 父模板

    • 1.所有子類都具有的相同的內容的, 在父模板中直接寫死
    • 2.每個子類的模板中不一樣的內容,使用block模板定義好
  • 子模板

    • 1.根據子類自己的需求,去重寫父類中的block對應的內容
    • 2.如果重寫之後,還想保留父類的內容,那麼使用{{super()}}
    • 3.繼承格式: {% extends '父檔名'%}, 寫在頁面的頂部
  • 注意點:

    • 定義block的格式

      {% block 名稱 %}
      
      {% endblock %}
      
from flask import Flask,render_template

app = Flask(__name__)

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

    # return render_template("file09zi.html")
    return render_template("file10zi.html")

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

file11fu.html

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

    {# 頭部部分 #}
    {% block titleBlock %}
        <h1>靜夜思</h1>
    {% endblock %}

    {# 正文部分 #}
    {% block contentBlock %}

    {% endblock %}


    {# 底部部分 #}
    <div>
        <a href="#">點我有驚喜</a>
    </div>

</body>
</html>

file09zi.html

{% extends 'file11fu.html' %}

{# 重寫正文部分 #}
{% block contentBlock %}
    <p>
        床前一鍋湯,<br>
        撒了一褲襠, <br>
        抬頭拿抹布, <br>
        低頭擦褲襠 <br>
    </p>
{% endblock %}


{#<!DOCTYPE html>#}
{#<html lang="en">#}
{#<head>#}
{#    <meta charset="UTF-8">#}
{#    <title>Title</title>#}
{#</head>#}
{#<body>#}
{##}
{#    <h1>靜夜思</h1>#}
{##}
{#    <p>#}
{#        床前一鍋湯,<br>#}
{#        撒了一褲襠, <br>#}
{#        抬頭拿抹布, <br>#}
{#        低頭擦褲襠 <br>#}
{#    </p>#}
{##}
{#    <div>#}
{#        <a href="#">點我有驚喜</a>#}
{#    </div>#}
{##}
{##}
{#</body>#}
{#</html>#}

file10zi.html

{% extends 'file11fu.html' %}

{# 重寫父類titleBlock內容 #}
{% block titleBlock %}
    {{ super() }}
    <h1>新靜夜思</h1>
{% endblock %}

{# 重寫父類中的contentBlock內容 #}
{% block contentBlock %}
    <p>
        床前明月光,<br>
        疑似地上霜, <br>
        舉頭望明月, <br>
        低頭思故鄉 <br>
    </p>
{% endblock %}

{#<!DOCTYPE html>#}
{#<html lang="en">#}
{#<head>#}
{#    <meta charset="UTF-8">#}
{#    <title>Title</title>#}
{#</head>#}
{#<body>#}
{##}
{#    <h1>靜夜思</h1>#}
{##}
{#    <p>#}
{#        床前明月光,<br>#}
{#        疑似地上霜, <br>#}
{#        舉頭望明月, <br>#}
{#        低頭思故鄉 <br>#}
{#    </p>#}
{##}
{#    <div>#}
{#        <a href="#">點我有驚喜</a>#}
{#    </div>#}
{##}
{##}
{#</body>#}
{#</html>#}

十三、程式碼複用之包含

  • 解釋: 在一個檔案中完全擁有另外一個檔案,不夠靈活,沒法擴充套件

  • 格式:

    方式一:
    {% include '檔案' %}
    
    方式二:
    {% include '檔案' ignore missing %}
    
    
  • 注意點: ignore missing 如果包含的檔案不存在,也不會報錯

十四、模板使用練習

  • 題目: 給出5條資料,顯示前四條
"""
給定如下5條資料,只顯示4行資料,背景顏色依次為:黃,綠,紅,紫
my_list = [
    {
        "id": 1,
        "value": "我愛工作"
    },
    {
        "id": 2,
        "value": "工作使人快樂"
    },
    {
        "id": 3,
        "value": "沉迷於工作無法自拔"
    },
    {
        "id": 4,
        "value": "日漸消瘦"
    },
    {
        "id": 5,
        "value": "以夢為馬,越騎越傻"
    }
]
"""
from flask import Flask, render_template

app = Flask(__name__)

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

    #1.定義5條資料
    my_list = [
        {
            "id": 1,
            "value": "我愛工作"
        },
        {
            "id": 2,
            "value": "工作使人快樂"
        },
        {
            "id": 3,
            "value": "沉迷於工作無法自拔"
        },
        {
            "id": 4,
            "value": "日漸消瘦"
        },
        {
            "id": 5,
            "value": "以夢為馬,越騎越傻"
        }
    ]

    #2.在模板中顯示4條
    return render_template("practice.html",list=my_list)

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

practice.html

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

    <ul>
        {# 如果dict.id 不等於5才遍歷 #}
        {% for dict in list if dict.id !=5 %}

            {# 方式一 #}
{#            {% if dict.id == 1 %}#}
{#                <li style="background: yellow;"> {{ dict.value }} </li>#}
{#            {% elif dict.id == 2 %}#}
{#                <li style="background: green;"> {{ dict.value }} </li>#}
{#            {% elif dict.id == 3 %}#}
{#                <li style="background: red;"> {{ dict.value }} </li>#}
{#            {% else %}#}
{#                <li style="background: purple;"> {{ dict.value }} </li>#}
{#            {% endif %}#}

            {# 遍歷的時候可以獲取到從0開始的索引 #}
{#            <h3>{{ loop.index0  }}</h3>#}

            {# 遍歷的時候可以獲取到從1開始的索引 #}
{#            <h3>{{ loop.index  }}</h3>#}


            {# 方式二 #}
            {% if loop.index == 1 %}
                <li style="background: yellow;"> {{ dict.value }} </li>
            {% elif loop.index == 2 %}
                <li style="background: green;"> {{ dict.value }} </li>
            {% elif loop.index == 3 %}
                <li style="background: red;"> {{ dict.value }} </li>
            {% else %}
                <li style="background: purple;"> {{ dict.value }} </li>
            {% endif %}



        {% endfor %}
    </ul>


</body>
</html>

十五、模板特有變數

  • 解釋: 不需要通過python程式傳遞就可以直接使用的變數
  • 常見的特有變數如下:
    • config: 就是flask中的app.config, 表示應用程式中的所有配置資訊
    • request: 表示請求上下文物件,封裝的是請求相關的資料
    • g: 區域性的全域性變數(瞭解)
    • url_for(): 反解析,通過函式的名字,解析到檢視函式的路徑
    • get_flashed_messsages(): 用來消耗flash方法中儲存的訊息.
      • 場景: 登陸出錯,可以顯示
      • 注意點:
      • 1.使用flash儲存訊息的時候需要設定SECRET_KEY
      • 2.因為flash內部的訊息儲存,依賴於了session
from flask import Flask,render_template

app = Flask(__name__)
app.config["SECRET_KEY"] ="hahaha"
@app.route('/')
def hello_world():

    return render_template("special_variable.html")

@app.route('/test/<int:age>')
def test(age):
    return "test..."


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

special_variable.html

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

    <h2>config變數: {{ config }}</h2>
    <h2>request: {{ request.method }}</h2>
    <h2>request: {{ request.url }}</h2>
    <h2>url_for(): {{ url_for("hello_world") }}</h2>
    <h2>url_for(): {{ url_for("test",age=100) }}</h2>

</body>
</html>

get_flashed_messsages()的使用

from flask import Flask,render_template,flash

app = Flask(__name__)
app.config["SECRET_KEY"] = "onjgtex"

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

    return render_template("flash.html")

@app.route('/test')
def test():
    #儲存訊息
    flash("登陸成功")

    return "test"

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

flash.html

用來消耗flash方法中儲存的訊息

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

    {{ get_flashed_messages() }}

</body>
</html>

十六、csrf攻擊流程

  • 解釋: 跨站點請求偽造
  • 掌握: 需要理解講義中的攻擊流程圖
  • 程式碼演示: webA, webB

十七、csrf攻擊手動解決

  • 在cookie增加一個csrf_token
  • 在表單中增加一個csrf_token
  • 校驗: 取出cookie和表單中的csrf_token比較如果二者一致那麼是正常請求
  • 具體過程,看keynote圖解

十八、CSRFProtect解決csrf

  • 使用流程:
    • 1.安裝擴充套件包
      • pip install flask-wtf
    • 2.匯入包
      • from flask_wtf.csrf import CSRFProtect
    • 3.設定SECRET_KEY,便於csrf_token加密
    • 4.建立CSRFProtect物件,保護app物件
    • 5.需要在表單中設定csrf_token隱藏欄位即可
    • 例子: 註冊案例
    • 注意點: CSRFProtect一旦保護了app之後, 會對'POST', 'PUT', 'PATCH', 'DELETE'做校驗.
from flask import Flask, render_template, request
from flask_wtf.csrf import CSRFProtect


app = Flask(__name__)

# 1. 先設定金鑰
app.config["SECRET_KEY"] = "weorinsaiudfhnb"
# 2. 保護app
CSRFProtect(app)


@app.route('/', methods=["GET", "POST"])
def index():
    if request.method == "POST":
        # 獲取到引數
        username = request.form.get("username")
        password = request.form.get("password")
        repassword = request.form.get("repassword")

        # 校驗引數
        if not all([username, password, repassword]):
            return "引數填寫不全"

        # 判斷兩次的密碼是否一致
        if password != repassword:
            return "兩次密碼輸入不一致,請重新輸入"

        return "註冊成功"

    else:
        return render_template("register.html")


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

對應的register.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <form action="/" method="post">
    {# 3. 只要使用了CSRFProtect保護app,就可以直接使用csrf_token()作為value值       #}
        <input type="hidden" name="csrf_token" value="{{ csrf_token() }}">

        使用者名稱:<input type="text" name="username"><br>
        密碼:<input type="password" name="password"><br>
        確認密碼:<input type="password" name="repassword"><br>
        <input type="submit" value="註冊">
    </form>
</body>
</html>