Django 模板層

雲崖先生發表於2020-09-18

Django模板層

   模板層的主要任務就是如何將頁面呈現給使用者,使用者能看到的所有東西都是屬於模板層進行呈現的。

   HTML檔案就是模板,模板是會先經過渲染,再呈現給使用者。

   學習模板層主要有以下兩點:

   變數相關或處理均使用{{}},即模板變數。

   邏輯相關或處理均使用{%%},即模板標籤。

模板變數傳值

   其實在聊Django檢視層的時候介紹的render()方法中就允許將檢視函式中的變數傳遞給模板。

   模板中使用{{name}}即可完成呼叫。

def test(request):
    username = "雲崖先生"
    return render(request,"test.html",locals())
<body>
    {{username}}
</body>

傳值型別

   Python中所有基本資料型別均可以進行模板傳遞,但是函式、類等可呼叫型別會出現一些令人意外的情況。

   注意:可呼叫型別傳遞時會自動加括號呼叫,並將返回值進行渲染(如果沒有返回值則為None),但是不支援引數的傳遞。

   後端返回:

def test(request):

    STR = "雲崖先生"
    INT = 100
    FLOAT = 11.11
    BOOL = True
    DICT = {"v1":"k1"}
    SET = {"1","2","3"}
    TUPLE = (1,2,3)
    LIST = [1,2,3,4]

    def func():
        return "function"

    class T1(object):
        pass

    return render(request,"test.html",locals())

   模板接收:

<body>

    <p>{{STR}}</p>
    <p>{{INT}}</p>
    <p>{{FLOAT}}</p>
    <p>{{BOOL}}</p>
    <p>{{DICT}}</p>
    <p>{{SET}}</p>
    <p>{{TUPLE}}</p>
    <p>{{LIST}}</p>
    <p>{{func}}</p>  <!-- 不支援引數傳遞 -->
    <p>{{T1}}</p>  <!-- 例項化出了一個類 -->
    
</body>

   渲染結果:

雲崖先生

100

11.11

True

{'v1': 'k1'}

{'1', '2', '3'}

(1, 2, 3)

[1, 2, 3, 4]

function

<app01.views.test.<locals>.T1 object at 0x000001C1E00ACB38>

元素取出

   無論後端的變數是list或者dict,均可使用.語法進行取值。

傳值:
def test(request):

    DICT = {"hobby":["basketball","football","volleyball"]}

    return render(request,"test.html",locals())
    

取值:
{{DICT.hobby.2}}

結果:
volleyball

模板變數過濾

   由於後端的函式模板中使用函式無法傳遞引數,所以提供自帶的過濾器進行操作。

   過濾器有非常多的功能,大概有六十多種過濾器提供使用,這裡只介紹常用的。

使用示例

   以下舉例常用的模板過濾器

過濾器描述
{{v1|length}} 獲取v1的長度
{{v1|default:v2}} 如果v1為真,則使用v1,否則使用v2
{{v1|filesizeformat}} v1必須是int型別,對其進行KB、MB、GB、TB等進位制之間的轉換
{{v1|date:"Y-m-d X"}} v1必須是時間型別,對其進行自定義格式化
{{v1|slice:"start:stop:step"}} v1必須是可通過索引取值的型別,對其進行切片
{{v1|truncatechars:num}} v1必須是可通過索引取值的型別,對其擷取指定長度的字元(包含三個.
{{v1|truncatewords:num}} v1必須是字串型別,對其進行擷取特點數量的單詞,以空格分隔(不包含.
{{v1|cut:'v2'}} 移除v1中特定的字元v2
{{v1|join:'v2'}} 使用v2對v1進行拼接
{{v1|add:v2}} v1與v2進行相加,如都是int則為加法,如都是str則為拼接
{{v1|safe}} 識別v1中的HTML程式碼並進行渲染(預設不會進行渲染)

   後端返回:

def test(request):
    import datetime
    v_1 = "12345"
    v_2 = False
    v_3 = 10240
    v_4 = datetime.datetime.now()
    v_5 = ["一","二","三","四","五","六","七"]
    v_6 = "abcdefghijklmn"
    v_7 = "HELLO WORD HELLO DJANGO"
    v_8 = "ABC$$$"
    v_9 = "QWETYU"
    v_10 = 10
    v_11 = "<h1>標題一<h1>"
    return render(request,"test.html",locals())

   模板接收加過濾:

<body>

   <p>{{v_1|length}}</p>
   <p>{{v_2|default:1}}</p>
   <p>{{v_3|filesizeformat}}</p>
   <p>{{v_4|date:"Y-m-d h:m:s"}}</p>
   <p>{{v_5|slice:"1:4:2"}}</p>
   <p>{{v_6|truncatechars:6}}</p> 
   <!-- 上面的: 三個點佔三個位置 -->
   <p>{{v_7|truncatewords:3}}</p>
   <!-- 上面的:三個點不佔位置 -->
   <p>{{v_8|cut:"$"}}</p>
   <p>{{v_9|join:"---"}}</p>
   <p>{{v_10|add:10}}</p>
   {{v_11|safe}}

</body>

   渲染結果:

   image-20200910223625256

後端渲染

   預設的,後端如果返回HTML程式碼模板層是不會進行渲染的,這是為了防止XSS攻擊。

   但是如果使用mark_safe()進行包裹,則會進行渲染。

  

def test(request):

    from django.utils.safestring import mark_safe # 匯入模組
    
    res = "<h1>不會渲染<h1>"
    res_safe = mark_safe("<h1>會渲染<h1>")
    return render(request,"test.html",locals())
<body>

    {{res}}
    {{res_safe}}

</body>

   image-20200910224438551

模板標籤分支

基本使用

   模板中支援對{% if %}、{% else %}、{% elif %}進行使用,與Python中邏輯一致。

   注意:判斷完成後要使用{% endif %}進行結束判斷的標誌

<body>

    {% if user %} <!-- 在{%%}的模板標籤中,被{{}}套住的變數可以不用寫{{}}-->
        <p>有內容</p>
    {% else %}
        <p>無內容</p>
    {% endif %}
    
</body>

模板標籤迴圈

基本使用

   模板中支援{% for %}來進行操作。

   注意:迴圈完成後要使用{% endfor %}進行結束迴圈的標誌

li = ["A","B","C"]

<body>
    
    {% for row in li %}
        <p> {{row}} </p> <!-- 在{%%}外使用變數,依然要巢狀上{{}} -->
    {% endfor %}
        
</body>

A

B

C

forloop

   {{forloop}}記錄了一些迴圈的資訊。

   如下所示:

li = ["A","B","C"]

<body>
    
    {% for row in li %}
       <p> {{forloop}} </p> <!-- 可使用 {{forloop.counter/first/last}} 等 -->
    {% endfor %}
        
</body>

   image-20200910230625903

不可迴圈

   如果一個迭代物件為空,則不可迴圈。此時可以使用{% empty %}進行操作

li = []

<body>

    {% for row in li %}
    	<p>不為空</p>
    {% empty %}
    	<p>迭代物件為空</p> <!--執行這裡-->
    {% endfor %}

</body>

鍵值方法

   在迴圈中,可以對被遍歷的物件使用keys、values、items進行操作。

   這與Python中是一樣的。

dic = {"name":"雲崖","age":"18","gender":"男",}

<body>

    {% for key in dic.keys %}
        <p>{{key}}</p>
    {% endfor %}

    {% for value in dic.values %}
        <p>{{value}}</p>
    {% endfor %}

    {% for kv in dic.items %}
        <p>{{kv}}</p>
    {% endfor %}

</body>

----------------
name

age

gender
----------------
雲崖

18

男
----------------
('name', '雲崖')

('age', '18')

('gender', '男')

模板標籤別名

   可以使用{% with %}配合as對巢狀複雜的單項資料做一個別名。方便後續進行拿出,更推薦與迴圈進行配套使用。

li = [
	{"username":"Yunya"},
	{"username":"Jack"},
	{"username":"Tom"},
]

----------------

<body>

    {% for row in li %}

        {% with row.username as data %}
                <p>{{data}}</p>
                <!-- 如果不使用別名,則需要寫 {{row.username}} 這只是一個示例,實際的巢狀可能比這個複雜的多 -->
        {% endwith %}
        
    {% endfor %}
        
</body>

----------------

Yunya

Jack

Tom

自定義系列

   所有的,自定義都需要完成以下三步驟:

   1.在APP中建立templatetags資料夾,必須叫這個名字

   2.在該資料夾下建立一個py檔案,可以是任意名字

   3.匯入一個模組,並寫一句話

   from django import template

   register = template.Library()

自定義過濾器

   先完成上面三步,再進行過濾器的建立。

   自定義過濾器的作用就是豐富內建的過濾器,完成你的需求。

   注意:過濾器的呼叫是{{ v1|filter:v2 }},過濾器最多隻能傳遞兩個引數,且第一個引數在|左邊,第二個引數傳遞的格式必須是過濾器名字:右邊的引數。

   自定義過濾器全步驟:

# app01/templatetags/tags.py

from django import template
register = template.Library()


@register.filter(name="f1")  # 在使用時,使用的是這個名字。函式名可以隨便取。注意一定要關鍵字傳參
def f1(left, right):  # left 代表斜槓左邊的值,right代表斜槓右邊的值
    return left + right

   基本使用:

<body>

    {% load tags %}  <!-- 先匯入標籤的檔案,tags.py -->
    {{1|f1:2}} <!-- 最多兩個引數,引數1|過濾器名字:引數2  這是固定格式
        
</body>

-------

3

自定義標籤

   自定義標籤與自定義過濾器比較相似但是也有兩大不同。

   1.支援無限制的引數傳遞

   2.引數傳遞方式不同

   注意:標籤的呼叫是{% tag v1 v2 v3 %},這與過濾器有較大的不同。

   先完成上面三步,再進行標籤的建立。

# app01/templatetags/tags.py

from django import template
register = template.Library()


@register.simple_tag(name="t1")  # 在使用時,使用的是這個名字。函式名可以隨便取。注意一定要關鍵字傳參
def t1(v1, v2, v3):
    return v1 + v2 + v3

   基本使用:

<body>

    {% load tags %} <!-- 先匯入標籤的檔案,tags.py -->
    {% t1 1 2 3%} <!--引數無限制-->
        
</body>

自定義inclusion_tag

   自定義inclusion_tag與上面兩個都不太一樣,它返回的是一個區域性頁面,當你的專案中有很多網頁中都有相同的一部分網頁結構,則使用自定義inclusion_tag來完成。

   還是需要先完成上面三個步驟。

# app01/templatetags/tags.py

from django import template
register = template.Library()

@register.inclusion_tag("common.html") # 去根目錄找templates資料夾下的common.html檔案
def common_html(username): # 注意,這裡呼叫是函式名,而不是上面指定的名字了
    user = username
    return locals() # 返回當前名稱空間中所有變數名
# 專案根目錄/templats/common.html

<h1>當前登入:{{user}}</h1> # 可以拿到 common_html 函式中的所有變數名了
<h1>點選登出</h1>
# 被渲染的模板

<body>

    {% load tags %}
    {% common_html "雲崖" %} <!---這一處將會替換為指定的頁面,即common.html裡的內容-->
        
</body>

模板繼承引入

模板繼承

   模板繼承是全棧開發裡面Django所提供的非常牛逼的一個功能,它允許你一套HTML模板能被其他模板進行繼承並修改一些特定區域的內容。

   image-20200911004024841

# 專案根目錄/templats/common.html

{% block left %}
<!-- 這裡面可以允許修改,相當於給這塊區域起一個名字,left -->

{% endblock %}

============================

# 專案根目錄/templats/other.html

{% extends 'common.html' %}
<!-- 繼承common.html -->

{% block left %}
<!-- 執行修改 -->

{% endblock %}

   接下來看一下演示程式碼就知道了。

   這裡放一套公用的左右佈局頁面

# 專案根目錄/templats/common.html

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <script src='https://cdn.bootcdn.net/ajax/libs/jquery/3.5.1/jquery.js'></script>
    <link rel='stylesheet' href='https://cdn.jsdelivr.net/npm/bootstrap@3.3.7/dist/css/bootstrap.min.css'
        integrity='sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u' crossorigin='anonymous'>
    <script src='https://cdn.jsdelivr.net/npm/bootstrap@3.3.7/dist/js/bootstrap.min.js'
        integrity='sha384-Tc5IQib027qvyjSMfHjOMaLkfuWVxZxUPnCJA7l2mCWNIpG9mGCD8wGNIcPD7Txa'
        crossorigin='anonymous'></script>
    <title>Document</title>
    
    {% block CSS %}
        
    {% endblock %}
        
</head>

<body>

    <header class="navbar navbar-inverse">
        <!-- 頭部,不允許改變 -->
        <div class="navbar-header">
            <button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar"
                aria-expanded="false" aria-controls="navbar">
                <span class="sr-only">Toggle navigation</span>
                <span class="icon-bar"></span>
                <span class="icon-bar"></span>
                <span class="icon-bar"></span>
            </button>
            <a class="navbar-brand" href="#">Project name</a>
        </div>
        <div id="navbar" class="navbar-collapse collapse">
            <ul class="nav navbar-nav">
                <li class="active"><a href="#">Home</a></li>
                <li><a href="#about">About</a></li>
                <li><a href="#contact">Contact</a></li>
                <li class="dropdown">
                    <a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true"
                        aria-expanded="false">Dropdown <span class="caret"></span></a>
                    <ul class="dropdown-menu">
                        <li><a href="#">Action</a></li>
                        <li><a href="#">Another action</a></li>
                        <li><a href="#">Something else here</a></li>
                        <li role="separator" class="divider"></li>
                        <li class="dropdown-header">Nav header</li>
                        <li><a href="#">Separated link</a></li>
                        <li><a href="#">One more separated link</a></li>
                    </ul>
                </li>
            </ul>
            <ul class="nav navbar-nav navbar-right">
                <li><a href="../navbar/">Default</a></li>
                <li><a href="../navbar-static-top/">Static top</a></li>
                <li class="active"><a href="./">Fixed top <span class="sr-only">(current)</span></a></li>
            </ul>
        </div>
    </header>
    <main class="container-full">

        <div class="row">
            <section class="col-sm-3 col-md-2 sidebar">
                <!-- 左邊,允許改變一小塊區域 -->
      
                    <ul class="nav nav-sidebar">
                        <li class="active"><a href="#">menu1 <span class="sr-only">(current)</span></a></li>
                        <li><a href="#">menu2</a></li>
                        <li><a href="#">menu3</a></li>
                        <li><a href="#">menu4</a></li>
                    </ul>
                    {% block left %}
                    <!-- 這裡面可以允許修改,相當於給這塊區域起一個名字,left -->
 
                    {% endblock %}


            </section>

            <section class="col-lg-6">

                {% block right %}
                <!-- 這裡面可以允許修改,相當於給這塊區域起一個名字right -->
       

                {% endblock %}

            </section>
        </div>

    </main>

</body>

{% block Js %}
    
{% endblock %}

</html>
# 專案根目錄/templats/其他頁面.html

{% extends 'common.html' %}
<!-- 繼承common.html -->

{% block left %}
<!-- 修改內容 -->
<ul class="nav nav-sidebar">
    <li><a href="">menu-div-1</a></li>
    <li><a href="">menu-div-2</a></li>
    <li><a href="">menu-div-3</a></li>
</ul>
{% endblock %}

{% block right %}
<!-- 修改內容 -->
    <h4>Subheading</h4>
    <p>Donec id elit non mi porta gravida at eget metus. Maecenas faucibus mollis interdum.</p>

    <h4>Subheading</h4>
    <p>Morbi leo risus, porta ac consectetur ac, vestibulum at eros. Cras mattis consectetur purus sit amet fermentum.</p>

    <h4>Subheading</h4>
    <p>Maecenas sed diam eget risus varius blandit sit amet non magna.</p>

{% endblock %}

{% block css %}
<!-- 修改CSS -->
{% endblock %}

{% block js %}
<!-- 修改Js -->
{% endblock %}

模板引入

   模板引入是在一個HTML中引入一段公用的HTML程式碼。

# 專案根目錄/templats/common.html

<h1>公用咯<h1>

============================

# 專案根目錄/templats/other.html

{include "common.html"}  <!-- 將common.html中程式碼全部拿過來 -->

相關文章